事实上,如果我们需要观察Lucene索引的内容,我们完全可以使用Luke,但是我们知道Luke所提供的信息并不是总能满足我们的需要,而且很多人都认为Luke的功能十分强大,但是实际上我们自己完全可以自己开发一个类似Luke的工具。只要你对Java界面编程比较熟悉的话(这通常是比较难的),那么仅仅需要知道一些本文即将阐述的几个Lucene API就可以了。 这里我们遍历索引的思路是,首先得到索引词,然后根据索引词得到关于这个索引词的相关信息(主要就是根据倒排文件的结构遍历)。第一步就是得到索引词的枚举器(enumeration),在Lucene里为我们提供了TermEnum类,该类位于org.apache.lucene.index包下,它的声明为 public abstract class TermEnum abstract void abstract int abstract boolean boolean abstract Term 当我们看到TermEnum是一个抽象类的时候,我们也许会很无奈的想,我们必须要找到合适的并且已经继承了该类的非抽象类,然后还不得不对着它的文档再研读一番。你这么想是完全正确的,但是事实上我们完全没有必要这样做,因为Lucene的IndexReader类实际上为我们提供了一个很实用的方法 abstract TermEnum 您也许觉得我玩你,因为该方法也是一个抽象方法,因此IndexReader本身也是一个抽象方法!难道我们还需要找到一个继承该类的非抽象类么?当然不需要。我们有IndexSearcher类!而且令人振奋的是,该类终于不是抽象的啦!它含有一个我们神往的方法: IndexReader 但是您很可能又提出疑问了,IndexReader不是一个抽象类么,怎么能够返回一个抽象对象呢?是的,IndexReader的确是一个抽象方法,但是我们完全有理由相信该方法返回的实际上是一个继承自IndexReader的非抽象类。Lucene此处使用的是Java的多态,至于返回的到底是IndexReader的哪一个子类我们大可不必细究,交给JVM就好了。因此,我们就可以使用前面的所有的那些抽象方法(注意,当我们使用这些方法的时候,它们不再是抽象方法)了。 因此,得到一个索引的索引词就可以使用下面这段代码: IndexSearcher searcher = new IndexSearcher(IndexPath); TermEnum enumeration = reader.terms(); while(enumeration.next()){ //invoke the other methods in TermEnum } 如果您是一个细心的读者,您可能会问到:enumeration.next()不是会枚举出下一个词么,那么上面那段代码不就会直接跳过第一个索引词么?是的,如果您这么想,那说明您考虑的很细致,但是我可以告诉您,上面的代码完全没有问题。因为一开始TermEnum枚举的并不是第一个索引词而是一个空对象,因此在我们使用TermEnum的其他方法之前应当首先调用next()方法。 现在我们能够得到所有的索引词了,那么怎么根据这些索引词得到其他信息(出现的文章、位置等)呢?事实上,原理完全和上面的方法差不多,只是使用的方法不同而已。 如果刚才我们仔细阅读Lucene关于IndexReader的API文档的话,那么我们可以发现一个方法: TermPositions 现在我们就来看看TermPositions。我们可以发现,TermPositions并不是一个类,而是一个接口,而且该接口是继承自TermDocs接口的。现在我们暂且不看TermDocs,先来了解一下TermPositions接口,该接口的API说明文档是这样阐述的: public interface TermPositions 其中,document 和 frequency 的含义与 TermDocs中的相同。 而position部分则顺序列出了一个词在一个文档中的每一个出现位置。 该接口含有一个方法: int 使用该方法我们就可以自如地遍历上面三元组的position部分了,也就是说我们可以得到一个词在一个文档中的所有出现位置了! 但是您可能觉得这点信息实在是少得可怜。别着急,前面说过TermPositions接口是继承自TermDocs接口的(真是惊讶于Lucene的体系架构,你完全可以把Lucene的设计作为一个设计模式的范例去学习),那么TermDocs接口应该为我们设计了更多的实用方法。事实确实如此! 我们完全没有必要去全面的了解TermDocs接口,我们现在所需要知道的就是TermPositions接口究竟从TermDocs接口继承了哪些方法。从TermPositions的API文档处就可以轻易地发现它继承了如下方法:close, doc, freq, next, read, seek, seek, skipTo。这些方法几乎都是自解释的,这里就不再赘述每一种方法了,感兴趣的读者可以自行参阅Lucene的API说明文档。有了这些方法,我们就可以完成我们对Lucene索引文件的遍历了。这里我需要强调一下,虽然我们没有实现任何实现了上面接口的类,但是我们在调用reader.termPositions(Term term)方法时实际上Lucene给我们返回了一个实现了TermPositions接口的类的实例(如果您对这点仍然不甚了然的话,请您再去翻翻您的Java教程)。 利用下面这段代码,我们可以对于一个给定的Lucene索引打印出<term, document, frequency, <position>* >四元组。 IndexSearcher searcher = new IndexSearcher(IndexPath);//根据指定的路径构造一个搜索器 while(enumeration.next())//遍历索引此表 TermPositions posEnum = reader.termPositions(new Term("content",enumeration.term().text())); } out.close(); searcher.close(); 这样,我们就完成了一个简单的索引遍历的操作。打印出的结果的一个局部视图如下: modifyits 以"modifyself"来说,它出现在文档编号为AP890914-0048的文档中,在该文档中出现2次,位置分别是83和126。 当然,你可以使用更多的方法来打印出更多的信息。 好了,至此我们已经把基本的遍历Lucene索引的API及其使用介绍完了,你是不是觉得Luke实际上也没有很神秘呢?你完全有能力自己写一个Lucene索引查看器。 P.S. 本文完全是笔者自己从在使用经验中总结出来的,由于笔者自己也是刚接触Lucene,因此理解难免有偏颇之处,希望大家指正。同时笔者所使用的Lucene版本为2.0.0版,使用的API文档也是针对本版本的英文帮助(文中关于API的官方说明系笔者根据英文版翻译而来,若有错漏之处尽请指正)。 |