由于公司项目的要求,要求使用lucene进行搜索,数据源是mongodb,所以这里我先简单介绍一下mongodb。
1mongodb
首先是下载mongodb。http://www.mongodb.org/downloads。下载好之后如下图,
里面最重要的是mongo.exe和mongod.exe.你每次启动的话用命令每次都要输入mongod --dbpath D:/MongoDB/data来启动MongoDB。这样非常不方便,而且关机后就得从新启动,所以我推荐启用一个mongo服务
命令如下D:\MongoDB\bin>mongod --logpath D:\MongoDB\logs\MongoDB.log --logappend --dbpath D:\MongoDB\data --directoryperdb --serviceName MongoDB --install
我来解释一下,D:\MongoDB为我的根目录,--logpath D:\MongoDB\logs\MongoDB.log为设置日志 --logappend --dbpath D:\MongoDB\data为设置数据库数据。安装成功后会提示
Creating service MongoDB.
Service creation successful.
Service can be started from the command line via 'net start "MongoDB"'.
注意:logs文件夹和data文件夹要先新建好;serviceName的N字母要大写.
以上就是安装mongodb的方法,mongodb的一些基本语法就不在这里说了,随便一搜到处是。为什么公司要用mongodb,这就要跟mongodb的特性有关了,数据库大致分为两种,关系型和非关系型(当然从别的方面分还可以分成别的)。最主要区别就是关系型是一一对应的,这是它的优点也是缺点。正是这个原因诞生了NOSQL,NOSQL是not only sql 的简称。nosql里面最火的,就属于memcached,mongodb这两个了。这两个的用处也不相同,这里主要说mongodb。mongodb是介于关系型和非关系型之间的一种,发展到现在已经很成熟了,很容易找到你需要的解决方案。mongodb他也有一个缺点,就是不是可视化的,当然也有mongo的可视化工具,但是一般人不用。下面附上mongohelper
1 public class MongoDBHelper<T> where T : class 2 { 3 string connectionString = string.Empty; 4 string databaseName = string.Empty; 5 string collectionName = string.Empty; 6 static MongoDBHelper<T> mongodb; 7 8 public MongoDBHelper(string connectionString, string databaseName, string collectionName) 9 { 10 this.collectionName = collectionName; 11 this.connectionString = connectionString; 12 this.databaseName = databaseName; 13 } 14 15 public MongoDB.Configuration.MongoConfiguration configuration 16 { 17 get 18 { 19 var config = new MongoDB.Configuration.MongoConfigurationBuilder(); 20 config.Mapping(mapping => 21 { 22 mapping.DefaultProfile(profile => 23 { 24 profile.SubClassesAre(t => t.IsSubclassOf(typeof(T))); 25 }); 26 27 mapping.Map<T>();//将类型添加到集合中 28 mapping.Map<T>(); 29 }); 30 config.ConnectionString(connectionString); 31 return config.BuildConfiguration(); 32 } 33 } 34 35 public void Insert(T t) 36 { 37 using (MongoDB.Mongo mongo = new MongoDB.Mongo()) 38 { 39 try 40 { 41 mongo.Connect(); 42 43 var db = mongo.GetDatabase(databaseName); 44 45 var collection = db.GetCollection<T>(collectionName); 46 47 collection.Insert(t, true); 48 49 mongo.Disconnect(); 50 } 51 catch (Exception) 52 { 53 mongo.Disconnect(); 54 throw; 55 } 56 } 57 } 58 59 public void Update(T t,Expression<Func<T,bool>>func) 60 { 61 using (MongoDB.Mongo mongo=new MongoDB.Mongo()) 62 { 63 try 64 { 65 mongo.Connect(); 66 67 var db = mongo.GetDatabase(databaseName); 68 69 var collection = db.GetCollection<T>(collectionName); 70 71 collection.Update(t, func, true); 72 73 mongo.Disconnect(); 74 } 75 catch (Exception) 76 { 77 mongo.Disconnect(); 78 throw; 79 } 80 } 81 } 82 83 public List<T> GetList(int pageIndex, int pageSize,int needPageCount, Expression<Func<T, bool>> func, out int pageCount) 84 { 85 pageCount = 0; 86 87 using (MongoDB.Mongo mongo=new MongoDB.Mongo()) 88 { 89 try 90 { 91 mongo.Connect(); 92 93 var db = mongo.GetDatabase(databaseName); 94 95 var collection = db.GetCollection<T>(collectionName); 96 97 pageCount =Convert.ToInt32(collection.Count()/pageSize); 98 99 var modelList = collection.Linq().Where(func).Skip(pageSize * (pageIndex - 1)).Take(needPageCount * pageSize).Select(i => i).ToList(); 100 101 102 mongo.Disconnect(); 103 104 return modelList; 105 } 106 catch (Exception) 107 { 108 mongo.Disconnect(); 109 throw; 110 } 111 } 112 } 113 public List<T> ListAll(Expression<Func<T, bool>> func) 114 { 115 using (Mongo mongo = new Mongo()) 116 { 117 try 118 { 119 mongo.Connect(); 120 121 var db = mongo.GetDatabase(databaseName); 122 123 var collection = db.GetCollection<T>(collectionName); 124 125 var list = collection.Linq().Select(i => i).ToList(); 126 127 mongo.Disconnect(); 128 129 return list; 130 } 131 catch (Exception) 132 { 133 mongo.Disconnect(); 134 throw; 135 } 136 } 137 } 138 139 public T Single(Expression<Func<T,bool>>func) 140 { 141 using (Mongo mongo=new Mongo()) 142 { 143 try 144 { 145 mongo.Connect(); 146 147 var db = mongo.GetDatabase(databaseName); 148 149 var collection = db.GetCollection<T>(collectionName); 150 151 var single = collection.Linq().FirstOrDefault(func); 152 153 mongo.Disconnect(); 154 155 return single; 156 } 157 catch (Exception) 158 { 159 mongo.Disconnect(); 160 throw; 161 } 162 } 163 } 164 165 public void Delete(Expression<Func<T,bool>>func) 166 { 167 using (Mongo mongo=new Mongo()) 168 { 169 try 170 { 171 mongo.Connect(); 172 173 var db = mongo.GetDatabase(databaseName); 174 var collection = db.GetCollection<T>(databaseName); 175 //这个地方一定要加上T参数,不然会当成object类型的来处理,导致删除失败。 176 collection.Remove<T>(func); 177 mongo.Disconnect(); 178 } 179 catch (Exception) 180 { 181 mongo.Disconnect(); 182 throw; 183 } 184 } 185 }
2,lucene.net.
lucence.net是lucene的.net移植版本,lucene所解决的问题是站内搜索。通俗的说lucene是把数据库里的数据进行读取,然后根据自己的分词器进行分词,之后你所搜索的关键词都会通过lucene所创建的词库(这个词是加了索引的,所以搜索很快)进行搜索。
还要说明的一点就是lucene自带的分词器不好用,因为这个分词器主要是针对英文进行分词,中文的话,最好用盘古分词。一般都是lucene+pangu这么进行配置。当然现在hubble已经逐渐进行取代lucene了,因为盘古分词和hubb是一个开发团队开发的,用起来好用,速度也有所提升,在之后我会单独写一篇hubble的介绍。lucene的使用者还是很多的。下面代码就是对lucene的读和写(写就是写索引)我看到过一篇lucene对索引写的具体算法http://keben1983.blog.163.com/blog/static/143638081201092854659524/。有兴趣的朋友可以看看。
1 public class LuceneTest 2 { 3 4 public void Write(string indexPath,string txt) 5 { 6 7 8 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory());//FS→FileSystem 9 bool isUpdate = IndexReader.IndexExists(directory);//索引库文件夹存在并且存在索引库特征文件 10 if (isUpdate) 11 { 12 //同时只能有一段代码对索引库进行写操作!当使用IndexWriter打开directory的时候会自动给索引库上锁。!!! 13 //如果索引目录被锁定(比如索引过程中程序异常退出),则首先解锁 14 if (IndexWriter.IsLocked(directory)) 15 { 16 IndexWriter.Unlock(directory);//lock:锁。Un—否定。解锁。Do、Undo。 17 } 18 } 19 IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED); 20 //for (int i = 1000; i < 1100; i++) 21 //{ 22 23 Document document = new Document();//一篇文档 24 //字段的值是字符串类型 25 26 //Field.Store表示是否保存字段原值。Field.Store.YE的字段才能用document.Get取出来值 27 //document.Add(new Field("number", i.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); 28 //要进行全文检索的字段要设置 Field.Index. ANALYZED 29 document.Add(new Field("body", txt, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS)); 30 writer.AddDocument(document);//文档写入索引库 31 //} 32 writer.Close(); 33 directory.Close();//不要忘了Close,否则索引结果搜不到 34 } 35 36 /// <summary> 37 /// 38 /// </summary> 39 /// <param name="indexPath">索引库的位置</param> 40 /// <param name="keyWords">关键字</param> 41 /// <param name="and_or">关键带空格的关键字之间是什么关系如果是and连接就是true,如果是or连接就是false</param> 42 /// <param name="properties">就是文本的写入各个属性</param> 43 public List<string> Read(string indexPath,string keyWords,bool and_or,int startIndex,int showCount,params string[] properties) 44 { 45 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory()); 46 IndexReader reader = IndexReader.Open(directory, true); 47 IndexSearcher searcher = new IndexSearcher(reader);//Index:索引 48 49 //搜索条件 50 PhraseQuery query = new PhraseQuery(); 51 //foreach (string word in kw.Split(' '))//先用空格,让用户去分词,空格分隔的就是词“计算机 专业” 52 //{ 53 // query.Add(new Term("body", word)); 54 //} 55 //query.Add(new Term("body", "简历")); 56 57 query.Add(new Term("body",keyWords));//Add的查询条件是and的关系 58 //where Contains("body","简历") and Contains("body","大学生") 59 //query.Add(new Term("body",kw));//body中含有kw的文章 60 //query.SetSlop(100);//多个查询条件的词之间的最大距离。在文章中相隔太远一般也就无意义 61 //TopScoreDocCollector是盛放查询结果的容器 62 TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true); 63 searcher.Search(query, null, collector);//根据query查询条件进行查询,查询结果放入collector容器 64 ScoreDoc[] docs = collector.TopDocs(startIndex, showCount).scoreDocs;//得到所有查询结果中的文档。TopDocs可以实现分页 65 66 //return searcher.Doc(docs[0].doc).Get("body"); 67 List<string> list = new List<string>(); 68 for (int i = 0; i < docs.Length; i++) 69 { 70 ////SqlDataReader是把查询结果放到数据库服务器。DataSet是放入本程序的内容。 71 ////搜索ScoreDoc[]只能获得文档的id,这样不会把查询结果的Document一次性加载到内存中。降低了内存压力 72 ////需要获得文档的详细内容的时候通过searcher.Doc来根据文档id来获得文档的详细内容对象Document 73 int docId = docs[i].doc;//得到查询结果文档的id(Lucene内部分配的id) 74 Document doc = searcher.Doc(docId);//找到文档id对应的文档详细信息 75 //textBox1.AppendText(doc.Get("number") + "\n");//取出放进去字段的值 76 //textBox1.AppendText(doc.Get("body") + "\n");//Field.Store.YE的字段才能用document.Get取出来值 77 //textBox1.AppendText("-----------------------\n"); 78 list.Add(doc.Get("body")); 79 } 80 return list; 81 82 83 } 84 }
当然这只是针对数据库里单个字段进行匹配,要想针对多个字段,就多加几个document.Add()。但是这样的话,读的时候就不能只从一个字段读,例如:标题或正文包括lucene,并且时间在20060101到20060130之间的文章;
1 Directory dir = FSDirectory.getDirectory(PATH, false); 2 IndexSearcher is = new IndexSearcher(dir); 3 QueryParser parser = new QueryParser("content", new StandardAnalyzer()); 4 Query query = parser.parse("+(title:lucene content:lucene) +time:[20060101 TO 20060130]"; 5 Hits hits = is.search(query); 6 for (int i = 0; i < hits.length(); i++) 7 { 8 Document doc = hits.doc(i); 9 System.out.println(doc.get("title"); 10 } 11 is.close();
这样就能多条件,多字段搜索了。
第一次写博文,写的不好,见谅。等过两天写hubble+sql和hubble+mongodb