搜档网
当前位置:搜档网 › JAVA_Lucene_in_Action教程完整版

JAVA_Lucene_in_Action教程完整版

JAVA_Lucene_in_Action教程完整版
JAVA_Lucene_in_Action教程完整版

Lucene in Action(简体中文版)

共10部分

第一部分 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的责任不仅来自于简单的编译打包的构建过程,也来自于定制的任务,,我们在构建过程中使用Lucene创建索引文件。Ant任务现在生存在Lucene的Sandbox(沙箱)中,将在本书8.4节描述。

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

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

来自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提供的服务很像今天的https://www.sodocs.net/doc/ed6668686.html,。

尽管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的网站https://www.sodocs.net/doc/ed6668686.html,/hatcher2上下载。代码的使用说明包含在代码包的README文件。

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

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

为什么是JUnit?

我们相信书中的代码示例应该都是高质量的。典型的“hello world”例子经常帮助读者测试他们的环境。我们使用独特的方法来使用书中的代码示例。大部分示例是实际的JUnit测试用例

(https://www.sodocs.net/doc/ed6668686.html,)。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 expecte d 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 本书中用到的示例数据

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

代码约定和下载

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

第一部分

Lucene核心

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

第一章接触Lucene

本章包括

理解Lucene

使用基本的索引API

使用搜索API

考虑可替换产品

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”页

(https://www.sodocs.net/doc/ed6668686.html,/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的发布历史

注意Lucene的创建者,Doug Cutting,在信息搜索领域有很强的理论和实践经验。他发表过许多IR 主题相关的研究论文并曾在Excite、Apple和Grand Central等公司工作。最近,考虑到web搜索引擎数目的减少和这个领域的潜在垄断,他创建了Nutch,第一个开源的万维网搜索引擎(https://www.sodocs.net/doc/ed6668686.html,),它用来处理抓取、索引和搜索数十亿时常更新的网页。毫不奇怪,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 https://www.sodocs.net/doc/ed6668686.html, article

*/

public class Indexer {

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

if (args.length != 2) {

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

+ “ ”);

}

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 Fi leReader(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 搜索索引的查询

相关主题