自己动手写分布式搜索引擎
上QQ阅读APP看书,第一时间看更新

3.2.9 索引文件格式

在Lucene中,倒排索引结构存储在二进制格式的多个索引文件中,其中以tis为后缀的文件中包含单词信息;frq后缀的文件记录单词的文档编号和这个单词在文档中出现了多少次,也就是频率信息;prx后缀的文件包含单词出现的位置信息。

Lucene把和一个索引相关的文件全部放在一个目录下。其中既存储描述索引结构的元数据,又包含索引数据。每类信息放在不同后缀的二进制文件中。元数据包含以fnm为后缀的列信息文件。列信息文件的格式如下:

        字段数量,<字段名称,字段的二进制位描述>

其中,“字段数量”表示索引中字段的数量;“字段名称”是用字符串表示的字段的名称;“字段的二进制位描述”是个byte值和int值。byte值用1个最低位表示是否索引这个字段。

例如:

        doc.add(new Field("text", "content", Field.Store.NO, Field.Index.TOKENIZED));

在列信息文件中存储成:

        1, <content, 0x01>

词典文件以tis为后缀。这个文件中的词是按顺序存放的。词首先按词对应的字段名称排序,然后按词的正文排序。词典文件的格式如下:

        词的数量,<词,词的文档频率>

词用前缀压缩的方式存储,格式是:前缀的长度,后缀,字段编号。“词的文档频率”指词在多少个文档中出现过。排好序的词表中,前后两个词往往包括共同的前缀。“前缀的长度”变量是表示与前一项相同的前缀的字数。例如,如果前一个词是“bone”,后一个是“boy”的话,前缀的长度值为2,后缀值为“y”。

例如有两个文档,内容如下。

文档1:Penn State Football …football。

文档2:Football players … State。

在索引中的存储形式如下:

        4, <<0, football,1>,2> <<0, penn,1>, 1> <<1, layers,1>,1> <<0, state,1>,2>

词典文件太大,为了能把词信息完整地读入内存,设计出了词信息索引文件(.tii)。词信息索引文件不存储词本身,但是保存随机读取的文件的位置信息。

词在文档中出现的频率文件以frq为文件后缀。词频率首先是按照tis中的词序来排列,在每个词存储信息,存储这个词在文档中出现的频率,按照docID排序。频率文件中并没有存储docID,而是存储与前后两个docID的增量相关的一个值DocDelta。频率文件的格式如下:

        <DocDelta[, Freq? ]>

DocDelta同时决定了文档号和频数。详细地说,DocDelta/2表示当前docID相对于前一个docID的偏移量(或者是0,表示这是TermFreqs里面的第一项)。当DocDelta是奇数时表示在该文档中频数为1;当DocDelta是偶数时,则下一个整数表示在该文档中出现的频数。

例如,假设某一项在文档7中出现1次,在文档11中出现3次,在TermFreqs中就存在如下的整数序列:15, 8, 3。

在这里:

15=2*7+1,在文档7中出现频率是1。

8=2*(11-7),在文档11中出现频率>1。

3,在文档11中出现频率=3。

Posting List见表3-3,其对应的频率文件存储内容如下:

表3-3 Posting List

        <<2, 2, 3> <3> <5> <3, 3>>

存储词在文档中出现过的位置的位置文件以prx为后缀。TermPositions按照词来排序(依据tis文件中词的位置)。Positions数值按照docID升序排列。实际存储的是PositionDelta值,PositionDelta是当前位置相对于前一个出现位置(或者为0,表示这是第一次在这个文档中出现)的增量值。例如,假设某词在某文档第4项出现,在接下来的一个文档中第5项和第9项出现,将表示为如下的整数序列:4, 5, 4。表3-3对应的位置文件的存储内容如下:

        <<3, 64> <1>>  <<1> <0>>  <<0> <2>>  <<2> <13>>

从索引数据可以看出,索引数据是以词为中心组织的。TermPositions类提供了用来遍历一个词的<文档,频率,<位置>* >元组的方法。

Lucene的查询过程访问的文件如图3-6所示。

图3-6 查询访问文件

复合文件格式的索引优化后只有3个文件,其中segments_N和segments.gen是固定不变的,因为这两个文件是在索引级别存在的文件,还有一个是复合索引文件格式(.cfs)。可以通过setUseCompoundFile()方法设定是否使用复合文件格式。