现在的位置: 首页 > 综合 > 正文

Lucene in Action (中文)

2013年08月13日 ⁄ 综合 ⁄ 共 28710字 ⁄ 字号 评论关闭

Lucene in Action

中文版

 

ERIK HATCHER

OTIS GOSPODNETIC

zw_ren译

 

主要内容

 

第一部分 Lucene核心

1.      接触Lucene  

2.      索引  

3.      为程序添加搜索

4.      分析

5.      高极搜索技术

6.      扩展搜索

第二部分 Lucene应用

7.      分析常用文档格式

8.      工具和扩充

9.      Lucene其它版本

10.   案例学习

 

 

 

Lucene开始是做为私有项目。在1997年末,因为工作不稳定,我寻找自己的一些东西来卖。Java是比较热门的编程语言,我需要一个理由来学习它。我已经了解如何来编写搜索软件,所以我想我可以通过用Java写搜索软件来维持生计。所以我写了Lucene。

几年以后,在2000年,我意识到我没有销售天赋。我对谈判许可和合同没有任何兴趣,并且我也不想雇人开一家公司。我喜欢做软件,而不是出售它。所以我把Lucene放在SourceForge上,看看是不是开源能让我继续我想做的。

有些人马上开始使用Lucene。大约一年后,在2001年,Apache提出要采纳Lucene。Lucene邮件列表中的消息每天都稳定地增长。也有 人开始贡献代码,大多是围绕Lucene的边缘补充:我依然是仅有的理解它的核心的开发者。尽管如些,Lucene开始成为真正的合作项目。

现在,2004年,Lucene有一群积极的深刻理解其核心的开发者。我早已不再每天作开发,这个强有力的工作组在进行实质性的增加与改进。

这些年来,Lucene已经翻译成很多其它的语言包括C++、C#、Perl和Python。在最开始的Java和其它这些语言中,Lucene的应用比 我预想的要广泛地多。它为不同的应用(如财富100公司讨论组、商业Bug跟踪、Microsoft提供的邮件搜索和100页面范围的Web搜索引擎)提 供搜索动力。在业内,我被介绍为“Lucene人”。很多人告诉我他们在项目中使用到Lucene。我依然认为我只听说了使用Lucene的程序的小部 分。

如果我当初只是出售它,Lucene应用得不会这么广泛。程序开发人员看来更喜欢开源。他们在有问题时不用联系技术支持而只需查看一下源代码。如果这还不够,邮件列表中的免费支持比大多商业支持要好得多。类似Lucene的开源项目使得程序开发人员更加有效率。

Lucene通过开源已经变得比我想象的伟大的多。我见证了它的发展,是Lucene社区的努力才使得它如此兴旺。

Lucene的未来怎样?我无法回答。有了这本书,你现在也是Lucene社区的一员,现在由您将Lucene带往新的高地。旅途顺利!

 

DOUG CUTTING

Lucene和Nutch的作者

前言

来自Erik Hatcher

在Internet早期我就对搜索和索引感兴趣。我已经建立了用majordomo、MUSH(Mail User’s Shell)和少量Perl、 awk及shell脚本来管理邮件列表的存储结构。我实现了一个CGI的web接口,允许用户搜索这个列表和其它用户的信息,其内部使用了grep。然后 相继出现了Yahoo!、AltaVista和Excite,这些我都经常访问。

在我有了第一个儿子Jakob之后,我开始了数字照片档案的设计。我想开发一套管理图片的系统,可以给图片附加元数据,如关键字、拍摄日期。当然用我选择 的尺寸定位图片是很容易的。在19世纪90年代末,我构建了基于文件系统的原型,使用了Microsoft的技术,包括 Microsoft Index Server、Action Server Pages及处理图片的第三方COM组件。从那时起,我的职业生涯都消耗在 这些类似的技术上了。 I was able to cobble together a compelling application in a couple of days of spare-time hacking.

我的职业转向Java技术,并且我越来越少地利用Microsoft Windows。为了以系统无关的方式用Java技术重新实现我的个人照片档案系统 及搜索引擎,我使用了Lucene。Lucene的简单易用远远超过了我的期望—我所期望的其它开源库或工具在概念上简单,但是却难以使用。

在2001年,Steve Loughran和我开始编写Java Development with Ant(Manning)。我们采用图片搜索引擎 的思想,并把它推广为一个文档搜索引擎。这个程序示例在那本Ant书中使用,而且可被定制为图片搜索引擎。Ant的责任不仅来自于简单的编译打包的构建过 程,也来自于定制的任务,<index>,我们在构建过程中使用Lucene创建索引文件。Ant任务现在生存在Lucene的 Sandbox(沙箱)中,将在本书8.4节描述。

Ant已经应用在我的博客系统中,我称为BlogScene(http://www.blogscene.org/erik)。 在建立一个博客实体之后,我运行一个Ant构建过程,索引新的实体并将它们上传到我的服务器上。我的博客服务器由一个Servlet、一些验证模板和一个 Lucene索引组成,允许(rich)查询,甚至联合查询。与其它博客系统相比,BlogScene在特色和技巧上差很多,但是它的全文检索能力非常强 大。

我现在效力于维吉尼亚大学对Patacriticism的应用研究小组(http://www.patacriticism.org)。我用对文本分析、索引和搜索的经验通过讨论量子力学与艺术的关系来测试及拓展我的思路。“诗人是世界上不被认可的最伟大的工程师”。

来自Otis Gospodnetic

我对信息搜索与管理的兴趣和热情开始于在Middlebury大学的学生时代。那时候,我发现了信息的广大资源,即Web。尽管Web仍然刚开始发展,但 是对收集、分析、索引和搜索的长期需求是很明显的。我开始对建立来自Web的信息库感到困惑,开始编写Web爬行器梦想有种方法可以对这些收集的信息进行 搜索。我认为在巨大的未知领域中搜索是杀手级软件。有了这种思想以后,我开始了一系列收集和搜索项目。

在1995年,和同学Marshall Levin一起创建了WebPh,一个用来收集和找出个人联系信息的开源程序。基本上,这是一个简单的具有Web 接口(CGI)的电话本,那时排在首位的类型。(实际上,它在19世纪90年代末的案例学习中被引用为一个示例。)大学和政府机构是这个程序的主要用户, 现在还有很多在使用它。在1997年使用我的WebPh,我继续创建了Populus,一个当时很流行的白页。尽管技术(与WebPh类似)很普通,但是 Populus有很重的负担,并且能够与WhoWhere、Bigfoot和Infospace等大角色相媲美。

在两个关于个人联系信息的项目之后,是该探索新的领域了。我开始了下一个冒险,Infojump,用来在网上时事通讯、杂志、报纸中选择高质量的信息。我 拥有的软件由大量的Perl模块和脚本组成,Infojump利用一个称作Webinator的Web爬行器和一个全文搜索的产品叫作Texis。在 1998年Infojump提供的服务很像今天的FindArticles.com。

尽管WebPh、Populus和Infojump达到了它们的目的并是功能很完善,但它们都有技术的局限性。它们缺少的是一个用反向索引来支持全文搜索 强大的信息搜索库。为了不重复相同的工作,我开始搜寻一个我认为不可能存在的解决方案。在2000年早期,我发现了Lucene,我正在寻找的缺少的部 分,并且我一下子就喜欢上了它。

我在Lucene还在SourceForge的时候就加入了这个项目,后来2002年Lucene转移到Apache软件基金会。我对Lucene的热爱 是因为这些年来它已经成为我很多思想的核心组件。这些思想中的一个是Simpy,我最近的一个项目。Simpy是个有许多特点的个性Web服务,可以让用 户加标签、索引、搜索和共享在网上找到的信息。它主要使用了Lucene,上千条索引,由Doug Cutting的另一个项目Nutch(见第10章) 提供动力支持。我对Lucene的积极参与导致我被邀请与Erik Hatcher共同编写Lucene in Action。

Lucene In Action有关于Lucene最全面的信息。接下来的10章包含的信息围绕你使用Lucene创建优秀程序所需的所有主题。这是它 平坦且轻快的协作过程的结果,就像Lucene社区一样。Lucene和Lucene in Action证明了有类似兴趣的人们可以完成什么,不管在人 生中会碰到什么情况,都会积极地为全球知识的共享做出贡献。

 

致谢

 

首先并且是最重要的,我们感谢我们的妻子Carole(Erik)和Margaret(Otis),一直支持这本书的写作。没有她们的支持,这本书就不可 能出版。Erik感谢他的两个儿子,Ethan和Jakob,因为他们的忍耐和理解,Erik写这本书时没有时间陪他们玩耍。

我们真诚感谢Doug Cutting。没有Doug的贡献,就不可能有Lucene。没有其他Lucene的贡献者,Lucene就会少很多特征、更多 的Bug,Lucene的成长就会花更长的时间。感谢所有的贡献者,包括Peter Carlson、Tal Dayan、Scott Ganyo、 Eugene Gluzberg、Brian Goetz、Christoph Goller、Mark Harwook、Tim Jones、 Daniel Naber、Andrew C. Oliver、Dmitry Serebrennikov、Kelvin Tan和 Matt Tucher。同时,我们感谢所有贡献在第10章的案例的人:Dion Almaer、Michael Cafarella、 Bob Carpenter、Karsten Konrad、Terence Parr、Robert Selvaraj、 Ralf Steinbach、Holger Stenzhorn和Craig Walls。

 

本书简介

 

Lucene in Action为使用最好的Java开源搜索引擎的用户提供所有细节、最好的实践、警告、技巧。

本书假设读者熟悉基本的Java编程。Lucene本身是个Java档案(JAR)文件并能集成到简单的命令行程序和大型企业级应用程序中。

Roadmap

我们在本书第1部分覆盖Lucene核心编程接口(API)使你在将Lucene整合到你的程序中时愿意使用它:

n        第1章,接触Lucene。我们介绍了一些基本的信息搜索术语和Lucene的主要竞争对手。我们很快地构建了一个你马上能用或修改以适应需要的简单索引和搜索程序。这个示例程序向你打开了探索Lucene其它能力的大门。

n        第2章使你熟悉Lucene基本的索引操作。我们描述了索引数值和日期的不同字段类型和各种技术。包括调整索引过程、优化索引以及如何处理线程安全。

n        第3章向你介绍基本的搜索,包括Lucene如何根据查询来排列文档的细节。我们讨论基础的查询类型及它们如何通过用户输入的查询表达式创建。

n        第4章深入研究Lucene的索引核心,分析过程。分析器创建块及单词、单词流和单词过滤器。我们创建了一些定制的分析器, showcasing synonym injection and metaphone(like soundex) replacement.也分析 了非英语语言,典型的分析汉字文本的示例。

n        第5章讲述搜索章节剩余的。我们描述了一些高级的搜索特征,包括排序、过滤及使用词向量。高级的查询类型在此出现,包括SpanQuery家族。最后,我们讨论了Lucene对查询多索引的内建支持,并行的及远程的。

n        第6章超越高级搜索,向你展示了如何扩展Lucene的搜索能力。你将学到如何定制搜索结果的排序、扩展查询表达式分析、实现Hit收集和调整查询性能。

第2部分超越Lucene内建的工具并向你展示围绕Lucene可以做什么。

n        第7章,我们创建了可重用、可扩展的用来分析Word、HTML、XML、PDF及其它格式文档的框架。

n        第8章包括围绕Lucene的扩展和工具。我们描述了一些Lucene的索引查看和开发工具以及Lucene沙箱中的好东西。高亮搜索 项就是这种你想要的沙箱扩展,还有在Ant构建过程中创建索引的其它工具。使用noncore分析器,并使用类似WordNet的索引。

n        第9章描述Lucene翻译成其它各种语言的版本,如C++、C#、Perl和Python。

n        第10章将Lucene的技术细节带到大量优秀的案例学习中。这些案例由那些创建了以Lucene为核心的有趣的、快速的、可升级的程序的开发者提供。

谁应该阅读本书?

在程序中需要强大搜索能力的开发人员需要阅读这本书。Lucene in Action也适合于那些对Lucene或索引和搜索技术好奇的开发人员,他们 可能不会马上就用到它。把Lucene添加到你的工具箱对以后的项目来说是值得的—搜索是个热门的话题并且将来也会是。

这本书主要使用Java版的Lucene(来自Apache Jakarta),并且大多数示例使用Java。最适合熟悉Java的读者。Java经验是 很有帮助的,然而Lucene已经翻译成很多其它的语言包括C++、C#、Python和Perl。概念、技术甚至API本身都和Java版Lucene 差不多。

代码示例

本书的源代码可以从Manning的网站http://www.manning.com/hatcher2上下载。代码的使用说明包含在代码包的README文件。

书中出现的大多数代码是由我们编写并包含在代码包中。某些代码(尤其是案例代码)不在我们的代码包中提供。书中的代码片断归贡献者所有。同时,我们包含了Lucene代码库的部分代码,基于Apache软件许可协议(http://www.apache.org/licenses/LICENSE-2.0)。

代码示例不包括package 和import 语句,以节省空间;具体请参照实际代码。

为什么是JUnit?

我们相信书中的代码示例应该都是高质量的。典型的“hello world”例子经常帮助读者测试他们的环境。

我们使用独特的方法来使用书中的代码示例。大部分示例是实际的JUnit测试用例(http://www.junit.org)。 JUnit,是Java单元测试框架,可以断言一个特殊情况是否能以可重复的方式出现。通过IDE或Ant进行自动JUnit测试用例可以一步一步地构筑 系统。我们在本书用使用JUnit是因为平时都在其它项目中使用,并想让你看看我们如何编码。测试驱动开发 (Test Driven Development, TDD)是我们强烈推荐的开发模式。

如果你对JUnit不熟,请阅读以下基础。我们也建议你阅读Dave Thomas和Andy Hunt编著的 《Pragmatic Unit Testing in Java with JUnit》,还有Vincent Massol和Ted Husted编 著的《JUnit in Action》。

JUnit基础

这部分是对JUnit快速但当然不完整的介绍。我们将提供理解我们示例代码所需的基础知识。首先,我们的JUnit测试用例继承 junit.framework.TestCase并且很多通过部LiaTestCase基类间接继承它。我们的具体测试类附合这个命名习惯:给类名加后 缀Test。例如,我们的QueryParser的测试是QueryParserTest.java。

JUnit自动执行所有类似public void testXXX()的方法,此处XXX是个任意有意义的名称。JUnit测试方法必须简洁,保持好的设计。(例如创建可重复的功能模块等等)

断言

JUnit建立在一组assert语句上,使你自由编写简洁的测试代码并使JUnit框架处理失败状态及指出细节。最常用的assert语句是assertEquals;一些是为不同的数据类型而重载的assertEquals方法。一个示例测试方法如下:

public void testExample() {

    SomeObject obj = new SomeObject();

    assertEqueals(10, obj.someMethod());

}

如果指定的值(在本例中的10)不等于真实值(本例中是调用obj的someMethod的返回值),assert方法抛出运行时异常。除了 assertEquals,为了方便还有一些其他assert方法。我们也使用assertTrue(expression)、assertFalse (expression)和assertNull(expression)语句。这些测试分别判断这个表达式是否是true、false和null。

assert语句有个接受一个附加的String参数的重载表示。String参数都是用来汇报的,在测试失败时向开发人员指出更多信息。我们使用这个String消息参数以更好的描述。

通过以这种风格编写我们的测试用例,可以从我们构建大系统的复杂中解放出来,而且可以每次只关注更少的细节。利用合适的测试用例,我们能够增强信心和灵活 性。信心来自于我们知道代码的变化如优化算法不会破坏系统的其它部分,因为出现这种情况的话,自动测试组件能让我们在它影响产品之前发现。重构是一种改变 代码内部结构的艺术(或者说科学),所以它能够适应变化的需求而又不影响系统的对外接口。

在上下文中的JUnit

让我们看一下到目前为止谈论的JUnit并把它放到本书的上下文中。JUnit测试用例继承于junit.framework.TestCase,且测试方法都类似public void testXXX()形式。我们的测试用例之一(第3章)如下:

public class BasicSearchingTest extends LiaTestCase {

    public void testTerm() throws Exception {

        IndexSearcher searcher = new IndexSearcher(directory);

        Term t = new Term(“subject”, “ant”);

        Query query = new TermQuery(t);

        Hits hits = searcher.search(query);

        assertEquals(“JDwA”, 1, hits.length());

                                              One hit expected for 

                                          search for “ant”

        t = new Term(“subject”, “junit”);

        hits = searcher.search(new TermQuery(t));

        assertEquals(2, hits.length());

                                           Two hits expected for “junit”

        searcher.close();

    }

}

当然,我们将在之后解释这个测试用例中使用的Lucene API。现在我们只关注JUnit的细节。testTerm方法中的directory变量没 在此类中定义。JUnit提供一个在执行每个测试方法之前的初始化钩子;这个钩子是名为public void setUp()的方法。我们的 LiaTestCase基类以这种方式实现setUp:

public abstract class LiaTestCase extends TestCase {

    private String indexDir = System.getProperty(“index.dir”);

    protected Directory directory;

 

    protected void setUp() throws Exception {

        directory = FSDirectory.getDirectory(indexDir, false);

    }

}

如果testTerm中的第一个断言失败,我们会得到一个异常:

junit.framework.AssertionFalsedError: JDwA expected:<1> but was:<0>

    at lia.searching.BasicSearchingTest.

→  testTerm(BasicSearchingTest.java:20)

这个失败指出我们的测试数据与预期的结果不同。

测试Lucene

本书中的大部分测试都是测试Lucene本身的。实际上,这是否现实呢?难道要测的不是我们自己写的代码而是库本身?有个 Test Driven Development的姊妹篇是用来学习API的:Test Driven Learning。它为新API写测试以了解它是 如何工作以及你能从中得到什么时非常有帮助。这正是我们在大部分代码示例中所做的,所以测试都是测试Lucene它本身。但是不要把这些为学习而做的测试 抛开。保留它们以确保你在升级到新版的API或因API改变而重构时,它们能够保持真值。

模型对象

在一些用例中,我们使用模型对象来测试。模型对象用来作为探测器传入真实的业务逻辑,以判断这个业务逻辑是否正常工作。例如,第4章中有个 SynonymEngine接口(4.6节)。使用这个接口的真实业务逻辑是个分析器。当我们想测试这个分析器本身时,SynonymEngine使用什 么类型就不重要,我们只想使用一个定义良好并有可预见行为的对象。模型对象可以使得测试用例尽可能简单,这样它们每次只测试系统的一个方面,在测试失败要 修正什么错误时没有纠缠的依赖。使用模型对象的一个好处来自于设计的变动,例如关系的分离和设计使用接口代替直接具体实现。

我们的测试数据

为了避免每个小节都要用完全不同的数据,书中大部多都是围绕一组公共的示例数据以提供一致性。这些示例数据由书籍详细资料组成。表1显示了这些数据,你可以参考它来理解我们的例子。

表1 本书中用到的示例数据

Title / Author
 Category
 Subject
 
A Modern Art of Education

Rudoif Steiner
 /education/pedagogy
 education philosophy

psychology practice Waldorf
 
Imperial Secrets of Health

and Logevity

Bob Flaws
 /health/alternative/Chinese
 diet chinese medicine qi

gong health herbs
 
Tao Te Ching 道德經

Stephen Mitchell
 /philosophy/eastern
 taoism
 
G&ouml;del, Escher, Bach:

an Eternal Golden Braid

Douglas Hofstadter
 /technology/computers/ai
 artificial intelligence number theory mathematics music
 
Mindstorms

Seymour Papert
 /technology/computers/programming/eduction
 children computers powerful

ideas LOGO eduction
 
Java Development with Ant

Erik Hatcher,

Steve Loughran
 /technology/computers/programming
 apache jakarta ant build tool

junit java development
 
JUnit in Action

Vincent Massol, Ted Husted
 /technology/computers/programming
 junit unit testing mock

objects
 
Lucene in Action

Otis Gospodnetic,

Erik Hatcher
 /technology/computers/programming
 lucene search
 
Extreme Programming

Explained

Kent Beck
 /technology/computers/programming/methodology
 extreme programming agile

test driven development

methodology
 
Tapestry in Action

Howard Lewis-Ship
 /technology/computers/programming
 tapestry web user interface

components
 
The Pragmatic Programmer

Dave Thomas, Andy Hunt
 /technology/computers/programming
 pragmatic agile methodology

developer tools
 

 

除了表中所显示的数据字段,还有ISBN、URL和出版日期。种类和标题字段是我们主观值,但是另一些都是有关这些书的真实客观值。

代码约定和下载

列表和正文中的代码都以等宽字体的形式出现以与普通文本区分。在正文中Java方法名称通常都不包含完整声明。

 

 

 

 

 

第一部分

Lucene核心

本书的前半部分覆盖了“盒子外的”(嗯…,JAR外的)Lucene。你将以整体的观点接触Lucene并开发一个完整的索引和搜索程序。每个后续的章节 系统地深入研究特定的内容。“索引”数据和文档和后来的对它们的“搜索”是使用Lucene的第一步。在从索引过程返回后,“分析”将使你深入理解在使用 Lucene索引文本时发生了什么。搜索是Lucene真正的亮点:本部分以仅使用内建特征的“高级搜索”技术结束,“扩展搜索”显示了Lucene针对 定制目的的扩展性。

 

第一章  接触Lucene

本章包括

n        理解Lucene

n        使用基本的索引API

n        使用搜索API

n        考虑可替换产品

 

Lucene流行和成功的一个关键因素是它的简单。… …

1.1 信息组织和访问的演化

 

 

 

 

 

 

 

 

 

 

 

 

 

1.2 理解Lucene

不同的人使用不同的方法解决相同的问题—即信息超负荷问题。一些人使用新的用户接口来工作,一些使用智能代理,还有一些使用发展较为成熟的搜索工具如 Lucene。本章稍后我们展示代码示例之前,我们将提供给你一张高层次的图来说明Lucene是什么,它不是什么和它以后会变得怎样。

1.2.1 Lucene是什么

Lucene是一个高性能、可伸缩的信息搜索(IR)库。它使你可以为你的应用程序添加索引和搜索能力。Lucene是用java实现的成熟的、免费的开 源项目,是著名的Apache Jakarta大家庭的一员,并且基于在Apache软件许可 [ASF, License]。同样,Lucene是当前 与近几年内非常流行的免费的Java信息搜索(IR)库。

 

注意   贯穿这本书,我们将使用术语IR(Information Retrieval)来描述像Lucene这样的搜索工具。人们常常将IR库归诸于搜索引擎,但是一定不要将IR库与web搜索引擎混为一谈。

 

正如你马上就会发现的,Lucene提供了一组简单却足够强大的核心API,只需要最小限度地理解全文索引和搜索。你只须学习它的几个类从而把 Lucene集成到一个应用程序中。因为Lucene是一个Java库,它并不限定要索引和搜索的内容,这使得它比其它一些搜索程序更具有优势。

刚接触Lucene的人经常把它误解为一个现成的程序,类似文件搜索程序或web网络爬行器或是一个网站的搜索引擎。那些都不是Lucene: Lucene是一个软件库,一个开发工具包(如果你想这样称呼),而不是一个具有完整特征的搜索应用程序。它本身只关注文本的索引和搜索,并且这些事它完 成的非常好。Lucene使得你的应用程序只针对它的问题域来处理业务规则,而把复杂的索引和搜索实现隐藏在一组简单易用的API之后。你可以把 Lucene认为成一层,应用程序位于它之上,如图1.5所示。

 

图1.5 一个集成Lucene的典型应用

 

大量基于Lucene的完整的搜索程序已经构建出来。如果你正在寻找预创建的东西或是一个抓取、文档处理和搜索的框架,请参考Lucene Wiki 的“powered by”页(http://wiki.apache.org/jakarta-lucene/PoweredBy)以获得更多选择:Zilverling、SearchBlox、Nutch、LARM和jSearch,还有其它一部分的命名。Nutch和SearchBlox的案例研究包含在第10章。

1.2.2 Lucene能做什么

Lucene使你可以为你的应用程序添加索引和搜索能力(这些功能将在1.3节中描述)。Lucene可以索引并能使得可以转换成文本格式的任何数据能够 被搜索。在图1.5可以看出,Lucene并不关心数据的来源、格式甚至它的语言,只要你能将它转换为文本。这就意味着你可经索引并搜索存放于文件中的数 据:在远程服务器上的web页面,存于本地文件系统的文档,简单的文本文件,微软Word文档,HTML或PDF文件或任何其它能够提取出文本信息的格 式。

同样,利用Lucene你可以索引存放于数据库中的数据,提供给用户很多数据库没有提供的全文搜索的能力。一旦你集成了Lucene,你的应用程序的用户 就能够像这样来搜索:+George +Rice –eat –pudding, Apple –pie +Tiger, animal: monkey AND food:banana等等。利用Lucene,你可以索引和搜索email邮件,邮件列表档案,即时聊天记录,你的Wiki页 面……等等更多。

 

1.2.3 Lucene的历史

Lucene最初是由Doug Cutting开发的,在SourceForge的网站上提供下载。在2001年9月做为高质量的开源Java产品加入到 Apache软件基金会的Jakarta家族中。随着每个版本的发布,这个项目得到明显的增强,也吸线了更多的用户和开发人员。2004年7月, Lucene1.4版正式发布,10月的1.4.2版本做了一次bug修正。表1.1显示了Lucene的发布历史。

 

表1.1 Lucene的发布历史

版本
 发布日期
 里程碑
 
0.01
 2000年3月
 第一个开源版本(SourceForge)
 
1.0
 2000年10月
  
 
1.01b
 2001年7月
 最后的SourceForge版本
 
1.2
 2002年6月
 第一个Apache Jakarta版本
 
1.3
 2003年12月
 复合索引格式,查询分析器增加,远程搜索,token定位,可扩展的API
 
1.4
 2004年7月
 Sorting, span queries, term vectors
 
1.4.1
 2004年8月
 排序性能的bug修正
 
1.4.2
 2004年10月
 IndexSearcher optimization and misc. fixes
 
1.4.3
 2004年冬
 Misc. fixes
 

 

注意      Lucene 的创建者,Doug Cutting,在信息搜索领域有很强的理论和实践经验。他发表过许多IR主题相关的研究论文并曾在Excite、Apple和 Grand Central等公司工作。最近,考虑到web搜索引擎数目的减少和这个领域的潜在垄断,他创建了Nutch,第一个开源的万维网搜索引擎(http://www.nutch.org),它用来处理抓取、索引和搜索数十亿时常更新的网页。毫不奇怪,Lucene是Nutch的核心,10.1节包括Nutch如何使用Lucene的案例研究。

Doug Cutting 仍然是Lucene的后台主力,但是自从Lucene加入到Apache Jakarta的庇护之后,更多的聪明智慧注入进 来。在本书写作时,Lucene的核心工作组有数个积极的开发者,其中两位就是本书的作者。除了官方的项目开发人员,Lucene拥有大量积极的技术用户 群,经常贡献补丁,Bug修复和新的特征。

1.2.4 谁在使用Lucene

谁不使用呢?除了在Lucene Wiki的Powered by Lucene页提到的那些组织外,还有大量的知名的跨图组织正在使用Lucene。它 为Eclipse IDE、Encyclopedia Britannica CD-ROM/DVD、FedEx、Mayo Clinic、 Hewlett-Packard、New Scientist杂志、Epiphany、MIT的OpenCourseware和Dspace、 Akamai的EdgeComputing平台等等提供搜索能力。你的名字也将会出现在这个列表中。

1.2.5 Lucene其它版本:Perl, Python, C++, .NET, Ruby

判断一个开源软件是否成功的一种方法是通过它被改编为其它语言版本的数量。使用这个标准,Lucene是非常成功的!尽管开始时Lucene是用Java 写的,Lucene已经有很多其它语言的版本了:Perl,Python,C++和.NET,并且一些基础已经用Ruby实现了。这对于那些需要访问用不 同的语言写成的应用程序所得到的Lucene索引的开发者来说是个好消息。在第9章你将了解更多关于这方面的东西。

1.3 索引和搜索

所有搜索引擎的核心就是索引的概念:将原始数据处理成一个高效的交差引用的查找结构以便于快速的搜索。让我们对索引和搜索过程做一次快速的高层次的浏览。

1.3.1 什么是索引,为什么它很重要?

想像一下,你需要搜索大量的文件,并且你想找出包含一个指定的词或短语的文件。你如何编写一个程序来做到这个?一个幼稚的方法是针对给定的词或短语顺序扫 描每个文件。这个方法有很多缺点,最明显的就是它不适合于大量的文件或者文件非常巨大的情况。这时就出现了索引:为了快速搜索大量的文本,你必须首先索引 那个文本然后把它转化为一个可以让你快速搜索的格式,除去缓慢的顺序地扫描过程。这个转化过程称为索引,它的输出称为一条索引。

你可以把索引理解为一个可以让你快速随机访问存于其内部的词的数据结构。它隐含的概念类似于一本书最后的索引,可以让你快速找到讨论指定主题的页面。在 Lucene中,一个索引是一个精心设计的数据结构,在文件系统中存储为一组索引文件。我们在附录B中详细地说明了索引文件的结构,但是目前你只须认为 Lucene的索引是一个能快速的词汇查找的工具。

1.3.2 什么是搜索?

搜索是在一个索引中查找单词来找出它们所出现的文档的过程。一个搜索的质量用精确度和召回率来描述。召回率衡量搜索系统搜索到相关文档的能力,精确度衡量 系统过滤不相关文档的能力。然而,在考虑搜索时你必须考虑其它一些因素。我们已经提到速度和快速搜索大量文本的能力。支持单个和多个词汇的查询,短语查 询,通配符,结果分级和排序也是很重要的,在输入这些查询的时候也是友好的语法。Lucene强大的软件库提供了大量的搜索特征、bells和 whistles,所以我们不得不把关于搜索的讨论展开为三章(第3、5、6章)。

1.4 Lucene实战:一个简单的程序

让我们来实战Lucene。首先回忆在1.3.1节描述的索引和搜索文件的问题。此外,假设你要索引和搜索存放于一个目录树中的文件,并不只在一个目录 中。为了向你展示Lucene的索引和检索能力,我们将用到两个命令行程序:Indexer和Searcher。首先我们将索引一个包含文本文件的目录 树,然后我们搜索创建的索引。

这个示例程序将使你熟悉Lucene的API,简单易用而功能强大。代码清单是完整的,现成的命令行程序。如果文件索引/搜索是你要解决的问题,那你可复制一份代码,用它来适应你的需要。在接下来的章节中,我们将更深入的描述Lucene使用中的每个方面。

在我们可以利用Lucene搜索之前,需要创建一个索引,所以我们开始Indexer程序。

1.4.1 创建一个索引

在本节中,你将看到一个名为Indexer的类和它的四个静态方法。它们共同递归遍历文件系统目录并索引所有具有.txt扩展名的文件。当Indexer执行完毕时,为它的后续Searcher(在1.4.2小节中介绍)留下一个创建好的Lucene索引。

我们不期望你熟悉例子中用到的几个Lucene类和方法,我们马上就会解释它们。在有注释的代码列表之后,我们向你展示了如何使用Indexer。如果你感觉在看到编码之前学习Indexer如何使用很有帮助,直接跳到代码后面的用法讨论部分。

使用Indexer来索引文本文件

列表1.1展示了Indexer命令行程序。它用到两个参数:

n        我们存放Lucene索引的路径

n        包含我们要索引的文本文件的路径

 

列表 1.1 Indexer:遍历文件系统并且索引.txt文件

/**

 * This code was originally written for

 * Erik’s Lucene intro java.net article

 */

public class Indexer {

    public static void main(String[] args) throws Exception {

        if (args.length != 2) {

            throw new Exception(“Usage: java ” + Indexer.class.getName()

                + “ <index dir> <data dir>”);

        }

        File indexDir = new File(args[0]);

        File dataDir = new File(args[1]);

 

        long start = new Data().getTime();

        int numIndexed = index(indexDir, dataDir);

        long end = new Date().getTime();

 

        System.out.println(“Indexing ” + numIndexed + “ files took ”

            + (end - start) + “ milliseconds”);

    }

    

    // open an index and start file directory traversal

    public static int index(File indexDir, File dataDir)

        throws IOException {

        if (!dataDir.exists() || !dataDir.isDirectory()) {

            throw new IOException(dataDir

                + “ does not exist or is not a directory”);

        }

 

        IndexWriter writer = new IndexWriter(indexDir,   ① 创建Lucene索引

            new StandardAnalyzer(), true); 

        writer.setUseCompoundFile(false);

 

        indexDirectory(writer, dataDir);

 

        int numIndexed = writer.docCount();

        writer.optimize();

        writer.close();

        return numIndexed;

    }

 

    // recursive method that calls itself when it finds a directory

    private static void indexDirectory(IndexWriter writer, File dir)

        throws IOException {

 

        File[] files = dir.listFiles();

        for (int i = 0; i < files.length; i++) {

            File f = files;

            if (f.isDirectory()) {

                indexDirectory(writer, f);    ② 递归

            } else if (f.getName().endsWith(“.txt”)) {

                indexFile(writer, f);

            }

        }

    }

 

    // method to actually index file using Lucene

    private static void indexFile(IndexWriter writer, File f)

        throws IOException {

 

        if (f.isHidden() || !f.exists() || !f.canRead()) {

            return;

        }

        System.out.println(“Indexing ” + f.getCanonicalPath());

 

        Document doc = new Document();

        doc.add(Field.Text(“contents”, new FileReader(f)));  ③ 索引文件

                                                          内容

        doc.add(Field.Keyword(“filename”, f.getCannicalPath())); ④ 索引

                                                          文件名称

        writer.addDocument(doc);  ⑤ 添加片段到Lucene索引

    }

}

有趣的是,代码的大部分是执行目录的遍历()。只有IndexWriter的创建和关闭()和IndexFile方法中的四行()使用了Lucene API—有效的6行代码。

这个示例只关注.txt扩展名的文本文件是为了在说明Lucene的用法和强大功能时保持尽量简单。在第7章,我们将向你展示如何处理非文本文件,并且我们开发了一个现成的小框架来分析和索引几种常见的格式的文档。

运行Indexer

在命令行中,我们针对包含Lucene本身的源文件的本地工作目录运行Indexer。我们使Indexer索引/lucene目录下的文件并将Lucene 索引保存在build/index目录中。

% java lia.meetlucene.Indexer build/index /lucene

 

Indexing /lucene/build/test/TestDoc/test.txt

Indexing /lucene/build/test/TestDoc/test2.txt

Indexing /lucene/BUILD.txt

Indexing /lucene/CHANGES.txt

Indexing /lucene/LICENSE.txt

Indexing /lucene/README.txt

Indexing /lucene/src/jsp/README.txt

Indexing /lucene/src/test/org/apache/lucene/analysis/ru/

→  stemsUnicode.txt

Indexing /lucene/src/test/org/apache/lucene/analysis/ru/

→  test1251.txt

Indexing /lucene/src/test/org/apache/lucene/analysis/ru/

→  testKOI8.txt

Indexing /lucene/src/test/org/apache/lucene/analysis/ru/

→  testUnicode.txt

Indexing /lucene/src/test/org/apache/lucene/analysis/rn/

→  wordsUnicode.txt

Indexing /lucene/todo.txt

Indexing 13 files took 2205 milliseconds

 

Indexer打印出索引的文件名称,你可以看出它只索引扩展名为.txt的文本文件。

 

注意      如果你在Windows平台的命令行中运行这个程序,你需要调整命令行的目录和路径分割符。Windows命令行是java build/index c:/lucene。

 

当索引完成后,Indexer输出它索引的文件数目和所花费的时间。因为报告的时间包含文件目录遍历和索引,你不能把它做为一个正式的性能衡量依据。在我们的示例中,每个索引的文件都很小,但只有了2秒索引这些文件还是不错的。

索引速度是要关注的,我们将在第2章中讨论它。但是通常,搜索更加重要。

1.4.2 在索引中搜索

在Lucene中搜索和索引一样高效和简单。它的功能惊人地强大,在第3章和第5章你将看到。现在,让我们看一下Searcher,一个我们用来搜索 Indexer创建的索引的命令行程序。(记住我们的Seacher只是用来示范Lucene的搜索API的用法。你的搜索程序也可以是网页或带有GUI 的桌面程序或EJB等形式。)

在上一部分,我们索引了一个目录中的文本文件。在本例中的索引,放在文件系统的一个目录中。我们让Indexer在build/index目录中创建 Lucene索引,这个目录和我们调用Indexer的目录相关。在列表1.1中看出,这个索引包含被索引的文件和它们的绝对路径。现在我们要用 Lucene来搜索这个索引以找出包含指定文本片段的文件。例如,我们可能想找出包含关键字java或Lucene的所有文件,或者可能想找出包含短语 “system requirements”的所有文件。

使用Searcher实现搜索

Searcher程序和Indexer相辅相成并提供命令行搜索的能力。列表1.2展示了Searcher的全部代码。它接受两个命令行参数:

n        Indexer创建的索引的路径

n        搜索索引的查询

 

列表 1.2 Searcher:为参数传来的查询搜索Lucene索引

/**

 * This code was originally written for

 * Erik’s Lucene intro java.net article

 */

public class Searcher {

    public static void main(String[] args) throws Exception {

        if (args.length != 2) {

            throw new Exception(“Usage: java ” + Searcher.class.getName()

                + “ <index dir> <auery>”);

        }

        File indexDir = new File(args[0]);

        String q = args[1];

        

        if (!indexDir.exists() || !indexDir.isDirectory()) {

            throw new Exception(indexDir +

                “ does not exist or is not a directory.”);

        }

        search(indexDir, q);

    }

    public static void search(File indexDir, String q)

        throws Exception {

        Directory fsDir = FSDirectory.getDirectory(indexDir, false);

        IndexSearcher is = new IndexSearcher(fsDir); ① 打开索引

 

        Query query = QueryParser.parse(q, “contents”,  ② 分析查询

            new StandardAnalyzer());

        long start = new Date().getTime();

        Hits hits = is.search(query);      ③ 搜索索引

        long end = new Date().getTime();

 

        System.err.println(“Found ” + hits.length() +

            “ document(s) (in ” + (end - start) +

            “ milliseconds) that matched query ‘” +

               q + “’:”);

 

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

            Document doc = hits.doc(i);        ④ 得到匹配的文档

            System.out.println(doc.get(“filename”));

        }

    }

}

Searcher类似于Indexer,只有几行代码与Lucene相关。在search方法中出现了几种特别的事物,

 我们使用Lucene的IndexSearcher和FSDirectory类来打开我们的索引以进行搜索。

 我们使用QueryParser来把human-readable查询分析成Lucene的查询类。

 搜索以一个Hits对象的形式返回结果集。

 注意Hits对象包含的仅仅是隐含的文档的引用。换句话说,不是在搜索的时候立即加载,而是采用从索引中惰性加载的方式—仅当调用hits.doc(int)时。

运行Searcher

让我们运行Searcher并用‘lucene’查询在索引中找出几个文档:

%java lia.meetlucene.Searcher build/index ‘lucene’

Found 6 document(s) (in 66 milliseconds) that matched query ‘lucene’:

/lucene/README.txt

/lucene/src/jsp/README.txt

/lucene/BUILD.txt

/lucene/todo.txt

/lucene/LICENSE.txt

/lucene/CHANGES.txt

输出显示我们用Indexer索引的13个文档中的6个含有lucene这个单词,而且这次搜索花费66毫秒。因为Indexer在索引中存放了文件的绝 对路径,Searcher可以输出它们。在这个例子中,我们决定把文件和路径存为一个字段并没有考虑什么,但是以Lucene的观点,可以给要索引的文档 附加任意元数据。

当然,你可以使用更多复杂的查询,例如‘lucene AND doug’或者‘lucene AND NOT slow’或‘+lucene +book’等等。第3、5和第6章所有搜索的不同方面,包括Lucene的查询语法 。
使用xargs工具

Searcher类对Lucene的搜索特征的非常简化的示例。所以,它仅仅把匹配结果输出到标准输出上。然而,Searcher还有另一个技巧。考虑你 需要找出含有指定的关键词或短语的文件,并且你想以某种方式处理这些匹配文件。为了保持简单性,让我们考虑你想使用UNIX命令ls列出每个匹配的文件, 或许看看该文件的大小、许可位或拥有者。既然已经简单地把匹配文档的路径写到标准输出上,又把统计输出写到了标准错误上,你可以利用UNIX的xargs 工具来处理匹配文件,如下:

%java lia.meetlucene.Searcher build/index 

→  ‘lucene AND NOT slow’ | xargs ls –l

 

Found 6 document(s) (in 131 milliseconds) that

→  matched query ‘lucene AND NOT slow’:

-rw-r--r-- 1 erik staff 4215 10 Sep 21:51 /lucene/BUILD.txt

-rw-r--r-- 1 erik staff 17889 28 Dec 10:53 /lucene/CHANGES.txt

-rw-r--r-- 1 erik staff 2670 4 Nov 2001 /lucene/LICENSE.txt

-rw-r--r-- 1 erik staff 683 4 Nov 2001 /lucene/README.txt

-rw-r--r-- 1 erik staff 370 26 Jan 2002 /lucene/src/jsp/

→  README.txt

-rw-r--r-- 1 erik staff 943 18 Sep 21:27 /lucene/todo.txt

在这个例子中,我们选择布尔查询‘lucene AND NOT slow’来找出所有含有单词lucene但不含有单词slow的文件。这个查询花费了 131毫秒并找出6个匹配文件。我们把Searcher的输入传递给xargs命令,它将会依次使用ls –l命令来列出每个匹配的文件。与之类似,这些 匹配文件可以被复制、连接、email或打印到标准输出。

我们的索引和搜索应用程序示例展示了Lucene的优点。它的API使用简洁。代码的大小(并且这适用于所有使用Lucene的程序)与业务目的密切相关 --在这个示例中,是Indexer中负责寻找文本文件的文件系统爬行器和Searcher中打印某一查询的匹配文件名称到标准输出的代码。但是别让这个 事实或者这个示例的简单让你觉得自满:在Lucene的表面下有相当多的事情发生,而且我们还用到了一些最好的实践经验。为了更好的使用Lucene,更 深入地理解它如何工作及如何在需要的时候扩展它是很重要的。本书作者毫无保留地给出了这些。

1.5 理解核心索引类

在Indexer类中可见,你需要以下类来执行这个简单的索引过程:

n        IndexWriter

n        Directory

n        Analyzer

n        Document

n        Field

接下来是对这些类的一个简短的浏览,针对它们在Lucene的角色,给出你粗略的概念。我们将在整本书中使用这些类。

1.5.1 IndexWriter

IndexWriter是在索引过程中的中心组件。这个类创建一个新的索引并且添加文档到一个已有的索引中。你可以把IndexWriter想象成让你可 以对索引进行写操作的对象,但是不能让你读取或搜索。不管它的名字,IndexWriter不是唯一的用来修改索引的类,2.2小节描述了如何使用 Lucene API来修改索引。

1.5.2 Directory

Directory类代表一个Lucene索引的位置。它是一个抽象类,允许它的子类(其中的两个包含在Lucene中)在合适时存储索引。在我们的 Indexer示例中,我们使用一个实际文件系统目录的路径传递给IndexWriter的构造函数来获得Directory的一个实例。 IndexWriter然后使用Directory的一个具体实现FSDirectory,并在文件系统的一个目录中创建索引。

在你的应用程序中,你可能较喜欢将Lucene索引存储在磁盘上。这时可以使用FSDirectory,一个包含文件系统真实文件列表的Driectory子类,如同我们在Indexer中一样。

另一个Directory的具体子类是RAMDirectory。尽管它提供了与FSDirectory相同的接口,RAMDirectory将它的所有 数据加载到内存中。所以这个实现对较小索引很有用处,可以全部加载到内存中并在程序关闭时销毁。因为所有数据加载到快速存取的内存中而不是在慢速的硬盘 上,RAMDirectory适合于你需要快速访问索引的情况,不管是索引或搜索。做为实例,Lucene的开发者在所有他们的单元测试中做了扩展使用: 当测试运行时,快速的内存驻留索引被创建搜索,当测试结束时,索引自动销毁,不会在磁盘上留下任何残余。当然,在将文件缓存到内存的操作系统中使用时 RAMDirectory和FSDirectory之间的性能差别较小。你将在本书的代码片断中看到Directory的两个实现的使用。

1.5.3 Analyzer

在文本索前之前,它先通过Analyzer。Analyzer在IndexWriter的构造函数中指定,司职对文本内容提取关键词并除去其它的。如果要 索引的内容不是普通的文本,首先要转化成文本,如果2.1所示。第7章展示了如何从常见的富媒体文档格式中提取文本。Analyzer是个抽象类,但是 Lucene中有几个它的实现。有的处理的时候跳过终止词(不能用来把某个文件与其它文件区分开的常用的词);有的处理时把关键字转化为小写字母,所以这 个搜索不是大小写敏感等等。Analyzer是Lucene的一个重要的部分并且不只是在输入过滤中使用。对一个将Lucene集成到应用程序中的开发者 来说,对Analyzer的选择在程序设计中是重要元素。你将在第4章学到更多有关的知识。

1.5.4 Document

一个Document代表字段的集合。你可以把它想象为以后可获取的虚拟文档—一块数据,如一个网页、一个邮件消息或一个文本文件。一个文档的字段代表这 个文档或与这个文档相关的元数据。文档数据的最初来源(如一条数据库记录、一个Word文档、一本书的某一章等等)与Lucene无关。元数据如作者、标 题、主题、修改日期等等,分别做为文档的字段索引和存储。

 

注意      当我们在本书中提到一个文档,我们指一个Microsoft Word、RTF、PDF或其它文档类型;我们不是谈论Lucene的Document类。注意大小写和字体的区别。

 

Lucene只用来处理文本。Lucene的核心只能用来处理java.lang.String和java.io.Reader。尽管很多文档类型都能被 索引并使之可搜索,处理它们并不像处理可以简单地转化为java的String或Reader类型的纯文本内容那样直接。你将在第7章学到处理非文本文 档。

在我们的Indexer中,我们处理文本文件,所以对我们找出的每个文本文件,创建一个Document类的实例,用Field(字段)组装它,并把这个Document添加到索引中,完成对这个文件的索引。

1.5.5 Field

在索引中的每个Document含有一个或多个字段,具体化为Field类。每个字段相应于数据的一个片段,将在搜索时查询或从索引中重新获取。

Lucene提供四个不同的字段类型,你可以从中做出选择:

n        Keyword—不被分析,但是被索引并逐字存储到索引中。这个类型适合于原始值需要保持原样的字段,如URL、文件系统路径、日期、 个人名称、社会安全号码、电话号码等等。例如,我们在Indexer(列表1.1)中把文件系统路径作为Keyword字段。

n        UnIndexed—不被分析也不被索引,但是它的值存储到索引中。这个类型适合于你需要和搜索结果一起显示的字段(如URL或数据库 主键),但是你从不直接搜索它的值。因为这种类型字段的原始值存储在索引中,这种类型不适合于存放比较巨大的值,如果索引大小是个问题的话。

n        UnStored—和UnIndexed相反。这个字段类型被分析并索引但是不存储在索引中。它适合于索引大量的文本而不需要以原始形式重新获得它。例如网页的主体或任休其它类型的文本文档。

n        Text—被分析并索引。这就意味着这种类型的字段可以被搜索,但是要小心字段大小。如果要索引的数据是一个String,它也被存 储;但如果数据(如我们的Indexer例子)是来自一个Reader,它就不会被存储。这通常是混乱的来源,所以在使用Field.Text时要注意这 个区别。

所有字段由名称和值组成。你要使用哪种字段类型取决于你要如何使用这个字段和它的值。严格来说,Lucene只有一个字段类型:以各自特征来区分的字段。有些是被分析的,有些不是;有些是被索引,然面有些被逐字地存储等等。

表1.2提供了不同字段特征的总结,显示了字段如何创建以及基本使用示例。

 

表1.2 不同字段类型的特征和使用方法

Fied method/type
 Analyzed
 Indexed
 Stored
 Example usage
 
Field.Keyword(String,String)

Field.Keyword(String,Date)
  
 &#10004;
 &#10004;
 Telephone and Social Security numbers, URLs, personal names, Dates
 
Field.UnIndexed(String,

String)
  
  
 &#10004;
 Document type (PDF, HTML, and so on), if not used as search criteria
 
Field.UnStored(String,String)
 &#10004;
 &#10004;
  
 Document titles and content
 
Field.Text(String,String)
 &#10004;
 &#10004;
 &#10004;
 Document titles and content
 
Field.Text(String,Reader)
 &#10004;
 &#10004;
  
 Document titles and content
 

 

注意所有字段类型都能用代表字段名称和它的值的两个String来构建。另外,一个Keyword字段可以接受一个String和一个Date对象, Text字段接受一个String和一个Reader对象。在所有情况下,这些值在被索引之前都先被转化成Reader,这些附加方法的存在可以提供比较 友好的API。

 

注意      注意Field.Text(String, String)和 Field.Text(String, Reader)之间的区别。String变量存储字段数据,而Reader变量不存储。为索引一个String而 又不想存储它,可以用Field.UnStored(String, String)。

 

最后,UnStored和Text字段能够用来创建词向量(高级的话题,在5.7节中描述)。为了让Lucene针对指定的UnStored或Text字 段创建词向量,你可以使用Field.UnStored(String, String, true),Field.Text(String,  String, true)或Field.Text(String, Reader, true)。

在使用Lucene来索引时你会经常用到这几个类。为了实现基本的搜索功能,你还需要熟悉同样简单的几个Lucene搜索类。

1.6 理解核心搜索类

Lucene提供的基本搜索接口和索引的一样直接。只需要几个类来执行基本的搜索操作:

n        IndexSearcher

n        Term

n        Query

n        TermQuery

n        Hits

接下来的部分对这些类提供一个简要的介绍。我们将在深入更高级主题之前,在接下来的章节中展开这些解释。

1.6.1 IndexSearcher

IndexSearcher用来搜索而IndexWriter用来索引:暴露几个搜索方法的索引的主要链接。你可以把IndexSearcher想象为以 只读方式打开索引的一个类。它提供几个搜索方法,其中一些在抽象基类Searcher中实现;最简单的接受单个Query对象做为参数并返回一个Hits 对象。这个方法的典型应用类似这样:

IndexSearcher is = new IndexSearcher(

FSDirectory.getDirectory(“/tmp/index”, false));

Query q = new TermQuery(new Term(“contents”, “lucene”));

Hits hits = is.search(q);

我们将在第3章中描述IndexSearcher的细节,在第5、6章有更多信息。

1.6.2 Term

Term是搜索的基本单元。与Field对象类似,它由一对字符串元素组成:字段的名称和字段的值。注意Term对象也和索引过程有关。但是它们是由 Lucene内部生成,所以在索引时你一般不必考虑它们。在搜索时,你可能创建Term对象并TermQuery同时使用。

Query q = new TermQuery(new Term(“contents”, “lucene”));

Hits hits = is.search(q);

这段代码使Lucene找出在contents字段中含有单词lucene的所有文档。因为TermQuery对象继承自它的抽象父类Query,你可以在等式的左边用Query类型。

1.6.3 Query

Lucene中包含一些Query的具体子类。到目前为止,在本章中我们仅提到过最基本的Lucene Query:TermQuery。其它Query 类型有BooleanQuery,PhraseQuery, PrefixQuery, PhrasePrefixQuery, RangeQuery,  FilteredQuery和SpanQuery。所有这些都在第3章描述。Query是最基本的抽象父类。它包含一些通用方法,其中最有趣的是 setBoost(float),在第3.5.9小节中描述。

1.6.4 TermQuery

TermQuery是Lucene支持的最基本的查询类型,并且它也是最原始的查询类型之一。它用来匹配含有指定值的字段的文档,这在前几段只已经看到。

1.6.5 Hits

Hits类是一个搜索结果(匹配给定查询的文档)文档队列指针的简单容器。基于性能考虑,Hits的实例并不从索引中加载所有匹配查询的所有文档,而是每次一小部分。第3章描述了其中的细节。

1.7 其它类似的搜索产品

在你选择Lucene做为你的IR库之前,你可能想看看相同领域中的其它方案。我们对你可能相考虑的其它方案做了研究,这个小节对我们的发现做了总结。我们将这些产品分成两大类:

n        信息搜索(IR, Information Retrieval)库

n        索引和搜索程序

第一组比较小;它由一些比Lucene小的全文索引和搜索库组成。你可以把这个组的产品嵌入到你的程序中,如前面的图1.5所示。

第二组,比较大的组由一些现成的索引的搜索软件组成。这个软件一般设计为针对某种特定的数据,如网页,不如第一组的软件灵活。然而,其中一些产品也提供了它们的底层API,所以有时你也可以把它们当做IR库。

1.7.1 IR库

在我们对本章的研究中,我们发现两个IR库—Egothor和Xapian—提供了差不多的特征

抱歉!评论已关闭.