本人看到這篇非常不錯的Lucene.Net入門基礎教程,就轉載分享一下給大家來學習,
希望大家在工作實踐中可以用到。
一.簡單的例子
//索引
Private void Index()
{
IndexWriter writer = new IndexWriter(@"E:\Index", new StandardAnalyzer());
Document doc = new Document();
doc.Add(new Field("Text","哦耶,美麗的姑娘。", Field.Store.YES, Field.Index.TOKENIZED));
writer.AddDocument(doc);
writer.Close();
}
//搜索
Private void Search(string words)
{
IndexSearcher searcher = new IndexSearcher(@"E:\Index");
Query query = new QueryParser(“Text”, new StandardAnalyzer()).Parse(words);
Hits hits = searcher.Search(query);
for (int i = 0; i < hits.Length(); i )
System.Console.WriteLine(hits.Doc(i).GetField("Text").StringValue();
searcher.Close();
}
二.初識Lucene
1. Lucene是什么
Lucene是一個高性能的、可擴展的信息檢索工具包。它只是Java類庫,並不是現成的應用程序。它提供簡單易用卻十分強大的API接口,基於它你可以快速的構建功能強大的搜索程序(搜索引擎?)。當前最新版2.9.2.1。
2. 什么是索引
為了實現快速的搜索,Lucene會首先將需要處理的數據以一種稱為倒排索引(Inverted Index)的數據結構進行存儲。怎樣理解倒排索引呢?簡單的說,倒排索引並不是回答“這個文檔中包含哪些單詞?”這個問題,而是經過優化以后用來快速回答“哪些文檔包含詞XX?”這個問題。就像需要給書籍整理一份供快速查找的目錄一樣,Lucene也得為需要被搜索的數據整理優化出一份索引文件(Index file),而這個過程稱之為“索引”(Indexing)。
3. Lucene的核心類
索引過程:
IndexWriter Directory Analyzer Document Field
搜索過程:
IndexSearcher Term Query TermQuery Hits
三.索引
1. 索引過程的流程圖:
注:Lucene索引過程分為三個主要的操作階段:將數據換轉成文本、分析文本、並將分析過的文本保存到索引庫中
2. 基本的索引操作
2.1添加索引
Document
Field(理解Field的參數)
異構Document
追加域
增量索引
2.2刪除索引
軟刪除,僅添加了刪除標記。調用 IndexWriter.Optimize() 后真正刪除。
IndexReader reader = IndexReader.Open(directory);
// 刪除指定序號(DocId)的 Document。
reader.Delete(123);
// 刪除包含指定 Term 的 Document。
reader.Delete(new Term(FieldValue, "Hello"));
// 恢復軟刪除。
reader.UndeleteAll();
reader.Close();
2.3更新索引
事實上,Lucene沒有更新索引的方法
更新 = 刪除 + 添加
提示:當刪除和添加多個Document對象時,最好進行批量處理。這樣做的速度總是比交替的刪除和添加操作的速度快得多。
//只需將 create 參數設為 false,即可往現有索引庫添加新數據。
Directory directory = FSDirectory.GetDirectory("index", false);
IndexWriter writer = new IndexWriter(directory, analyzer, false);
writer.AddDocument(doc1);
writer.AddDocument(doc2);
writer.Optimize();
writer.Close();
3. 加權(boosing)
可以給 Document 和 Field 增加權重(Boost),使其在搜索結果排名更加靠前。缺省情況下,搜索結果以 Document.Score 作為排序依據,該數值越大排名越靠前。Boost 缺省值為 1。
Score = Score * Boost
通過上面的公式,我們就可以設置不同的權重來影響排名。
如下面的例子中根據 VIP 級別設定不同的權重。
Code [http://joinzhang.cnblogs.com]
Document document = new Document(); switch (vip) { case VIP.Gold: document.SetBoost(2F); break; case VIP.Argentine: document.SetBoost(1.5F); break; }
只要 Boost 足夠大,那么就可以讓某個命中結果永遠排第一位,這就是百度等網站的"收費排名"業務。
4. Directory
從指定目錄打開已有索引庫。
Code [http://joinzhang.cnblogs.com]
private Directory directory = FSDirectory.GetDirectory("c:\index", false);
將索引庫載入內存,以提高搜索速度。
Code [http://joinzhang.cnblogs.com]
private Directory directory = new RAMDirectory(FSDirectory.GetDirectory(@"c:\index", false)); //或 //private Directory directory = new RAMDirectory(c:\index");
注意 FSDirectory.GetDirectory 的 create 參數,為 true 時將刪除已有索引庫文件,可以通過 IndexReader.IndexExists() 方法判斷。
5.合並索引庫
將 directory1 合並到 directory2 中。
Code [http://joinzhang.cnblogs.com]
Directory directory1 = FSDirectory.GetDirectory("index1", false); Directory directory2 = FSDirectory.GetDirectory("index2", false); IndexWriter writer = new IndexWriter(directory2, analyzer, false); writer.AddIndexes(new Directory[] { directory }); Console.WriteLine(writer.DocCount()); writer.Close();
6. 優化索引
6.1很簡單,一個writer.Optimize()搞定,優化過程會降低索引的效率,優化結果提高搜索性能。不要時時Optimize(),優化一次就夠了
6.2批量向 FSDirectory 增加索引時,增大合並因子(mergeFactor )和最小文檔合並數(minMergeDocs)有助於提高性能,減少索引時間。
Code [http://joinzhang.cnblogs.com]
IndexWriter writer = new IndexWriter(directory, analyzer, true); writer.maxFieldLength = 1000; // 字段最大長度 writer.mergeFactor = 1000; writer.minMergeDocs = 1000; for (int i = 0; i < 10000; i ) { // Add Documentes... } writer.Optimize(); writer.Close();
利用 Lucene,在創建索引的工程中你可以充分利用機器的硬件資源來提高索引的效率。當你需要索引大量的文件時,你會注意到索引過程的瓶頸是在往磁盤上寫索引文件的過程中。為了解決這個問題, Lucene 在內存中持有一塊緩沖區。但我們如何控制 Lucene 的緩沖區呢?幸運的是,Lucene 的類 IndexWriter 提供了三個參數用來調整緩沖區的大小以及往磁盤上寫索引文件的頻率。
(1)合並因子 (mergeFactor)
這個參數決定了在 Lucene 的一個索引塊中可以存放多少文檔以及把磁盤上的索引塊合並成一個大的索引塊的頻率。比如,如果合並因子的值是 10,那么當內存中的文檔數達到 10 的時候所有的文檔都必須寫到磁盤上的一個新的索引塊中。並且,如果磁盤上的索引塊的隔數達到 10 的話,這 10 個索引塊會被合並成一個新的索引塊。這個參數的默認值是 10,如果需要索引的文檔數非常多的話這個值將是非常不合適的。對批處理的索引來講,為這個參數賦一個比較大的值會得到比較好的索引效果。
(2)最小合並文檔數 (minMergeDocs)
這個參數也會影響索引的性能。它決定了內存中的文檔數至少達到多少才能將它們寫回磁盤。這個參數的默認值是10,如果你有足夠的內存,那么將這個值盡量設的比較大一些將會顯著的提高索引性能。
(3)最大合並文檔數 (maxMergeDocs)
這個參數決定了一個索引塊中的最大的文檔數。它的默認值是 Integer.MAX_VALUE,將這個參數設置為比較大的值可以提高索引效率和檢索速度,由於該參數的默認值是整型的最大值,所以我們一般不需要改動這個參數。
7.大數據量索引(並發性、多線程和鎖機制)
7.1多線程索引
共享對象(注:一個IndexWriter或IndexReader對象可以被多個線程所共享)
巧用RAMDirectory
7.2安全鎖
Lucene使用基於文件的鎖
write.lock
禁用索引鎖 (disableLuceneLocks=true)
7.3並發訪問的規則
任意數量的只讀操作都可以同時執行。
在索引正在被修改時,我們也可以同時執行任意數量的只讀操作。
在某一時刻,只允許執行一個修改索引的操作。
四.搜索
1. IndexSearcher
通過IndexSearcher執行搜索
兩種構建IndexSearcher對象的方法: Directory對象與文件路徑。 (前者是推薦的)
Search()方法
2. Query
2.1創建Query對象
使用QueryParset構建Query對象。(注:QueryParset把查詢表達式轉換成Lucene內置的查詢類型。)
幾個常用的內置類型:TermQuery、RangeQuery、PrefixQuery、BooleanQuery。
2.2強悍的QueryParser
Query類的toString()方法
布爾查詢 (AND、 OR、 NOT) 例:a AND b(+a +b) a OR b(a b) a AND NOT b(+a -b)
組合查詢 圓括號”()” 例: (a OR b) AND c
域的選擇 例:tag:美女
范圍查詢 [ TO ] 和{ TO } 例:price:[100 TO 200] price:{100 TO 200}
……
(注:強悍,但不建議使用它)
3.Hits
3.1使用Hits對象訪問搜索結果
3.2Hits類的幾個方法
Length() Hits對象集合中所包含的文檔的數量
Document(n) 排名第n的Document實例
Id(n) 排名第n的DocumentID
Score(n) 排名第n的標准分值
4. 排序
4.1使用Sort對象排序
通過 SortField 的構造參數,我們可以設置排序字段,排序條件,以及倒排。
Code [http://joinzhang.cnblogs.com]
Sort sort = new Sort(new SortField(FieldName, SortField.DOC, false)); IndexSearcher searcher = new IndexSearcher(reader); Hits hits = searcher.Search(query, sort);
4.2按照索引順序(索引時的文檔ID)排序 使用Sort.INDEXORDER作為參數
4.3多域排序
4.4排序對性能的影響
排序對搜索速度影響還是很大的,盡可能不要使用多個排序條件。
建議:采用默認的積分排序,設計良好的加權機制
5.過濾
過濾(Filtering)是Lucene中用於縮小搜索空間的一種機制。
DateFliter 只限於指定日期域的值在某一時間范圍
QueryFilter 把查詢作果作為另一個新查詢可搜索的文檔空間。
建議:過濾器采取的是對搜索結果的再處理方式,會使程序的性能顯著下降,一般推薦使用BooleanQuery組合更多的搜索條件來達成效果。
例子:
我們搜索上架時間在 2005-10-1 到 2005-10-30 之間的商品。
對於日期時間,我們需要轉換一下才能添加到索引庫,同時還必須是索引字段。
Code [http://joinzhang.cnblogs.com]
// index document.Add(FieldDate, DateField.DateToString(date), Field.Store.YES, Field.Index.UN_TOKENIZED); //... // search Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-1"), DateTime.Parse("2005-10-30")); Hits hits = searcher.Search(query, filter);
除了日期時間,還可以使用整數。比如搜索價格在 100 ~ 200 之間的商品。
Lucene.Net NumberTools 對於數字進行了補位處理,如果需要使用浮點數可以自己參考源碼進行。
Code [http://joinzhang.cnblogs.com]
// index document.Add(new Field(FieldNumber, NumberTools.LongToString((long)price), Field.Store.YES, Field.Index.UN_TOKENIZED)); //... // search Filter filter = new RangeFilter(FieldNumber, NumberTools.LongToString(100L), NumberTools.LongToString(200L), true, true); Hits hits = searcher.Search(query, filter);
使用 Query 作為過濾條件。
Code [http://joinzhang.cnblogs.com]
QueryFilter filter = new QueryFilter(QueryParser.Parse("name2", FieldValue, analyzer));
我們還可以使用 FilteredQuery 進行多條件過濾。
Code [http://joinzhang.cnblogs.com]
Filter filter = new DateFilter(FieldDate, DateTime.Parse("2005-10-10"), DateTime.Parse("2005-10-15")); Filter filter2 = new RangeFilter(FieldNumber, NumberTools.LongToString(11L), NumberTools.LongToString(13L), true, true); Query query = QueryParser.Parse("name*", FieldName, analyzer); query = new FilteredQuery(query, filter); query = new FilteredQuery(query, filter2); IndexSearcher searcher = new IndexSearcher(reader); Hits hits = searcher.Search(query);
6. 多域搜索
使用MultiFieldQueryParser實現多域搜索
權重影響域的優先級,而不是域的使用順序
Code [http://joinzhang.cnblogs.com]
Query query = MultiFieldQueryParser.Parse("name*", new string[] { FieldName, FieldValue }, analyzer); IndexReader reader = IndexReader.Open(directory); IndexSearcher searcher = new IndexSearcher(reader); Hits hits = searcher.Search(query);
7. 組合搜索
除了使用 QueryParser.Parse 分解復雜的搜索語法外,還可以通過組合多個 Query 來達到目的。
Code [http://joinzhang.cnblogs.com]
Query query1 = new TermQuery(new Term(FieldValue, "name1")); //詞語搜索 Query query2 = new WildcardQuery(new Term(FieldName, "name*")); //通配符 Query query3 = new PrefixQuery(new Term(FieldName, "name1")); //字段搜索 Field:Keyword,自動在結尾添加 * Query query4 = new RangeQuery(new Term(FieldNumber, NumberTools.LongToString(11L)), new Term(FieldNumber, NumberTools.LongToString(13L)), true); //范圍搜索 Query query5 = new FilteredQuery(query, filter); //帶過濾條件的搜索 BooleanQuery query = new BooleanQuery(); query.Add(query1, BooleanClause.Occur.MUST); query.Add(query2, BooleanClause.Occur.MUST); IndexSearcher searcher = new IndexSearcher(reader); Hits hits = searcher.Search(query);
8. 分布搜索
我們可以使用 MultiReader 或 MultiSearcher 搜索多個索引庫。
Code [http://joinzhang.cnblogs.com]
MultiReader reader = new MultiReader(new IndexReader[] { IndexReader.Open(@"c:\index"), IndexReader.Open(@"\\server\index") }); IndexSearcher searcher = new IndexSearcher(reader); Hits hits = searcher.Search(query);
或
Code [http://joinzhang.cnblogs.com]
IndexSearcher searcher1 = new IndexSearcher(reader1); IndexSearcher searcher2 = new IndexSearcher(reader2); MultiSearcher searcher = new MultiSearcher(new Searchable[] { searcher1, searcher2 }); Hits hits = searcher.Search(query);
還可以使用 ParallelMultiSearcher 進行多線程並行搜索。
9. 顯示搜索語法字符串
我們組合了很多種搜索條件,或許想看看與其對等的搜索語法串是什么樣的。
Code [http://joinzhang.cnblogs.com]
BooleanQuery query = new BooleanQuery(); query.Add(query1, true, false); query.Add(query2, true, false); //... Console.WriteLine("Syntax: {0}", query.ToString());
輸出:
Syntax: +(name:name* value:name*) +number:[0000000000000000b TO 0000000000000000d]
五.分詞
1.何謂分析器
分析(Analysis),在Lucene當中指的是將域(Field)文本轉換為最基本的索引單元——項(Term)的過程。
2.內置的分析器
KeywordAnalyzer
SimpleAnalyzer
StopAnalyzer
WhitespaceAnalyzer
StandardAnalyzer (最強大的了)
3.中文分詞
官方沒有自帶的中文分詞,可以選擇第三方的開源中文分詞,如盤古分詞
PS:例子程序采用的的Lucene.Net版本為2.9.2.1,文中所舉例子程序未必能兼容最新版本,用法請以例子程序為准。
例子程序采用的中文分詞為盤古分詞。其官網為http://pangusegment.codeplex.com/