天天看點

艾偉_轉載:Lucene.Net操作上的一些技巧

以下例子采用 Lucene.NET 1.9 版本,可取去 Lucene.Net 下載下傳。

1. 基本應用

using System;

using System.Collections.Generic;

using System.Text;

using Lucene.Net;

using Lucene.Net.Analysis;

using Lucene.Net.Analysis.Standard;

using Lucene.Net.Documents;

using Lucene.Net.Index;

using Lucene.Net.QueryParsers;

using Lucene.Net.Search;

using Lucene.Net.Store;

using Lucene.Net.Util;

namespace ConsoleApplication1.Lucene

{

public class LuceneTest

private const string FieldName = "name";

private const string FieldValue = "value";

private Directory directory = new RAMDirectory();

private Analyzer analyzer = new StandardAnalyzer();

public LuceneTest()

}

private void Index()

IndexWriter writer = new IndexWriter(directory, analyzer, true);

writer.maxFieldLength = 1000;

for (int i = 1; i <= 100; i++)

Document document = new Document();

document.Add(new Field(FieldName, "name" + i, Field.Store.YES, Field.Index.UN_TOKENIZED));

document.Add(new Field(FieldValue, "Hello, World!", Field.Store.YES, Field.Index.TOKENIZED));

writer.AddDocument(document);

writer.Optimize();

writer.Close();

private void Search()

Query query = QueryParser.Parse("name*", FieldName, analyzer);

IndexSearcher searcher = new IndexSearcher(directory);

Hits hits = searcher.Search(query);

Console.WriteLine("符合條件記錄:{0}; 索引庫記錄總數:{1}", hits.Length(), searcher.Reader.NumDocs());

for (int i = 0; i < hits.Length(); i++)

int docId = hits.Id(i);

string name = hits.Doc(i).Get(FieldName);

string value = hits.Doc(i).Get(FieldValue);

float score = hits.Score(i);

Console.WriteLine("{0}: DocId:{1}; Name:{2}; Value:{3}; Score:{4}",

i + 1, docId, name, value, score);

searcher.Close();

除了 RAMDirectory,還可以使用 FSDirectory。(注意 FSDirectory.GetDirectory 的 create 參數,為 true 時将删除已有索引庫檔案,可以通過 IndexReader.IndexExists() 方法判斷。)

從指定目錄打開已有索引庫。 

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");

2. 多字段搜尋 

使用 MultiFieldQueryParser 可以指定多個搜尋字段。

Query query = MultiFieldQueryParser.Parse("name*", new string[] { FieldName, FieldValue }, analyzer);

IndexReader reader = IndexReader.Open(directory);

IndexSearcher searcher = new IndexSearcher(reader);

3. 多條件搜尋

除了使用 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);

4. 設定權重

可以給 Document 和 Field 增權重重(Boost),使其在搜尋結果排名更加靠前。預設情況下,搜尋結果以 Document.Score 作為排序依據,該數值越大排名越靠前。Boost 預設值為 1。 

Score = Score * Boost 

通過上面的公式,我們就可以設定不同的權重來影響排名。

如下面的例子中根據 VIP 級别設定不同的權重。

switch (vip)

case VIP.Gold: document.SetBoost(2F); break;

case VIP.Argentine: document.SetBoost(1.5F); break;

隻要 Boost 足夠大,那麼就可以讓某個命中結果永遠排第一位,這就是百度等網站的"收費排名"業務。明顯有失公平,鄙視一把。 

5. 排序

通過 SortField 的構造參數,我們可以設定排序字段,排序條件,以及倒排。

Sort sort = new Sort(new SortField(FieldName, SortField.DOC, false));

Hits hits = searcher.Search(query, sort);

排序對搜尋速度影響還是很大的,盡可能不要使用多個排序條件。

6. 過濾 

使用 Filter 對搜尋結果進行過濾,可以獲得更小範圍内更精确的結果。

舉個例子,我們搜尋上架時間在 2005-10-1 到 2005-10-30 之間的商品。

對于日期時間,我們需要轉換一下才能添加到索引庫,同時還必須是索引字段。

 //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 對于數字進行了補位處理,如果需要使用浮點數可以自己參考源碼進行。

// index

document.Add(new Field(FieldNumber, NumberTools.LongToString((long)price), Field.Store.YES, Field.Index.UN_TOKENIZED));

Filter filter = new RangeFilter(FieldNumber, NumberTools.LongToString(100L), NumberTools.LongToString(200L), true, true);

使用 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 = new FilteredQuery(query, filter);

query = new FilteredQuery(query, filter2);

7. 分布搜尋

我們可以使用 MultiReader 或 MultiSearcher 搜尋多個索引庫。

MultiReader reader = new MultiReader(new IndexReader[] { IndexReader.Open(@"c:\index"), IndexReader.Open(@"\\server\index") });

IndexSearcher searcher1 = new IndexSearcher(reader1);

IndexSearcher searcher2 = new IndexSearcher(reader2);

MultiSearcher searcher = new MultiSearcher(new Searchable[] { searcher1, searcher2 });

還可以使用 ParallelMultiSearcher 進行多線程并行搜尋。

8. 合并索引庫

将 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());

9. 顯示搜尋文法字元串

我們組合了很多種搜尋條件,或許想看看與其對等的搜尋文法串是什麼樣的。

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]

呵呵,就這麼簡單。

10. 操作索引庫

删除 (軟删除,僅添加了删除标記。調用 IndexWriter.Optimize() 後真正删除。)

// 删除指定序号(DocId)的 Document。

reader.Delete(123);

// 删除包含指定 Term 的 Document。

reader.Delete(new Term(FieldValue, "Hello"));

// 恢複軟删除。

reader.UndeleteAll();

reader.Close();

增量更新 (隻需将 create 參數設為 false,即可往現有索引庫添加新資料。)

Directory directory = FSDirectory.GetDirectory("index", false);

IndexWriter writer = new IndexWriter(directory, analyzer, false);

writer.AddDocument(doc1);

writer.AddDocument(doc2);

11. 優化 

批量向 FSDirectory 增加索引時,增大合并因子(mergeFactor )和最小文檔合并數(minMergeDocs)有助于提高性能,減少索引時間。

writer.maxFieldLength = 1000; // 字段最大長度

writer.mergeFactor = 1000;

writer.minMergeDocs = 1000;

for (int i = 0; i < 10000; i++)

// Add Documentes...

繼續閱讀