本人看到这篇非常不错的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 级别设定不同的权重。

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对象可以被多个线程所共享)
巧用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 的构造参数,我们可以设置排序字段,排序条件,以及倒排。

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 之间的商品。
对于日期时间,我们需要转换一下才能添加到索引库,同时还必须是索引字段。

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

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 = searcher.Search(query);

8. 分布搜索
我们可以使用 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 进行多线程并行搜索。

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]

五.分词
1.何谓分析器
分析(Analysis),在Lucene当中指的是将域(Field)文本转换为最基本的索引单元——项(Term)的过程。

2.内置的分析器
KeywordAnalyzer
SimpleAnalyzer
StopAnalyzer
WhitespaceAnalyzer
StandardAnalyzer (最强大的了)

3.中文分词
官方没有自带的中文分词,可以选择第三方的开源中文分词,如盘古分词

例子源码下载 SourceCode
PS:例子程序采用的的Lucene.Net版本为2.9.2.1,文中所举例子程序未必能兼容最新版本,用法请以例子程序为准。
例子程序采用的中文分词为盘古分词。其官网为http://pangusegment.codeplex.com/

转自:http://www.cnblogs.com/JoinZhang/archive/2010/08/25/1808131.html

【转载】Lucene.Net入门教程及示例的更多相关文章

  1. Terraform入门教程,示例展示管理Docker和Kubernetes资源

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 简介 最近工作中用到了Terraform,权当学习记录一下,希望能帮助到其它人. Terraform系列文章如下: T ...

  2. TestNG 自动化测试入门教程--典型示例

    TestNG介绍 TestNG是Java中的一个测试框架, 类似于JUnit 和NUnit,   功能都差不多, 只是功能更加强大,使用也更方便 Java中已经有一个JUnit的测试框架了.  Tes ...

  3. 【转载】Systemd 入门教程:实战篇

    作者: 阮一峰 日期: 2016年3月 8日 上一篇文章,我介绍了 Systemd 的主要命令,今天介绍如何使用它完成一些基本的任务. 一.开机启动 对于那些支持 Systemd 的软件,安装的时候, ...

  4. Gulp入门教程(转载)

    本人转载自: Gulp入门教程

  5. 中文代码示例之Angular入门教程尝试

    原址: https://zhuanlan.zhihu.com/p/30853705 原文: 中文代码示例教程之Angular尝试 为了检验中文命名在Angular中的支持程度, 把Angular官方入 ...

  6. 2018-06-20 中文代码示例视频演示Python入门教程第三章 简介Python

    知乎原链 Python 3.6.5官方入门教程中示例代码汉化后演示 对应在线文档: 3. An Informal Introduction to Python 不知如何合集, 请指教. 中文代码示例P ...

  7. 2017-11-07 中文代码示例之Angular入门教程尝试

    "中文编程"知乎专栏原址 原文: 中文代码示例教程之Angular尝试 为了检验中文命名在Angular中的支持程度, 把Angular官方入门教程的示例代码中尽量使用了中文命名. ...

  8. ENVI Services Engine5.1 应用开发入门教程

    原文地址: ENVI Services Engine5.1 应用开发入门教程_ENVI-IDL中国_新浪博客 http://blog.sina.com.cn/s/blog_764b1e9d0102uy ...

  9. 正则表达式30分钟入门教程<转载>

    来园子之前写的一篇正则表达式教程,部分翻译自codeproject的The 30 Minute Regex Tutorial. 由于评论里有过长的URL,所以本页排版比较混乱,推荐你到原处查看,看完了 ...

随机推荐

  1. AMD加载器实现笔记(五)

    前几篇文章对AMD规范中的config属性几乎全部支持了,这一节主要是进一步完善.到目前为止我们的加载器还无法处理环形依赖的问题,这一节就是解决环形依赖. 所谓环形依赖,指的是模块A的所有依赖项的依赖 ...

  2. TW2015技术雷达中文版发布

    今天thoughtworks 2015新版技术雷达pdf发布了,你可以从这里下载http://engage.thoughtworks.com/HQ0000Q0QOf5pE70nbD00GP,在这里你可 ...

  3. [.net 面向对象编程基础] (19) LINQ基础

    [.net 面向对象编程基础] (19)  LINQ基础 上两节我们介绍了.net的数组.集合和泛型.我们说到,数组是从以前编程语言延伸过来的一种引用类型,采用事先定义长度分配存储区域的方式.而集合是 ...

  4. CSS3 动画一瞥

    伴随HTML5而来的CSS3让前端大湿们可以用简单的CSS样式即可写出动画效果来,而在这之前,一提到动画我们可能会想到JavaScript,Flash,Java插件等.如果是用JavaScript那倒 ...

  5. 知方可补不足~SQL2005使用ROW_NUMBER() OVER()进行数据分页

    回到目录 数据分页是这个经常说的东西,无论在WEBForm还是WinForm中它都会被单独拿出来,或者是公用组件,或者是公用类库,反正对于数据分页这个东西,总是我们关注的一个话题,但事实上,数据分页归 ...

  6. 搭建jekyll博客

    使用jekyll将markdown文件生成静态的html文件,并使用主题有序的进行布局,形成最终的博客页面. 特点 基于ruby 使用Markdown书写文章 无需数据库 可以使用GitHub Pag ...

  7. Atitit 马尔可夫过程(Markov process) hmm隐马尔科夫。 马尔可夫链,的原理attilax总结

    Atitit 马尔可夫过程(Markov process) hmm隐马尔科夫. 马尔可夫链,的原理attilax总结 1. 马尔可夫过程1 1.1. 马尔科夫的应用 生成一篇"看起来像文章的 ...

  8. Atitit intellij idea的使用总结attilax

    Atitit intellij idea的使用总结attilax 1. ideaIC-2016.2.4.exe1 1.1. Ij vs eclipse市场份额1 1.2. Ij的优点(方便的支持gro ...

  9. Android 4.2版本以下使用WebView组件addJavascriptInterface方法存在JS漏洞

    JS注入漏洞存在的Android版本:Android < 4.2 综述:Android的SDK中提供了一个WebView组件,用于在应用中嵌入一个浏览器来进行网页浏览.WebView组件中的ad ...

  10. TextView 显示内容时出现 ArrayIndexOutOfBoundsException 的解决方法(Android 4.1)

    很久以前做的表情输入及显示,用的系统的SpannableString,完成后的代码在其他版本的Android手机上没有问题,但是在在4.1和4.1.1的手机上显示时,有概率出现程序崩溃的问题. 下面是 ...