最近在瞭解關於頁面站內搜索相關功能,現在大多數網站都有相關功能。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學習,若有什麼缺陷,望大牛們多多提出意見,本人不勝感激!!!