`

lucene 遍历索引

阅读更多


使用Lucene的API遍历Lucene索引
一般使用Lucene的人都很少需要对索引进行遍历之类的操作,因为使用Lucene一般都不会对其索引文件产生太大兴趣,只注重将Lucene作为一个全文检索工具来使用而已,并不在意其内部实现和结构。但是很多学习Lucene的朋友都希望可以看见完整的Lucene索引内容,至少包含索引词、索引词出现的文档、索引词在文档中的位置(这里指的位置并不是词在原文中的位置,而是指其在Lucene对文档进行过滤后得到的新文档的位置)等信息。前几个月笔者就因为在实验室里的一个实验性的项目做了一些需要遍历Lucene索引的工作。

事实上,如果我们需要观察Lucene索引的内容,我们完全可以使用Luke,但是我们知道Luke所提供的信息并不是总能满足我们的需要,而且很多人都认为Luke的功能十分强大,但是实际上我们自己完全可以自己开发一个类似Luke的工具。只要你对Java界面编程比较熟悉的话(这通常是比较难的),那么仅仅需要知道一些本文即将阐述的几个Lucene API就可以了。

这里我们遍历索引的思路是,首先得到索引词,然后根据索引词得到关于这个索引词的相关信息(主要就是根据倒排文件的结构遍历)。第一步就是得到索引词的枚举器(enumeration),在Lucene里为我们提供了TermEnum类,该类位于org.apache.lucene.index包下,它的声明为

public abstract class TermEnum 
extends Object 
根据官方的API说明,该类是一个用于枚举索引词的抽象类。索引词枚举器总是按照Term.compareTo()进行排序。索引词枚举器中的任意一个词都比它之前的词要大。 
它只有一个无参构造方法。除了继承自Object类的方法外,它主要有以下几个方法:

abstract void
close()
          关闭枚举器,释放资源。

abstract int
docFreq()
          返回当前索引词的文档频率。

abstract boolean
next()
          枚举器向后移动一个位置。

boolean
skipTo(Term target)
          使枚举器向后移动,直到移动到某个大于等于(这里的比较概念是由Term.compareTo()定义的)target的词为止。

abstract Term
term()
          返回当前枚举器所枚举的词。

当我们看到TermEnum是一个抽象类的时候,我们也许会很无奈的想,我们必须要找到合适的并且已经继承了该类的非抽象类,然后还不得不对着它的文档再研读一番。你这么想是完全正确的,但是事实上我们完全没有必要这样做,因为Lucene的IndexReader类实际上为我们提供了一个很实用的方法

abstract TermEnum
terms()
          返回一个关于当前索引中所有索引词的一个枚举器。

您也许觉得我玩你,因为该方法也是一个抽象方法,因此IndexReader本身也是一个抽象方法!难道我们还需要找到一个继承该类的非抽象类么?当然不需要。我们有IndexSearcher类!而且令人振奋的是,该类终于不是抽象的啦!它含有一个我们神往的方法:

IndexReader
getIndexReader()
          返回该搜索对应的索引的索引阅读器。

但是您很可能又提出疑问了,IndexReader不是一个抽象类么,怎么能够返回一个抽象对象呢?是的,IndexReader的确是一个抽象方法,但是我们完全有理由相信该方法返回的实际上是一个继承自IndexReader的非抽象类。Lucene此处使用的是Java的多态,至于返回的到底是IndexReader的哪一个子类我们大可不必细究,交给JVM就好了。因此,我们就可以使用前面的所有的那些抽象方法(注意,当我们使用这些方法的时候,它们不再是抽象方法)了。

因此,得到一个索引的索引词就可以使用下面这段代码:

        IndexSearcher searcher = new IndexSearcher(IndexPath);
        IndexReader reader = searcher.getIndexReader();

        TermEnum enumeration = reader.terms();

        while(enumeration.next()){

//invoke the other methods in TermEnum

}

如果您是一个细心的读者,您可能会问到:enumeration.next()不是会枚举出下一个词么,那么上面那段代码不就会直接跳过第一个索引词么?是的,如果您这么想,那说明您考虑的很细致,但是我可以告诉您,上面的代码完全没有问题。因为一开始TermEnum枚举的并不是第一个索引词而是一个空对象,因此在我们使用TermEnum的其他方法之前应当首先调用next()方法。

现在我们能够得到所有的索引词了,那么怎么根据这些索引词得到其他信息(出现的文章、位置等)呢?事实上,原理完全和上面的方法差不多,只是使用的方法不同而已。

如果刚才我们仔细阅读Lucene关于IndexReader的API文档的话,那么我们可以发现一个方法:

TermPositions
termPositions(Term term)
          返回一个包含term的所有文档的枚举器。

现在我们就来看看TermPositions。我们可以发现,TermPositions并不是一个类,而是一个接口,而且该接口是继承自TermDocs接口的。现在我们暂且不看TermDocs,先来了解一下TermPositions接口,该接口的API说明文档是这样阐述的:

public interface TermPositions 
extends TermDocs
TermPositions 提供枚举一个词的<document, frequency, <position>* >三元组的接口 。

其中,document 和 frequency 的含义与 TermDocs中的相同。 而position部分则顺序列出了一个词在一个文档中的每一个出现位置。

该接口含有一个方法:

int
nextPosition()
          返回在当前文档中的下一个出现位置。

使用该方法我们就可以自如地遍历上面三元组的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);//根据指定的路径构造一个搜索器
        IndexReader reader = searcher.getIndexReader();//得到搜索器的索引阅读器
        
        TermEnum enumeration = reader.terms();//得到索引的索引词表

        while(enumeration.next())//遍历索引此表
        {
            if(enumeration.term().field().equals("content"))//我们仅处理所在域域名为content的索引词
            {
                //out是一个输出流,它输出到一个文本,这里没有给出out的定义,读者可以自己定义它
                out.write(enumeration.term().text() + "\n");

                TermPositions posEnum = reader.termPositions(new Term("content",enumeration.term().text()));
                StringBuffer sb = new StringBuffer(65536);
                while(posEnum.next())
                {
                     sb.append(reader.document(posEnum.doc()).getField("DOCNO").stringValue());//DOCNO是笔者所使用语料的文档的标号,对应一般使用者的"filename"域
                     sb.append(":");
                     sb.append(posEnum.freq());
                     sb.append(" ");
                     for( int i = 0; i < posEnum.freq(); i++)
                         sb.append("["+posEnum.nextPosition()+"]");
                     sb.append(";");
                 }
                 out.write(sb.toString()+"\n");

         }

out.close();

searcher.close();

这样,我们就完成了一个简单的索引遍历的操作。打印出的结果的一个局部视图如下:

modifyits
AP890915-0286 :1 [317]; AP890918-0217 :1 [368]; AP891215-0011 :1 [245];
modifyrecipes
AP890830-0142 :1 [332];
modifyself
AP890914-0048 :2 [83] [126];
modifythe
AP890814-0212 :1 [133]; AP890923-0115 :1 [58];

以"modifyself"来说,它出现在文档编号为AP890914-0048的文档中,在该文档中出现2次,位置分别是83和126。

当然,你可以使用更多的方法来打印出更多的信息。

好了,至此我们已经把基本的遍历Lucene索引的API及其使用介绍完了,你是不是觉得Luke实际上也没有很神秘呢?你完全有能力自己写一个Lucene索引查看器。

P.S. 本文完全是笔者自己从在使用经验中总结出来的,由于笔者自己也是刚接触Lucene,因此理解难免有偏颇之处,希望大家指正。同时笔者所使用的Lucene版本为2.0.0版,使用的API文档也是针对本版本的英文帮助(文中关于API的官方说明系笔者根据英文版翻译而来,若有错漏之处尽请指正)。

分享到:
评论

相关推荐

    最新Lucene教程

    索引包是整个系统核心,全文检索的根本就是为每个切出来的词建索引,查询时就只需要遍历索引,而不需要去正文中遍历,从而极大的提高检索效率。 5)org.apache.1ucene.queryParser查询分析器,实现查询关键词间的...

    Lucene 源码解析

    在创建完最重要的IndexWriter之后,就开始遍历需要索引的文件,构造对应的Document和Filed类,最终通过IndexWriter的addDocument函数开始索引。 Document的构造函数为空,StringField、TextField和Field的构造函数...

    Lucene入门源码

    Lucene源码,创建索引,搜索指定路径的txt文件,遍历文本,打印搜索结果

    解密搜索引擎技术实战-Lucene&java;精华版

    用简单的例子介绍了Lucene的最新应用方法,包括完整的搜索实现过程:从完成索引到搜索用户界面的实现。此外还进一步介绍了实现准实时搜索的方法,展示了Solr的用法以及实现分布式搜索服务集群的方法。最后介绍了在...

    1.解密搜索引擎技术实战:Lucene&Java;精华版(第3版)

    本书总结搜索引擎相关理论与实际解决方案,并给出了Java实现,其中利用了流行的开源项目Lucene和Solr,而且还包括...用简单的例子介绍了Lucene的应用方法,包括完整的搜索实现过程:从完成索引到搜索用户界面的实现。

    自己动手写搜索引擎(罗刚著).doc

    6.1.1 理解 Lucene 的索引库结构 146 6.1.2 设计一个简单的索引库 148 6.2 创建和维护索引库 149 6.2.1 创建索引库 149 6.2.2 向索引库中添加索引文档 149 6.2.3 删除索引库中的索引文档 151 6.2.4 更新索引库中的...

    解密搜索引擎技术实战Java精华版

    爬虫部分介绍了网页遍历方法和如何实现增量抓取,并介绍了从网页等各种格式的文档中提取主要内容的方法。自然语言处理部分从统计机器学习的原理出发,包括了中文分词与词性标注的理论与实现以及在搜索引擎中的实用等...

    网络爬虫调研报告.docx

    而Lucene只是个搜索引擎工具,它提供API接口,通过编写程序对信息进行索引和检索,在其后台需要网络爬虫程序的支持,其目的是通过网络爬虫软件抓取网页,作为提供给Lucene搜索引擎的资源,进行索引和查询。...

    ELK学习资料

    1.支持lucene 6.x:索引性能提升 提升25% 磁盘占用少了 2.新增sliced scroll类型:并发遍历 新增切片类型,进行并发的遍历 3.新增profile api:查询优化 4、新增reindex:对数据进行重建 进行异步重建提高索引的性能...

    网络爬虫调研报告.doc

    它遍历 Web空间 ,不断从一个站点移动到另一个站点 ,自动建立索引 ,并加入到网页数据库中。网络爬虫进入某个超级文本时 ,它利用 HTML语言的标记结构来搜索信息及获取指向其他超级文本的 URL地址 ,可以完全不依赖用户...

    网络爬虫调研报告(1).doc

    它遍历 Web空间 ,不断从一个站点移动到另一个站点 ,自动建立索引 ,并加入到网页数据库中。网络爬虫进入某个超级文本时 ,它利用 HTML语言的标记结构来搜索信息及获取指向其他超级文本的 URL地址 ,可以完全不依赖用户...

    网络爬虫调研报告(2).doc

    它遍历 Web空间 ,不断从一个站点移动到另一个站点 ,自动建立索引 ,并加入到网页数据库中。网络爬虫进入某个超级文本时 ,它利用 HTML语言的标记结构来搜索信息及获取指向其他超级文本的 URL地址 ,可以完全不依赖...

    JAVA上百实例源码以及开源项目

    递归遍历矩阵 1个目标文件,简单! 多人聊天室 3个目标文件 第一步:运行ServerData.java 启动服务器,然后服务器处于等待状态 第二步:运行LoginData.java 启动(客户端)登陆界面 输入用户名 ip为本机localhost 第...

    JAVA上百实例源码以及开源项目源代码

    递归遍历矩阵 1个目标文件,简单! 多人聊天室 3个目标文件 第一步:运行ServerData.java 启动服务器,然后服务器处于等待状态 第二步:运行LoginData.java 启动(客户端)登陆界面 输入用户名 ip为本机localhost 第...

    ZendFramework中文文档

    10.7.3. 遍历结果集 10.8. Zend_Db_Table Relationships 10.8.1. Introduction 10.8.2. Defining Relationships 10.8.3. Fetching a Dependent Rowset 10.8.4. Fetching a Parent Row 10.8.5. Fetching a ...

Global site tag (gtag.js) - Google Analytics