`

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技术的增量索引

    - **首次创建索引**:首先,我们需要遍历整个数据源,创建每个文档的实例,然后将这些文档添加到Lucene的索引writer中。完成这一步后,就会生成一个完整的初始索引。 - **监控数据变更**:为了实现增量索引,我们...

    基于lucene的索引与搜索

    本文档主要探讨了基于Apache Lucene的索引与搜索技术,这是构建高效全文搜索引擎的关键组成部分。Lucene是一个开源的Java库,提供了强大的文本分析、索引和搜索功能,被广泛应用于各种信息检索系统中。 第一章 引言...

    lucene实现索引查询

    以下是关于使用Lucene实现索引查询的详细知识: ### 一、创建索引 创建索引是Lucene的核心过程,它涉及到以下步骤: 1. **定义索引目录**:首先,你需要指定一个目录来存储索引文件。这通常是一个文件夹,可以...

    lucene-索引(index).pdf

    Lucene索引的构建和查询都是基于反向索引进行的,这意味着当用户输入查询词时,Lucene能够迅速找到包含这些词的所有文档,而不必遍历整个文档集。 #### 索引文件格式 Lucene索引文件格式是其内部设计的关键部分,...

    深入 Lucene 索引机制

    2. 索引匹配:Lucene通过遍历倒排索引,找到所有匹配查询的Term,并结合Posting List找出包含这些Term的文档。 3. 排序和评分:Lucene使用TF-IDF(词频-逆文档频率)和其他因素(如文档长度、查询词位置等)计算每...

    lucene全文检索简单索引和搜索实例

    《Lucene全文检索:简单索引与搜索实例详解》 Lucene是Apache软件基金会的开源项目,是一款强大的全文检索库,被广泛应用于Java开发中,为开发者提供了构建高性能搜索引擎的能力。在本文中,我们将深入探讨如何基于...

    Lucene3.0创建索引

    ### Lucene3.0创建索引 在Lucene3.0中创建索引是一个关键功能,可以帮助用户快速地检索和管理大量的文本数据。本篇文章将详细介绍如何使用Lucene3.0来创建索引,并通过一个具体的例子来演示整个过程。 #### 一、...

    lucene索引查看工具及源码

    - **倒排索引结构**:分析 Luke 如何遍历和展示倒排索引,包括 postings 数据和 term frequencies。 总的来说,Luke 不仅是一个实用的 Lucene 索引查看工具,还是学习 Lucene 内部原理的宝贵资料。通过研究 Luke 的...

    Lucene索引和查询

    在本项目中,代码会遍历指定的文件夹,读取每个文件的内容,然后进行上述的索引创建操作。由于涉及文件I/O,所以可能需要考虑性能优化和错误处理。 **2. 查询过程** 查询阶段,用户输入查询字符串,Lucene将这个...

    最简单的Lucene建立、搜索索引的方法

    2. **创建索引目录**:Lucene的索引存储在文件系统中的一个目录下,所以你需要先创建一个文件夹作为索引的存储位置。 3. **配置Analyzer**:Analyzer是Lucene中用于分词的组件,可以根据需求选择不同的Analyzer。...

    lucene 对 xml建立索引

    ### Lucene对XML文档建立索引的技术解析与实践 #### 一、引言 随着互联网技术的迅猛发展,非结构化数据(如XML文档)在企业和组织中的应用日益广泛。如何高效地处理这些非结构化的数据,特别是进行快速检索成为了一...

    lucene索引简单介绍

    8. **查询执行(Query Execution)**:Lucene使用查询树来优化搜索性能,通过遍历倒排索引,找到匹配文档,并根据评分计算结果排序。 在实际应用中,我们可以通过Java API与Lucene进行交互,创建索引、搜索、更新和...

    Lucene 索引、删除、检索 实例

    **Lucene 索引、删除、检索 实例** Lucene 是一个高性能、全文本搜索库,由Apache软件基金会开发。它提供了一个简单的API,使得开发者可以方便地在应用程序中集成高级的搜索功能。本实例将详细介绍如何使用Lucene...

    用LUCENE连击MYSQL建立索引并搜索的JAVA代码。

    在这个场景中,我们讨论的是如何结合Lucene和MySQL来实现一个Java应用程序,该程序能够从MySQL数据库中提取数据,创建索引,并进行高效的搜索。 首先,我们需要理解Lucene的工作原理。Lucene通过分析文本,将文档...

    Lucene对本地文件多目录创建索引

    标题中的“Lucene对本地文件多目录创建索引”指的是使用Apache Lucene库来构建一个搜索引擎,该搜索引擎能够索引本地计算机上的多个文件目录。Lucene是一个强大的全文搜索库,它允许开发者在Java应用程序中实现高级...

Global site tag (gtag.js) - Google Analytics