艾伟_转载: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);
Hits hits = searcher.Search(query);

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

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();
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));
IndexSearcher searcher = new IndexSearcher(reader);
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));

//...

// 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"));
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);
7. 分布搜索

我们可以使用 MultiReader 或 MultiSearcher 搜索多个索引库。

MultiReader reader = new MultiReader(new IndexReader[] { IndexReader.Open(@"c:\index"), IndexReader.Open(@"\\server\index") });
IndexSearcher searcher = new IndexSearcher(reader);
Hits hits = searcher.Search(query);

IndexSearcher searcher1 = new IndexSearcher(reader1);
IndexSearcher searcher2 = new IndexSearcher(reader2);
MultiSearcher searcher = new MultiSearcher(new Searchable[] { searcher1, searcher2 });
Hits hits = searcher.Search(query);

还可以使用 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());
writer.Close();

9. 显示搜索语法字符串

我们组合了很多种搜索条件,或许想看看与其对等的搜索语法串是什么样的。
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]

呵呵,就这么简单。

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);
IndexWriter writer = new IndexWriter(directory, analyzer, false);
writer.AddDocument(doc1);
writer.AddDocument(doc2);
writer.Optimize();
writer.Close();

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

 

 

//...
时间: 2024-10-09 16:26:49

艾伟_转载:Lucene.Net操作上的一些技巧的相关文章

艾伟_转载:在PC上显示和操作Windows Mobile

简介 在Windows Mobile和Wince开发中,直接在PC上操控相关设备是必不可少的步骤.本文讲述在PC上显示和操作Windows Mobile的方法,包括使用ActiveSync Remote Display和MyMobiler两种方法. 安装ActiveSync 不管那种方法,安装ActiveSync是必须的,请到下面链接进行下载安装ActiveSync 4.5. ActiveSync Remote Display ActiveSync Remote Display是MS提供的远程显

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.Inde

艾伟_转载:Visual Studio调试之断点技巧篇

文章导航 Visual Studio调试之断点基础篇 Visual Studio调试之断点进阶篇 Visual Studio调试之断点技巧篇 函数断点 在前面的文章Visual Studio调试之避免单步跟踪调试模式里面我讲了如何设置函数断点,说实话,我个人喜欢设置函数断点,而不是在代码行里面设置断点.一般来说,函数断点在下面几种情形下有用: 1.       例如调试一个网站程序,你通过分析网站的日志发现最有可能发生错误的函数,打开调试器并将调试器附加到程序上去,设置函数断点,重新执行网站--

艾伟_转载:Lucene.net操作索引库

  删除 (软删除,仅添加了删除标记.调用IndexWriter.Optimize() 后真正删除)IndexReader reader = IndexReader.Open(directory); // 删除指定序号(DocId)的 Document.reader.Delete(123); // 删除包含指定 Term 的 Document.reader.Delete(new Term(FieldValue, "Hello")); // 恢复软删除.reader.UndeleteAl

艾伟_转载:数组排序方法的性能比较(上):注意事项及试验

昨天有朋友写了一篇文章,其中比较了List的Sort方法与LINQ中排序方法的性能,而最终得到的结果是"LINQ排序方法性能高于List.Sort方法".这个结果不禁让我很疑惑.因为List.Sort方法是改变容器内部元素的顺序,而LINQ排序后得到的是一个新的序列.假如两个排序方法的算法完全一致,LINQ排序也比对方多出元素复制的开销,为什么性能反而会高?如果LINQ排序的算法/实现更为优秀,那为什么.NET Fx不将List.Sort也一并优化一下呢?于是今天我也对这个问题进行了简

艾伟_转载:Lucene.Net学习心得

一.Lucene点滴 (发音为['lusen]),我经常就读鹿神,是头活蹦乱跳的好鹿,研究它吧,保证感觉它很神!Lucene是一个非常优秀的开源的全文搜索引擎,我们可以在它的上面开发出各种全文搜索的应用来.Lucene在国外有很高的知名度,现在已经是Apache的顶级项目. 二.倒排索引原理简述 Lucene是一个高性能的java全文检索工具包,它使用的是倒排文件索引结构.具体解释算法理论就不讲了,直接用例子来说明吧,如果你认真仔细的读懂例子,真正领会了其中的思想,你肯定就明白了Lucene索引

艾伟_转载:基于.NET平台的Windows编程实战(四)—— 数据库操作类的编写

本系列文章导航 基于.NET平台的Windows编程实战(一)--前言 基于.NET平台的Windows编程实战(二)-- 需求分析与数据库设计 基于.NET平台的Windows编程实战(四)-- 数据库操作类的编写 基于.NET平台的Windows编程实战(五)-- 问卷管理功能的实现 基于.NET平台的Windows编程实战(六)-- 题目管理功能的实现 大家都知道本系统的正常运行少不了数据库操作这一块,且其在本系统中具有决定性作用,可以说没有它的操作系统将无法运行,故在本节课程中,专门把针

艾伟_转载:.NET Discovery 系列之三--深入理解.NET垃圾收集机制(上)

本系列文章导航 .NET Discovery 系列之一--string从入门到精通(上) .NET Discovery 系列之二--string从入门到精通(勘误版下) .NET Discovery 系列之三--深入理解.NET垃圾收集机制(上) .NET Discovery 系列之四--深入理解.NET垃圾收集机制(下) .Net Discovery 系列之五--Me JIT(上) .NET Discovery 系列之六--Me JIT(下) .NET Discovery 系列之七--深入理解

艾伟_转载:[一步一步MVC]第六回:什么是MVC(上)?

本系列文章导航 [一步一步MVC]第一回:使用ActionSelector控制Action的选择 [一步一步MVC]第二回:还是ActionFilter,实现对业务逻辑的统一Authorize处理 [一步一步MVC]第三回:MVC范例大观园 [一步一步MVC]第四回:漫谈ActionLink,有时"胡搅蛮缠" [一步一步MVC]第五回:让TagBuilder丰富你的HtmlHelper [一步一步MVC]第六回:什么是MVC(上)? 引言 所谓MVC,其实就是M.V.C而已.归根揭底,