第一部分 接触 Lucene
Lucene的核心
本书的第一部分(first half of)覆盖了Lucene的对外接口(covers out-of-the-box Lucene)。你将接触到Lucene(Meet Lucene)的一个全面的概况(general overview),并且还开发一个完整的索引建立和检索的程序。每一个连续的章节(Each successive chapter)系统地(systematically )深入研究(delves into)特定的领域(specific areas)。索引“Indexing”数据和文档以及随后搜索“Searching”它们是使用Lucene的第一步。回到一个覆盖索引的进程(Returning to a glossed-over indexing process),分析器“Analysis”将替代( fill in)你对影响Lucene索引后的文本的事物的理解。搜索“Searching”是Lucene真正擅长(really shines)的地方。本部分结束的(concludes with)章节是介绍仅使用内置的特性探讨高级搜索“Advanced searching”技术,而扩展搜索“Extending search”展示(showcasing)Lucene对自定义的目标“custom purposes”的扩展性(extensibility)。
接触 Lucene
This chapter covers 本章节包括
■ Understanding Lucene 理解 Lucene
■ Using the basic indexing API 使用基本的索引API
■ Working with the search API 在工作中使用搜索API
■ Considering alternative products 考虑其他可选的产品
Lucene的流行和成功背后的关键因素(key factors)的其中一个就是它的简单(simplicity)。它的索引和搜索API的细致暴露(careful exposure)出一个良好设计软件的标志(a sign of the well- designed software)。因此(Consequently),你为了开始使用它不需要深入了解(in-depth knowledge about)Lucene的信息索引和获取(retrieval)是怎样工作的。而且(Moreover),Lucene的简单易懂(straightforward)的API只需你学习使用它的一小部分(a handful of)类。
在本章中,我们通过可以使用的(ready-to-use)代码示例向你怎样使用Lucene来执行基本的索引和搜索,然后我们简单地介绍(briefly introduce)所有这些这两节步骤中(for both of these processes)你需要了解的核心元素。我们同样也提供关于Java/non-Java,free和商业产品竞争的简单回顾(brief reviews)。
1.1 信息组织和访问的演化information organization and access
(略)
1.2 理解 Lucene
不同的人使用不同的方法(different approaches)来解决相同的问题,即信息超负荷问题(information overload)。一些人使用新奇的(novel)用户接口来工作,一些使用智能的代理(intelligent agents),另一些开发成熟的(sophisticated)搜索工具如Lucene。在本节稍后我们展示(jump into action)代码示例之前,我们提供你一张高层次(high-level)的图,说明哪些是Lucene的东西,哪些不是,以及Lucene未来的样子。
1.2.1 Lucene 是什么
Lucene 是一个高性能(high performance)的可伸缩的(scalable)信息检索库(Information Retrieval (IR) library),它可以让你给你的应用程序添加索引和搜索能力。Lucene是一个成熟的(mature)免费的open-source 项目,使用Java实现,它是广泛流行的Apache Jakarta项目大家庭中的其中一个成员,并且许可License是基于自由主义(liberal)的Apache Software License基础之上的。同样的(As such),Lucene现在在很短的几年内已经成为一个最流行的免费的Java IR library。
NOTE 贯穿本书中,我们将使用term Information Retrieval (IR)来描述像Lucene这样的搜索工具。人们常常将IR libraries引用为搜索引擎,但是你却不应该搞混IR libraries和web search engines。
正如你马上就会发现的一样,Lucene提供一个简单的而且依然强大的核心API,而且仅需要最小限度地学习全文索引和检索(full-text indexing and searching),你仅需要学习一把(a handful of)类就能开始把Lucene集成进一个应用程序。因为Lucene是一个Java库,它不承担(make assumptions about)它索引和检索的是什么,这使得它比其它一些搜索应用程序更有优势(an advantage over)。对Lucene陌生(new to)的人经常错把它当作一个可以马上使用(ready-to-use)的应用程序,就像一个文件检索(file-search)程序,或者一个web网络爬虫(crawler),或者一个web站点搜索引擎。这些都不是Lucene的实质:Lucene实际是一个软件库(software library),一个开发工具包(toolkit)如果你愿意这样称呼(if you will),而不是一个具备完整特性的(full-featured)搜索应用程序。这使它关注自己的文本索引和搜索技术(It concerns itself with text indexing and searching),并且这些事它完成得非常好。Lucene使得你的应用程序处理业务规则(business rules),特别地针对它的问题领域(problem domain),而把复杂的索引和搜索实现掩盖起来,只提供简单易用(simple-to-use)的API。你可以把Lucene当作一层(layer),应用程序位于它之上(sit on top of),就像图1.5所描述(depicted)的那样。一些拥有完整特性的搜索程序被建立在Lucene上层。如果你寻找一些与之相关的预创建的东西(something prebuilt)或者一个为了抓取(crawling),处理(document handling)和搜索文档,请参考(consult) Lucene Wiki “powered by” 网页(http://wiki.apache.org/jakarta-lucene/PoweredBy)更多选项如下: Zilverline, SearchBlox, Nutch, LARM, and jSearch, 还有其它一小部分的命名(to name a few)。个案研究(Case studies of)包括Nutch 和SearchBlox 将在第10章介绍。
1.2.2 Lucene 能为你做什么
Lucene 允许给你的程序添加索引和搜索的能力。(这些功能将在1.3节里描述),Lucene能够索引并且可以使得任何能够被转换成文本格式的(textual format)数据能够被搜索(search- able),请参考图1.5。
Figure 1.5 A typical application integration with Lucene一个集成了Lucene的典型程序
Lucene并不关心数据源,及其格式,甚至其语言,只要你能够把它转换成文本就行。这意味着你能够使用Lucene来索引和搜索存储在文件中的数据,以及在远端web服务器的web网页,还有存储在本地文件系统中文档,简单的文本文件, Microsoft Word 文档,或者PDF文档,或者任何其它你能分离出(extract)文本信息的格式都行。简单地,通过Lucene的帮助你能够索引存储在你的数据库中的数据,可以给你的用户提供全文检索能力(full-text search capabilities),而这很多数据库都不提供。一旦你集成了Lucene,使用你的程序的用户就能够让搜索这样一些查询词:+George +Rice -eat –pudding, Apple –pie +Tiger, animal:monkey 或者food:banana,等等。.使用Lucene,你能够索引和搜索email邮件,邮件列表档案,即时通信聊天信息,你的Wiki网页,等等继续。
1.2.3 Lucene 的历史
Lucene 最先是由Doug Cutting开发的,它最初提供下载是在它在SourceForge网站的主页上。它后来加入Apache软件基金的高质量的开源Java产品的Jakarta家族是在2001年九月份。从那之后的每一次发布,项目得到可喜明显易见的增强,吸引更多的用户和开发者加入。在2004年七月份,发布了 Lucene version 1.4,后来修正了一个bug在10月份早期发布了1.4.2 版本。
Doug Cutting 在Lucene幕后依然保持主要的影响力,但是自从Lucene移动到Apache Jakarta的庇护(umbrella)之下后,更多聪明的智慧力量加入到这个项目中来。在本书写作的时候,Lucene的核心团队包括了半打的活跃开发者(a dozen active developers),其中两个还是本书的作者。除了这些官方的开发者之外,Lucene还有一个非常庞大(a fairly large)而且活跃的技术用户社区,非常频繁地贡献(frequently contributes)补丁,BUG修复,以及新的特性等。
1.2.4 谁在使用 Lucene
谁在使用呢?除了Lucene的Wiki网站提及(mentioned)的团体(organizations)之外,许多(a number of)其他的大型的著名的(well-known)跨国(multinational)组织也在使用Lucene。它为Eclipse IDE提供搜索能力,大不列颠百科全书(Encyclopedia Britannica)CD-ROM/DVD,FedEx 还有Mayo Clinic,惠普Hewlett-Packard,New Scientist magazine, Epiphany,MIT’s OpenCourseware 以及DSpace, Akamai’s EdgeComputing platform等等,当然,你的名字也将很快加入此列表中。
1.2.5 Lucene 其它语言版本:Perl, Python, C++, .NET, Ruby
有一条判断一个开源软件是否成功的途径是通过考察它被改成其它编程语言的数量。使用这个标准,Lucene是非常成功的,尽管原始的Lucene是用Java写的,在本书写作的时候,Lucene已经被有很多其它语言版本了: Perl,Python,C++ 和 .NET,以及一些基础(groundwork)的工作在转换到Ruby语言的工作中完成了。这是及其令人兴奋的新闻,对那些需要访问用不同语言写成的应用程序的Lucene索引时的开发者来说确实如此。你在第9章中能够学到更多有关这方面的东西。
1.3 索引和搜索Indexing and searching
所有搜索引擎的心就是索引的概念(concept):处理那些原始的数据转换成一个非常有效率(highly efficient)的交叉引用的(cross-reference)查找(lookup)为了便于(facilitate)加快(rapid)搜索。让我们做一个快速的从很高层面来观察(quick high-level look at)索引和搜索的处理。
1.3.1 索引是什么,为什么它很重要?
假设(Suppose)你需要搜索一个巨大数量的文件,而且你想能够找出那些包含了某一些词或者短语(a certain word or a phrase)的文件,你会怎样写一个程序完成这样的事呢?一个幼稚的方法(naive approach)也许是持续地在每一个文件中扫描(sequentially scan)给定的词汇或者短语。这个方法有很多缺点(a number of flaws),太巨大了。这就是索引进来的地方:为了快速地搜索很庞大数量的文本,你必须先索引这些文本,并转换成一个会让你搜索更快速的格式,消除(eliminating)那些缓慢的按顺序的扫描过程(the slow sequential scanning process)。这个转换过程就叫索引(indexing),它的输出被叫做一条索引(an index)。
你可以把一条索引当作一个数据结构(data structure),它允许快速的随机访问(fast random access)那些存储在里面的词汇。在它之后的的概念(concept behind it)与一本书后的索引类似(is analogous to),这可以让你很快速地定位(locate)讨论某些确定主题的(discuss certain topics)的页。而在Lucene之中(In the case of Lucene),一条索引是一个特别设计的数据结构(a specially designed data structure),典型的情况是(typically)存储在文件系统之中作为一系列文件(a set of index files)。我们在附录B(appendix B)中详细地(in detail in)覆盖了(cover)索引文件的结构,但是目前暂时把一条Lucene索引当作一个允许快速检索词汇的工具。
1.3.2 什么是搜索呢 What is searching?
搜索(Searching)就是查找一条索引里的单词来找出它们出现的文档的这样一个过程。一个搜索的质量是用精度(precision)和回调法(recall metrics)来典型地描述(typically described)的。回调(Recall)测量(measures)搜索系统查找相关的(relevant)文档是否好,然而(whereas)精度(precision)测量系统过滤出(filters out)不相关(irrelevant)文档是否好,然而你在思考搜索的时候必须考虑许多(a number of)其他因素(factors)。我们已经提及(mentioned)速度和快速搜索大量(large quantities of)文本的能力。支持(Support for)单个的(Single)和多个词汇的查询(multiterm queries),短语查询(phrase queries),通配符(wildcards),结果分级(result ranking),以及排序(sorting)功能,也同样重要,照现在的样子(as is)输入这些查询的(entering those queries)一个友好的语法(friendly syntax)。Lucene的强大的软件库提供许多查询特性,(bells)和(whistles)—很多我们都不得不展开(spread)我们的查询覆盖(coverage over)三章(第3章和5章和6章)。
1.4 Lucene in action: 一个简单的程序
让我们看看Lucene in action,为了写这个程序,回想(recall)索引和搜索文件的问题,这个问题在 1.3.1 节描述过,此外(furthermore)假设(suppose)你需要索引和搜索存储在一个目录树(directory tree)中的文件,而不是仅仅查找单一的一个目录。为乐向你展示Lucene的索引和搜索能力,我们将使用一对命令行(command-line)的应用程序:Indexer和Searcher。首先我们将给一个包含文本文件的目录树建立索引,然后我们搜索这个创建的索引。
这些示例程序将让你熟悉(familiarize)Lucene的API,它的轻松的使用(its ease of use)以及它的强大。这些代码清单(code listings)是完整的,可以使用的(ready-to-use)命令行程序。如果文件的索引/检索是你需要去解决的问题,你可以复制这些代码清单,并且揉拧(tweak)它们来适合(suit)你所需要的地方。在接下来的章节中,我们将描述Lucene在许多更高级使用的细节(much greater detail)的每一个方面(each aspect)。
在我们能够使用Lucene来搜索之前,我们需要先创建一个索引,所以我们以一个Indexer应用程序作为开始。
1.4.1 创建一个索引
在本节中,你将看到一个单一的类叫作Indexer,它的四个静态方法(static methods)合起来(together),它们递归地(recursively)来回遍历(traverse)文件系统的目录,并给所有扩展名为.txt的文件建上索引。当Indexer执行完成后(completes execution),它会为它的同胞:Searcher工具(将在1.4.2节中介绍(presented))产生(leaves behind)一个Lucene索引。
我们不期望你熟悉(be familiar with)在这个例子中使用的这些少数的Lucene类和方法,我们会简单地介绍它们(explain them shortly)。在这些有注释的代码清单(the annotated code listing)后,我们向你展示怎样使用Indexer,如果它能在你看到它是怎么编码的之前帮助你学习Indexer是怎样使用的,请直接跳到(go directly to)在这些代码后的使用讨论(usage discussion)部分。
Using Indexer to index text files
Listing 1.1 shows the Indexer command-line program. It takes two arguments:
■ A path to a directory where we store the Lucene index
■ A path to a directory that contains the files we want to index
Listing 1.1 Indexer: traverses a file system and indexes .txt files
/** *//**
* 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 Date().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, 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[i];
if (f.isDirectory()) {
indexDirectory(writer, f);
} else if (f.getName().endsWith(".txt")) {
indexFile(writer, f);
}
}
}
// method to actually index a 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.getCanonicalPath()));
writer.addDocument(doc);
}
}
译者 Naven 审校 Scar 未完待续