Lucene 指南--史蒂文 J. 欧文斯

来源:百度文库 编辑:神马文学网 时间:2024/04/26 16:29:17
Lucene 指南
作者:史蒂文 J. 欧文斯
Jarkarta Lucene(http://jakarta.apache.org/lucene/) 是高性能的,充满特色的,java语言的,开源的文本搜索引擎,由Doug Cutting撰写其应用程序接口。
注意 Lucene 是一个具体的应用程序接口而不是应用程序。 这意味着所有的复杂部份已经被完成,留给用户您的只是容易的规划。您需要做的,不是去完成一个完整的搜寻引擎程序,你只需要花费非常少的时间通过一些的选项建立一个适合您所需要的搜寻程序。你能容易地扩展订制搜寻程序,使程序完全地符合您的需求。 Lucene 在这方面令人吃惊地容易扩展和使用。
下面介绍的前提是我把您看作是具备基本能力的程序师而且已基本掌握Java。
开始使用
这个指南是一个简述; Lucene 分别有以下四个类:
FileDocument IndexFiles SearchFiles DeleteFiles
这些类是对该如何使用 Lucene 的非常好的说明。如果对当前讨论的事物预先有个大体的模型理解,学习这些代码就会相对比较容易,所以我写了这篇指南,是因为它开始看起来就像原代码一样。现在我可以说使用Lucene确实非常的容易。
概述
我将在我介绍某个 Lucene API名时不时的使用斜体字的强调标签。
以下是用一个简单的图表表示Lucene的各个类如何组织的:
索引
文档
字段(名/值   )
Lucene的核心是索引。你将抽去数据进入索引,然后通过检索索引来取得搜索结果。为了建立索引,你要建立一个IndexWriter类型的对象。然后使用一个IndexSearcher类型的对象对索引进行搜寻。
搜寻本身是一个使用IndexSearcher.search()执行一个查询Query类型的对象。IndexSearcher.search()执行将返回一个匹配的结果对象,包含了一个Document类型对象的矢量集。
Document对象储存在索引中,但是他们必须放在索引中的一些特定点,那是你需要做的。你必须选择哪些数据进入, 并把他们转换成文档。你读取每笔由文件抽象成的数据文件 ( 可以是通过数据库进入, 或是任何其他方式), 将这些大块数据分解储存为字段格式的对象(名/值)。当你构造完一个文档,使用IndexWriter将它加入索引。
查询需求可能相当复杂,因此,Lucene包含一个帮助产生查询对象的工具,称做QueryParser。QueryParser带有一个查询字符串,它就像你使用一个英特网搜寻引擎时输入的字符串, 产生一个查询Query类型的对象。
注意:有时常出现gotcha(口语I have got you) ,因此,虽然它只是很低级的细节问题,我还是要在这里提到它。它起一个分析器的作用。Lucene对全文检索,第一步的一个步骤将清扫全文。你在这时会使用到一个分析器——它将消除标点和常用无意义的虚词(the, a, an等等)。Lucene 提供多种不同的分析器,并且你完全可以构造自己的分析器, 但是BIG GOTCHA能持续正常运行的前提是你必须在分析索引和搜索时使用相同的分析器。
你注意到上方没有的东西吗? Lucene处理索引,查询和反馈,但是它不处理:
管理进程(实例化对象并将他们连接在一起,在索引和搜索时)
·              选择数据文件
·              剖析数据文件
·              从用户取得检索字符串
·              向用户返回查询结果
那些未处理的部分是你需要完成的。在 Lucene contrib 你可以取得一些对您有帮助的工具和很好的范例,但是通常 Lucene 集中在完成索引和搜索, 其他的由你完成。 (因此你能构造相应的检索来满足你需求的方案)
我假定Lucene用于命令行驱动或网络驱动。我上文中的范例代码是通过命令行驱动的可查询的食谱数据库。以后我会再写关于如何使用网络驱动的Lucene应用程序并且把它作为例子加入这个指南文章。
不要自作聪明
你将会注意这是一个很平常的主题。如果你展开lucene用户列表而且听到Doug Cutting的解答,你会发现那个相同的主题。那一个主题是不要自作聪明, 你已具备足够的聪明来进行快速索引和搜索。这虽不能说它总是擅长使用暴力方法,但是在 Lucene 中,如果有一个简单的解法,那样或许作最大多数的感觉。记住Knuth的话: " 早期的最佳化是从源头就孕育罪恶".
构造索引或搜索
开始,你要抽取数据导入搜索程序之内(构造索引)或把数据从搜索程序中取出来(搜索).
我会详细察看这些类的次序,你将在范例源文件中遇到他们。好吧, 准确的说, 我将按照数据的顺序跟随浏览它们,即按照搜索请求从输入文件到输出文件。
如果你不确定你是否达到这种程度的认识, 建议看一看我的文章《核心概述》。
深度的索引
您通过建立字段型文档(由名/值 的对组成)产生索引,并将它们导入IndexWriter抽取其中的字段值而产生一个索引。
文档对象
Lucene不对文件编索引,它只对文档对象编索引。将其编入索引中然后从中搜寻文件, 你首先需要将这些文件转化为文档对象。
文档对象是一个字段对象的集合(名/值 对)。因此,对于每个文件,首先将其转化为一个文档,然后将其置入相应的字段。
这是最开始的高明之处, 取决于你给什么类型的文件建立索引,那些文件由多少数据构成, 以及多少种结构需要保存。 Lucene 仅仅处理名/值 对。电子邮件, 举例来说, 主要的名/值有:
发送到: fred
发送自: barney
主题: 晚餐?
正文: 让我们今晚共聚晚餐!
对于比较复杂的文件,你必须将那些结构化简为一组组名/值的字段。
顺便一提,我所说的虽然名为" 文件 ",但它数据源可以是任何东西——一个非常大的文件块,一组SQL查询的返回结果集,或是仅仅来自电子邮件的某条信息。
一个最小的标准的 Lucene 范例将是如下结构:
字段包含的内容
将会使用于场合
原始的文档路径
在检索之后把这些原始文档递交给实际用户
修改日期
与原始的文件修改日期相比较, 看看是否需要将它重新编入索引
文件的内容
搜索的目标
注意:这里只是举一个例子,并不是必须这样做。举例来说,如果你没有修改日期字段,没有什么关系,你只需要每次都对你的所有文件重建索引。(并且事实上,那是重建索引时推荐的标准方案, 在"不要自作聪明"的经验法则之下)
全字段
你也许想过将所有字段的内容干脆就合并在一起然后将它们整个儿保存在一个“全文”字段。 这是最容易的构造方法,这样你的用户能一次就搜寻他们想查找的所有的字段。是的,你可以提出一个复合方案重构用户的查询需求,如此它将对所有已知字段检索, 但是注意确保它够简单。
目录对象
你可以将一个索引保存在一个 Lucene 目录对象中。Lucene目录是一个Java文件系统抽象类。目录类使Lucene中具体类的行为隐藏。它让你只需要关注那些背后的事情,例如将文件暂存在内存中(Lucene受两个目录类影响, 为了保证高效执行,其一基于文件,另一则基于随机存取储存器)
分析器和分解器
分析器的工作是将字符串分解成断文流的形式。通过字符串中的常用字进行分解,那便是索引中所保存的内容(连同位置和其他的细节一起)。
每个分析器包含一个或多个的分解器而且可能包括过滤器。分解器按照日常规则将整个文章分解成一个个词。过滤器用在分解器之后完成剩下的工作。
Lucene 提供了一个分析器的抽象类,以及三个分析器的实现类。让我们具体看看:
简单分析器
简单分析器仅仅使用分解器将所有的输入分解
断词分析器
断词分析器包括基本分解,以及包含任何“断词”的过滤器。断词就相当与一些无意的虚词(a, an, the),它们在英语中出现的过于频繁,完全是检索中的额外无用的部分。断词分析器内置了一系列的这种无用的词,当然你也可以自己定义。
标准分析器
标准分析器完成以上两者所有的功能,此外还对无用的字符进行基本的清理。例如将无意义的分隔符删除(就像" T.L.A." 变成 " TLA")
这些分析器是英语专用的。Lucene的用户已经扩展了许多其他语言的分析器。在Lucene Sandbox里可以找到。如果你找不到你所用语言的分析器,你甚至可以自己扩展相关语言分析器。当你在学习的时候,使用简单分析器即可。
深度的搜索
为了实际的搜索,你需要一个索引搜索器IndexSearcher,但我们待会才讲到那儿; 在你之前你必须为索引搜索器提供一个查询体。索引搜索器并不直接通过索引检索,而是通过查询体进行的。
查询和查询解析器对象
当你把用户的参数字符串递交给QueryParser.parse(),你将得到一个查询体, 包括一个默认的检索字段 (如果用户不明确指出检索字段) 和一个分析器。 这个分析器是一个用来分解用户传入的参数的查询解析器。Gotcha 警告:再一次注意,你必须确定分解参数和分解索引时你使用的是相同类型的分析者。断词分析器或许一个安全的选择, 因为在范例代码中就时使用这个分析器。QueryParser.parse()的返回值为一个查询体对象。
查询解析器有一个parse()的静态方法,我猜那是为了方便起见。你可以用一个分析器以及默认字段的字符串来实例化一个查询解析器。 然而, 注意查询解析器QueryParser不是线程安全的,因此每一个线程必须有它自己的查询解析器。
索引检索器
得到索引检索器的一个简单实例仅需要告诉Lucene一个参数,使它知道到哪取得这个存在的索引。这个参数可以是以下两种类型之一:
·              包含文件的路径的字符串
·              一个 Lucene 目录类型的对象 (见"深度的索引")
复合索引
如果对单一索引进行检索,你将对单个索引使用索引检索器。如果需要在多个索引之间检索,你就要对每个索引建立索引检索器实例,并构造一个数组,将这些实例添加进数组中,然后将这个数组作为参数构造一个复合搜索器。
检索
为了实际的搜索,你要把用户输入的字符串参数传递给查询解析器( 而且记得 (第三次强调) 此处的查询解析器必须与你构造索引时使用同种类型的;查询解析器将调用分析器来分解字符串参数)并返回一个解析过的查询体对象。
然后你把这个解析过的查询体对象交给IndexSearcher.search()执行。它将返回一个查询命中目标的对象,这个命中对象是一个匹配的文档的集合。
命中
IndexSearcher.search(query)将返回一个命中对象, 它是一个矢量Vector,包含一系列Lucene文档对象的列表,即是和你传给IndexWriter相同的那些文档对象。现在,为了显示结果你必须对这个命中对象格式化, 或者产生指向原始文件所在的超链接,或者你必须对查询结果有个基本的处理计划。
这里没有提到的问题
这里没有提及一个Lucene项目需要包含哪些类,或是在上文中顺带提及。毕竟,这篇指南并不是要把所有的问题全部讨论, 否则的话,我只需要简单的让大家看源代码算了。
我强烈推荐大家首先边看这篇指南边分析提供的演示范例程序。然后,重复一次,。这一次当范例代码中调用Lucene类时,你再去查看Lucene的类的源代码看看它们究竟如何实现功能的。这不只是学习Lucene的一个好方法,这也是学习更多有关编程的一个优秀的方法。
后文
下面我们将继续我们的进程,实际的写一个检索程序的例子,构造索引并在上面进行检索。
在那之后,我们将真正的使用servlets和JSP建立一张网页搜索引擎的基本页面。我们已经看到Lucene是多么容易上手,而且servlet/ jsp的应用也不难(除非你自己想要搞复杂, 当然这是可能的)。今后也将介绍多线程的Lucene的相关问题问题。我们如此幸运Lucene真的是那么那么的简单易用, 因为大部分甚至所有的Lucene主类是线程安全的。
史蒂文 J. 欧恩,版权所有的版权 2001。