本人看到這篇非常不錯的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();}
二.初識Lucene1. 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添加索引DocumentField(理解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 級別設定不同的權重。
Document document = new Document();switch (vip){ case VIP.Gold: document.SetBoost(2F); break; case VIP.Argentine: document.SetBoost(1.5F); break;}
只要 Boost 足夠大,那么就可以讓某個命中結果永遠排第一位,這就是百度等網站的"收費排名"業務。
4. Directory從指定目錄打開已有索引庫。
private Directory directory = FSDirectory.GetDirectory("c:/index", false);
將索引庫載入內存,以提高搜索速度。
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 中。
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)有助于提高性能,減少索引時間。
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對象可以被多個線程所共享)巧用RAMDirectory7.2安全鎖Lucene使用基于文件的鎖write.lock禁用索引鎖 (disableLuceneLocks=true)7.3并發訪問的規則任意數量的只讀操作都可以同時執行。在索引正在被修改時,我們也可以同時執行任意數量的只讀操作。在某一時刻,只允許執行一個修改索引的操作。
四.搜索1. IndexSearcher通過IndexSearcher執行搜索兩種構建IndexSearcher對象的方法: Directory對象與文件路徑。 (前者是推薦的)Search()方法
2. Query2.1創建Query對象使用QueryParset構建Query對象。(注:QueryParset把查詢表達式轉換成Lucene內置的查詢類型。)幾個常用的內置類型:TermQuery、RangeQuery、PrefixQuery、BooleanQuery。
2.2強悍的QueryParserQuery類的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.Hits3.1使用Hits對象訪問搜索結果3.2Hits類的幾個方法Length() Hits對象集合中所包含的文檔的數量Document(n) 排名第n的Document實例Id(n) 排名第n的DocumentIDScore(n) 排名第n的標準分值
4. 排序4.1使用Sort對象排序通過 SortField 的構造參數,我們可以設置排序字段,排序條件,以及倒排。
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 之間的商品。對于日期時間,我們需要轉換一下才能添加到索引庫,同時還必須是索引字段。
// indexdocument.Add(FieldDate, DateField.DateToString(date), Field.Store.YES, Field.Index.UN_TOKENIZED);//...// searchFilter 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 對于數字進行了補位處理,如果需要使用浮點數可以自己參考源碼進行。
// indexdocument.Add(new Field(FieldNumber, NumberTools.LongToString((long)price), Field.Store.YES, Field.Index.UN_TOKENIZED));//...// searchFilter filter = new RangeFilter(FieldNumber, NumberTools.LongToString(100L), NumberTools.LongToString(200L), true, true);Hits hits = searcher.Search(query, filter);
使用 Query 作為過濾條件。
QueryFilter filter = new QueryFilter(QueryParser.Parse("name2", FieldValue, analyzer));
我們還可以使用 FilteredQuery 進行多條件過濾。
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實現多域搜索權重影響域的優先級,而不是域的使用順序
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 來達到目的。
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 = search
新聞熱點
疑難解答