lucene+盤古分詞

通常的網站都會有都會有搜索的功能,通常實現搜索主要有三種方案算法

第一種是最差的,也是最不推薦的,使用數據庫的模糊查詢例如select * form table where 字段 like XXX,這種查詢的缺點很明顯:數據庫

(1)       沒法查找幾個關鍵詞不連在一塊兒的狀況測試

(2)       全表掃描 效率低下網站

 

第二種:使用SqlServer的全文本檢索功能this

舉例:select * form table where msg = ‘江蘇南京’搜索引擎

這是就能夠寫成select * form table where msg.contains(‘江蘇南京’);編碼

這樣搜索出來的結果就能夠既包含江蘇也能夠包含南京,而且匹配的速度也快,還能夠實現分詞。spa

缺點:.net

(1):只有正版的SqlServer才支持上面的技術code

(2):數據庫的分詞不太靈活,不能本身修改詞庫

 

第三種:使用lucene.Net(本文重點講解)  

Lucene.Net只是一個全文檢索開發包,不是一個成熟的搜索引擎,他的功能就是:將數據交給Lucene.Net,         查詢數據的時候從Lucene.Net查詢數據,能夠當作是一個提供了全文檢索功能的數據庫,lucene.net只對文本信息進行檢索,若是不是文本信息,要轉換爲文本信息。lucene會將扔給他的詞切詞保存,由於是保存的時候分詞(切詞),因此搜索速度很是快。

 

分詞是搜索結果好壞的關鍵:

lucene不一樣的分詞算法就是不一樣的類,全部的分詞算法類都從Analyzer類繼承,不一樣的分詞算法有不一樣的優缺點。

例如:內置的StandardAnalyzer是將英文按照空格,標點符號等進行分詞,講中文按照單個字進行分詞,一個漢字算一個詞(就是所謂的一元分詞)

二元分詞:每兩個漢字算一個單詞,「歡迎大家你們」會分爲「歡迎」,迎你,大家,們大,你們     要在網上下載一個二元分詞的算法:CJKAnalyzer

基於詞庫的分詞算法:基於一個詞庫進行分詞,能夠提升分詞的成功率,有庖丁解牛,盤古分詞等。效率低(相對於一元分詞與二元分詞)但準確度較高

 

注意:lucene.Net對漢語的分詞效果很差,須要藉助於第三方的分詞算法:開源的盤古分詞(能夠在開源中國社區下載,裏面有詳細的Demo,以及dll文件)

 

編寫代碼以下(體驗盤古分詞):

中國開源社區下載Pangu分詞forLucene

第一步:將WebDemo裏的bin文件夾下的Dictionaries文件夾複製到項目的根目錄下,而後改文件夾名爲Dict並設置裏面的內容的屬性的若是較新則複製到輸出目錄

 

第二部:添加引用Lucene.net.dll文件和PanGu.Lucene.Analyzer.dll文件

 

Analyzer analyzer = new Lucene.Net.Analysis.PanGu.PanGuAnalyzer();

            TokenStream tokenStream = analyzer.TokenStream("", new System.IO.StringReader("北京,Hi歡迎大家你們"));

            Lucene.Net.Analysis.Token token = null;

            while ((token = tokenStream.Next()) != null)

            {

                ListBox1.Items.Add(token.TermText());

            }

 

既然是分詞,那就確定有詞庫,有詞庫就能夠修改,使用剛纔的Bin文件夾下的PanGu.Lucene.ImportTool.exe文件打開詞庫修改詞庫的內容,就能夠實現最新的分詞效果。

 

能夠理解爲:

先創建一個索引系統,而後打開索引系統,使用luceneIndexWriter類向裏面寫入索引(document對象),該對象從盤古分詞對已有的文章的分詞獲得

 
 
 
 

 

基本思想如上圖

lucene+PanGu

創建lucene詞庫:(將數據交給lucene,使用Pangu分詞),並實現搜索的功能(第二段代碼)

 1 protected void Button4_Click(object sender, EventArgs e)
 2         {
 3             string indexPath = Server.MapPath(@"/Demo/lucenedir");//注意和磁盤上文件夾的大小寫一致,不然會報錯。將建立的分詞內容放在該目錄下。
 4 
 5             //指定索引文件(打開索引目錄) FS指的是就是FileSystem  個人理解:索引系統
 6             FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory());
 7 
 8             //IndexReader:對索引進行讀取的類。該語句的做用:判斷索引庫文件夾是否存在以及索引特徵文件是否存在。
 9             bool isUpdate = IndexReader.IndexExists(directory);
10             if (isUpdate)
11             {
12                 //同時只能有一段代碼對索引庫進行寫操做。當使用IndexWriter打開directory時會自動對索引庫文件上鎖。
13                 //若是索引目錄被鎖定(好比索引過程當中程序異常退出),則首先解鎖
14                 //(提示一下:若是我如今正在寫着已經加鎖了,可是尚未寫完,這時候又來一個請求,那麼不就解鎖了嗎?這個問題後面會解決)
15                 if (IndexWriter.IsLocked(directory))
16                 {
17                     IndexWriter.Unlock(directory);
18                 }
19             }
20 
21             //向索引庫中寫索引。這時在這裏加鎖。
22             IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);
23             for (int i = 1; i <= 10; i++)
24             {
25                 string txt = File.ReadAllText(Server.MapPath(@"/Demo/測試文件/" + i + ".txt"), System.Text.Encoding.Default);//注意這個地方的編碼
26                 Document document = new Document();//表示一篇文檔。
27                 //Field.Store.YES:表示是否存儲原值。只有當Field.Store.YES在後面才能用doc.Get("number")取出值來.Field.Index. NOT_ANALYZED:不進行分詞保存
28                 document.Add(new Field("number", i.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
29 
30                 //Field.Index. ANALYZED:進行分詞保存:也就是要進行全文的字段要設置分詞 保存(由於要進行模糊查詢)
31 
32                 //Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS:不只保存分詞還保存分詞的距離。
33                 document.Add(new Field("body", txt, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));
34                 writer.AddDocument(document);
35 
36             }
37             writer.Close();//會自動解鎖。
38             directory.Close();//不要忘了Close,不然索引結果搜不到
39         }


 1 protected void Button5_Click(object sender, EventArgs e)
 2         {
 3             //建立的分詞的內容所存放的目錄
 4             string indexPath = Server.MapPath(@"/Demo/lucenedir"); ;
 5             string kw = "C#";
 6             kw = kw.ToLower();
 7 
 8             FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory());
 9             IndexReader reader = IndexReader.Open(directory, true);
10             IndexSearcher searcher = new IndexSearcher(reader);
11 
12             //搜索條件
13             PhraseQuery query = new PhraseQuery();
14             //foreach (string word in kw.Split(' '))//先用空格,讓用戶去分詞,空格分隔的就是詞「計算機   專業」
15             //{
16             //    query.Add(new Term("body", word));
17             //}
18             //query.Add(new Term("body","語言"));--能夠添加查詢條件,二者是add關係.順序沒有關係.
19             //  query.Add(new Term("body", "大學生"));
20             query.Add(new Term("body", kw));//body中含有kw的文章
21             query.SetSlop(100);//多個查詢條件的詞之間的最大距離.在文章中相隔太遠 也就無心義.(例如 「大學生」這個查詢條件和"簡歷"這個查詢條件之間若是間隔的詞太多也就沒有意義了。)
22             //TopScoreDocCollector是盛放查詢結果的容器
23             TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true);
24             searcher.Search(query, null, collector);//根據query查詢條件進行查詢,查詢結果放入collector容器
25 
26             //獲得全部查詢結果中的文檔,GetTotalHits():表示總條數   TopDocs(300, 20);//表示獲得300(從300開始),到320(結束)的文檔內容.
27             //能夠用來實現分頁功能
28             ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;
29             this.ListBox1.Items.Clear();
30             for (int i = 0; i < docs.Length; i++)
31             {
32                 //搜索ScoreDoc[]只能得到文檔的id,這樣不會把查詢結果的Document一次性加載到內存中。
33                 //下降了內存壓力,須要得到文檔的詳細內容的時候經過searcher.Doc來根據文檔id來得到文檔的詳細內容對象Document.
34                 //獲得查詢結果文檔的id(Lucene內部分配的id)
35                 int docId = docs[i].doc;
36 
37                 //找到文檔id對應的文檔詳細信息
38                 Document doc = searcher.Doc(docId);
39 
40                 // 取出放進字段的值
41                 this.ListBox1.Items.Add(doc.Get("number") + "\n");
42                 this.ListBox1.Items.Add(doc.Get("body") + "\n");
43                 this.ListBox1.Items.Add("-----------------------\n");
44             }
45         }


注意事項:搜索的時候是區分大小寫的,要想不區分,能夠在創建詞庫的時候直接將全部的詞都轉換爲大寫或小寫的,搜索的時候作出相應的轉換便可

相關文章
相關標籤/搜索