最近在了解关于页面站内搜索相关功能,现在大多数网站都有相关功能。java
搜索能够经过如下四种办法来实现:程序员
1.使用数据库的模糊查询:like '%视频教程%'。web
2.使用数据库的全文检索功能。算法
3.使用百度、google的站内搜索sql
4.就是今天我要写的基于java版本Lucene移植过来的lucene.net来实现搜索。数据库
问什么不使用前面三种方法来实现搜索啦?dom
缘由:方法一 模糊程度过低,没法匹配几个关键词不挨着的;形成全表扫描,效率低。ide
方法二 数据库全文检索很傻瓜化,和普通SQL同样。数据全文检索灵活性不强。工具
方法三 受制于人;索引不及时、不全面、不精准;用户体验感差学习
Lucene.Net只是一个全文检索开发包,不是一个成型的搜索引擎,它的功能就是:把数据扔给Lucene.Net ,查询数据的时候从Lucene.Net 查询数据,能够看作是提供了全文检索功能的一个数据库。Lucene.Net无论文本数据怎么来的。用户能够基于Lucene.Net开发知足本身需求的搜索引擎。 Lucene.Net只能对文本信息进行检索。若是不是文本信息,要转换为文本信息,好比要检索Excel文件,就要用NPOI把Excel读取成字符串,而后把字符串扔给Lucene.Net。Lucene.Net会把扔给它的文本切词保存,加快检索速度。midomi.com。由于是保存的时候分词(切词),因此搜索速度很是快!
lucene分词是核心的算法,搜索引擎内部保存的就是一个个的“词(Word)”。Lucene.net中不一样的分词算法就是不一样的类,而全部的分词算法类都从Analyzer类继承。
首先在项目的根路径下加入Lucene.Net.dll和Lucene.Net.pdb的引用。
第一种:内置的StandardAnalyzer 是将英文按照空格、标点符号等进行分词,将中文按照单个字进行分词,一个汉字算一个词。
1 //实例化一个StandardAnalyzer类的对象 2 Analyzer analyzer = new StandardAnalyzer(); 3 //经过分析器获得一个TokenStream 4 TokenStream tokenStream = analyzer.TokenStream("",new StringReader("北京,Hi欢迎大家你们")); 5 Lucene.Net.Analysis.Token token = null; 6 //经过循环,Next()继续分词,若是没有更多词,则返回null 7 while ((token = tokenStream.Next()) != null) 8 { 9 //输出分词后的结果 10 Console.WriteLine(token.TermText()); 11 }
第二种:二元分词算法,每两个汉字算一个单词,“北京欢迎你”会分词为“北京 京欢 欢迎 迎你”。
这里介绍的CJKAnalyzer二元分词,和上面的StandardAnalyzer 代码类似,这里代码就不写了。只须要把实例化类改成CJKAnalyzer类就好了。
第三种:基于词库的分词算法,基于一个词库进行分词,能够提升分词的成功率。有庖丁解牛、盘古分词等。
这里介绍盘古分词
PanGu4Lucene下载地址我放到百度云盘里,须要的请自行下载http://pan.baidu.com/s/1GGBgr
打开PanGu4Lucene\WebDemo\Bin,将Dictionaries添加到项目的根路径下(并更名为Dict),
先测试一下盘古分词:
创建一个winform项目
测试代码:
1 private void button1_Click(object sender, EventArgs e) 2 { 3 listBox1.Items.Clear(); 4 //须要分词的字符串 5 string s = "程序员的生活,请注意本身的身体,少熬夜!!身体才是本钱。"; 6 //实例化一个PanGuAnalyzer类的实例 7 Analyzer analyzer = new PanGuAnalyzer(); 8 9 TokenStream tokenStream = analyzer.TokenStream("", new StringReader(s)); 10 11 Lucene.Net.Analysis.Token token = null; 12 while ((token = tokenStream.Next()) != null)//Next继续分词,若是没有更多词,则返回null 13 { 14 listBox1.Items.Add(token.TermText());//获得分到的词 15 } 16 }
下面来完成实际场景实例:
首先也创建一个winform项目,也能够在上面项目中直接添加也行。固然这里也可使用webform来实现,这里就不详解了。
首先咱们要先建立索引:
1 string indexPath = @"C:\LucenePanGudir";//注意和磁盘上文件夹的大小写一致,不然会报错。 2 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath),new NativeFSLockFactory());//根据文件夹创建一个FSDirectory 3 bool isUpdate = IndexReader.IndexExists(directory);//索引库文件夹存在而且存在索引库特征文件 4 if (isUpdate) 5 { 6 //同时只能有一段代码对索引库进行写操做!当使用IndexWriter打开directory的时候会自动给索引库上锁。!!! 7 //先判断索引目录是否被锁定。若是索引目录被锁定(好比索引过程当中程序异常退出),则首先解锁 8 if (IndexWriter.IsLocked(directory)) 9 { 10 IndexWriter.Unlock(directory); 11 } 12 } 13 IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED); 14 for (int i = 1; i < 100; i++) 15 { 16 string txt = File.ReadAllText(@"D:\lucene\PanGuTestArticle\" + i + ".txt");//用于创建索引须要的文章,这里能够经过读取数据库来得到创建索引的数据源,这里就不详解了。 17 Document document = new Document();//用于存储须要创建索引的一篇文档 18 19 //Field.Store表示是否保存字段原值。Field.Store.YES的字段才能用document.Get取出来值 20 //字段的值是字符串类型-- i.ToString() 21 document.Add(new Field("number", i.ToString(), Field.Store.YES, Field.Index. NOT_ANALYZED)); 22 //要进行全文检索的字段要设置 Field.Index. ANALYZED 23 document.Add(new Field("body", txt, Field.Store.YES, Field.Index. ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS)); 24 writer.AddDocument(document);//文档写入索引库 25 } 26 writer.Close(); 27 directory.Close();//不要忘了Close,不然索引结果搜不到
下面就能够经过代码来实现搜索的功能
1 string indexPath = @"C:\LucenePanGudir"; 2 3 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory()); 4 IndexReader reader = IndexReader.Open(directory, true); 5 IndexSearcher searcher = new IndexSearcher(reader);//建立一个索引搜索器。 6 7 //搜索条件 8 PhraseQuery query = new PhraseQuery(); 9 //添加搜索条件,这里也能够先获得用户输入的搜索条件使用盘古分词法来获得要添加的搜索条件。方法和上面我写的盘古分词测试代码类似,这里就再也不详解了。 10 query.Add(new Term("body", "程序"));//body中含有程序的文章 11 query.Add(new Term("body", "大学生"));//Add的查询条件是and的关系, 至关于sql语句的where Contains("body","程序") and Contains("body","大学生") 12 query.SetSlop(100);//多个查询条件的词之间的最大距离。在文章中相隔太远通常也就无心义 13 //TopScoreDocCollector是盛放查询结果的容器 14 TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true); 15 searcher.Search(query, null, collector);//根据query查询条件进行查询,查询结果放入collector容器 16 ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;//获得全部查询结果中的文档。TopDocs能够实现分页 17 for (int i = 0; i < docs.Length; i++) 18 { 19 //搜索ScoreDoc[]只能得到文档的id,这样不会把查询结果的Document一次性加载到内存中。下降了内存压力 20 //须要得到文档的详细内容的时候经过searcher.Doc来根据文档id来得到文档的详细内容对象Document 21 int docId = docs[i].doc;//获得查询结果文档的id(Lucene内部分配的id) 22 Document doc = searcher.Doc(docId);//找到文档id对应的文档详细信息 23 textBox1.AppendText(doc.Get("number")+"\n");//取出放进去字段的值 24 textBox1.AppendText(doc.Get("body") + "\n");//Field.Store.YE的字段才能用document.Get取出来值 25 textBox1.AppendText("-----------------------\n"); 26 }
以上是本人对lucene.net学习,若有什么缺陷,望大牛们多多提出意见,本人不胜感激!!!