lucene 3.0施用教程.doc & JAVA Lucene in Action教程完整版三(简体中文版)

lucene 3.0使用教程.doc & JAVA Lucene in Action教程完整版三(简体中文版)
JAVA Lucene in Action教程完整版三(简体中文版)

使用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()                + “ ”);         }        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 –lFound 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        IndexWritern        Directoryn        Analyzern        Documentn        Field接下来是对这些类的一个简短的浏览,针对它们在Lucene的角色,给出你粗略的概念。我们将在整本书中使用这些类。1.5.1 IndexWriterIndexWriter是在索引过程中的中心组件。这个类创建一个新的索引并且添加文档到一个已有的索引中。你可以把IndexWriter想象成让你可以对索引进行写操作的对象,但是不能让你读取或搜索。不管它的名字,IndexWriter不是唯一的用来修改索引的类,2.2小节描述了如何使用Lucene API来修改索引。1.5.2 DirectoryDirectory类代表一个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)  ? ? Telephone and Social Security numbers, URLs, personal names, Dates
Field.UnIndexed(String,String)   ? Document type (PDF, HTML, and so on), if not used as search criteria
Field.UnStored(String,String) ? ?  Document titles and content
Field.Text(String,String) ? ? ? Document titles and content
Field.Text(String,Reader) ? ?  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        IndexSearchern        Termn        Queryn        TermQueryn        Hits接下来的部分对这些类提供一个简要的介绍。我们将在深入更高级主题之前,在接下来的章节中展开这些解释。1.6.1 IndexSearcherIndexSearcher用来搜索而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 TermTerm是搜索的基本单元。与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 QueryLucene中包含一些Query的具体子类。到目前为止,在本章中我们仅提到过最基本的Lucene Query:TermQuery。其它Query类型有BooleanQuery,PhraseQuery, PrefixQuery, PhrasePrefixQuery, RangeQuery, FilteredQuery和SpanQuery。所有这些都在第3章描述。Query是最基本的抽象父类。它包含一些通用方法,其中最有趣的是 setBoost(float),在第3.5.9小节中描述。1.6.4 TermQueryTermQuery是Lucene支持的最基本的查询类型,并且它也是最原始的查询类型之一。它用来匹配含有指定值的字段的文档,这在前几段只已经看到。1.6.5 HitsHits类是一个搜索结果(匹配给定查询的文档)文档队列指针的简单容器。基于性能考虑,Hits的实例并不从索引中加载所有匹配查询的所有文档,而是每次一小部分。第3章描述了其中的细节。1.7 其它类似的搜索产品在你选择Lucene做为你的IR库之前,你可能想看看相同领域中的其它方案。我们对你可能相考虑的其它方案做了研究,这个小节对我们的发现做了总结。我们将这些产品分成两大类:n        信息搜索(IR, Information Retrieval)库n        索引和搜索程序第一组比较小;它由一些比Lucene小的全文索引和搜索库组成。你可以把这个组的产品嵌入到你的程序中,如前面的图1.5所示。第二组,比较大的组由一些现成的索引的搜索软件组成。这个软件一般设计为针对某种特定的数据,如网页,不如第一组的软件灵活。然而,其中一些产品也提供了它们的底层API,所以有时你也可以把它们当做IR库。1.7.1 IR库在我们对本章的研究中,我们发现两个IR库—Egothor和Xapian—提供了差不多的特征集合并且基本上都是辅助开发者的。我们也发现了MG4J,它并不是一个IR库而是一套创建IR库的有用工具;我们认为使用IR的开发者应该了解它。这里是我们对这三种产品的评论。Egothor一个全文索引和搜索的Java库,Egothor的核心算法与Lucene类似。它已经存在了很多年并拥有少量积极的开发者和用户团体。领头人是捷克工程师Leo Galambos,一个在IR领域有深厚理论背景的博士研究生。他时常参与Lucene用户和开发者邮件列表的讨论。Egothor提供一个扩展的Boolean模块,使得它起到纯Boolean模块和Vector模块的作用。你可以通过一个查询时参数来选择使用哪个模块。这个软件有大量不同的查询类型,支持类似的查询语法,并允许多线程查询,如果你工作在多CPU计算机或搜索远程索引时是相当简单的。Egothor多以现成的程序如网络爬行器… …1.7.2 索引和搜索程序另一组可用的软件,包括免费的和商业的,包装成打包好的产品。这些软件通常不暴露大量的API且不让你基于它构建定制的软件。其中大部分提供了一种机制使你控制有限的参数集合,但却不能以预期的方法来使用这个软件。(当然,也有些特殊情况。)这样,我们不能把这种软件直接和Lucene相比。然而,其中一些产品可能对你的需求来说是足够的,并能让你的工作运转起来,尽量Lucene或其它的IR库以长期角度来说是个不错的选择。这里是此类产品中比较流行的几种:n        SWISH,SWISH-E和SWISH++ -- http://homepage.mac.com/pauljlucas/software/swish/, http://swish-e.org/n        Glimpse和Webglimpse—http://webglimpse.net/n        Namazu—http://www.namazu.org/n        ht://Dig—http://www.htdig.org/n        Harvest和Harvest-NG—http://www.sourceforge.net/projects/harvest/,http://
webharvest.sourceforge.net/ng/n        Microsoft Index Server—http://www.microsoft.com/NTServer/techresources/webserv/
IndxServ.aspn        Verity—http://www.verity.com/1.7.3 在线资源上一小节只是对相关产品的总的看法。很多资源可以帮你找到的其它的IR库和产品:n        DMOZ—在DMOZ Open Directory Project(ODP)中,你将发现http://dmoz.org/Computers/
Software/Information_Retrieval/和它的子版块有大量的信息。n        Google—尽管Google Directory是基于Open Directory的数据,这两个目录确实不一样。所以你也应该访问http://directory.google.com/Top/Computers/Software/
Information_Retrival/。n        Searchtools—有个搜索工具的专门的网站http://www.searchtools.com/。这个网站并不时常更新,但是它已经很多年了并且相当广泛。软件根据操作系统、编程语言、许可证等等进行分类。如果你仅仅对用Java写的搜索软件感兴趣,访问http://www.searchtools.com/toos
/tools-java.html。我们提供了一些Lucene的替代品的正面评论,但是我们确信你的工作会让你觉得Lucene才是最好的选择。1.8 总结

在本章中,你获得了Lucene的一些基本知识。现在你知道了Lucene是一个IR库而不是现成的产品,当然也不是Lucene的初识者常常认为的web爬行器。你也了解了Lucene是如何产生的以及在Lucene背后的关键人物和组织。根据Manning’s in Action的思想,我们先向你展示了两个独立的程序,Indexer和Searcher,它们可以对存储于文件系统的文本文件进行索引和搜索。然后我们主要描述了在那两个程序中用到每个Lucene类。最后贡献出我们对类似于Lucene的一些产品的研究。搜索无处不在,在你阅读本书时也可能发生,你对你程序中不可或缺的搜索感兴趣。基于你的需求,集成Lucene可能是微不足道的,或者它可能会包含在架构层的设计中。我们像本章一样组织了后面两章的内容。首先我们需要做的是索引一些文档;在第2章详细讨论这个过程。