lucene排序、设置权重、优化、分布式搜索
1. 基本应用
?
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));2. 多字段搜索
使用 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);
3. 多条件搜索
除了使用 QueryParser.Parse 分解复杂的搜索语法外,还可以通过组合多个 Query 来达到目的。
Query query1 = new TermQuery(new Term(FieldValue, “name1″)); // 词语搜索?
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query);
4. 设置权重
可以给 Document 和 Field 增加权重(Boost),使其在搜索结果排名更加靠前。缺省情况下,搜索结果以 Document.Score 作为排序依据,该数值越大排名越靠前。Boost 缺省值为 1。
Score = Score * Boost通过上面的公式,我们就可以设置不同的权重来影响排名。
如下面的例子中根据 VIP 级别设定不同的权重。
Document document = new Document();只要 Boost 足够大,那么就可以让某个命中结果永远排第一位,这就是百度等网站的”收费排名”业务。明显有失公平,鄙视一把。
5. 排序
通过 SortField 的构造参数,我们可以设置排序字段,排序条件,以及倒排。
Sort sort = new Sort(new SortField(FieldName, SortField.DOC, false));?
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query, sort);
排序对搜索速度影响还是很大的,尽可能不要使用多个排序条件。
6. 过滤
使用 Filter 对搜索结果进行过滤,可以获得更小范围内更精确的结果。
举个例子,我们搜索上架时间在 2005-10-1 到 2005-10-30 之间的商品。
对于日期时间,我们需要转换一下才能添加到索引库,同时还必须是索引字段。
?
//…
// 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 对于数字进行了补位处理,如果需要使用浮点数可以自己参考源码进行。
?
//…
// search
Filter 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″));?
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);
7. 分布搜索
我们可以使用 MultiReader 或 MultiSearcher 搜索多个索引库。
MultiReader reader = new MultiReader(new IndexReader[] { IndexReader.Open(@”c:\index”), IndexReader.Open(@”\\server\index”) });或
IndexSearcher searcher1 = new IndexSearcher(reader1);还可以使用 ParallelMultiSearcher 进行多线程并行搜索。
8. 合并索引库
将 directory1 合并到 directory2 中。
Directory directory1 = FSDirectory.GetDirectory(“index1″, false);?
IndexWriter writer = new IndexWriter(directory2, analyzer, false);
writer.AddIndexes(new Directory[] { directory });
Console.WriteLine(writer.DocCount());
writer.Close();
9. 显示搜索语法字符串
我们组合了很多种搜索条件,或许想看看与其对等的搜索语法串是什么样的。
BooleanQuery query = new BooleanQuery();?
Console.WriteLine(“Syntax: {0}”, query.ToString());
输出:
Syntax: +(name:name* value:name*) +number:[0000000000000000b TO 0000000000000000d]
呵呵,就这么简单。
10. 操作索引库
删除 (软删除,仅添加了删除标记。调用 IndexWriter.Optimize() 后真正删除。)
IndexReader reader = IndexReader.Open(directory);?
// 删除指定序号(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);11. 优化
批量向 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();