【Lucene3.0 初窥】索引文件格式(3):Field数据[.fdx/.fdt/.fnm]

喜糖 2010-04-23

注意:以下文章是参见http://lucene.apache.org/java/3_0_1/fileformats.html#Fields和实践中读取文件内容概括总结出来的。

Fields数据磁盘文件存储细节

Lucene 的数据域在内存中组织成Document和Field数据结构。每次建立索引的Document对象都可能拥有不同的Fields,而查询的时候,也可以通过查询词找到文档的相关Fields信息(这些Fields在创建的时候必须是Field.Store.YES)。这些Fields信息存储在后缀名为.fdx/.fdt/.fnm的磁盘文件中。

★ .fnm  域名存储文件

     FieldInfos (.fnm) --> FNMVersion,FieldsCount, <FieldName, FieldBits> FieldsCount

     FNMVersion  --> VInt   域版本号(Lucene2.9新加的)

     FieldsCount  --> VInt   域的数量

     FieldName --> String   域名

     FieldBits --> Byte  域状态标志位

     每个域的8位FieldBits记录了这个域的标志信息。我们从最低位开始,详细阐述一下这些标志位(其实大部分已经在《Document/Fields》阐述过了):

(1) No.1 bit:1表示此域被索引,0则不被索引。所谓被索引,也就是需要将这个域的值加入倒排索引表中去。

      * Field.Index.NO则表示不被索引。

*Field.Index.ANALYZED则表示不但被索引,而且被分词。比如索引"helloworld"后,无论是搜"hello",还是搜"world"都能够被搜到。

*Field.Index.NOT_ANALYZED表示虽然被索引,但是不分词。比如索引"helloworld"后,仅当搜"helloworld"时,能够搜到,搜"hello"和搜"world"都搜不到。

     

      域还有一个标志来设定此域的值是否要被存储。

*Field.Store.Yes则表示存储此域,Field.Store.NO则表示不存储此域。

     

       索引域(indexed)和存储域(Stored)的区别      * 建立索引的域可以将域值作为query来进行搜索。Lucene中建立索引的域如果不被存储,是不会将其域数据内容存放在.fdt文件中的,但其索引表会存放在另外的文件中。

      * 只存储而不建立索引的域则不能通过键入query来搜索,但是可以通过其他Indexed域的搜索来得到相应的Stored域的信息。      很明显,这样区分是有意义的。在现实文档中的所有信息中,有这样一部分信息,是不太适合作为查询关键词,但是有必须被搜索出来的。比如文件路径,我们可以通过查询文件名来找到该文件的路径,但是谁也不会通过完整的文件路径去检索文件名吧?

(2) No.2 bit:1表示保存词向量,0为不保存词向量。

*Field.TermVector.YES表示保存词向量。

      * Field.TermVector.NO表示不保存词向量。

(3) No.3 bit:1表示在词向量中保存位置信息。      * Field.TermVector.WITH_POSITIONS

(4) No.4 bit:1表示在词向量中保存偏移量信息。      * Field.TermVector.WITH_OFFSETS

(5) No.5 bit:1表示不保存标准化因子

*Field.Index.ANALYZED_NO_NORMS

*Field.Index.NOT_ANALYZED_NO_NORMS

(6) No.6 bit:标志是否保存Payload。

【Lucene3.0 初窥】索引文件格式(3):Field数据[.fdx/.fdt/.fnm]

★ .fdx 域数据索引文件    .fdt 域数据文件

1、fdx 用于查找指定document的所有fields在.fdt文件中的位置,因为它包含的是固定长度的数据,因此这个文件可以很容易地进行随机访问。   FieldIndex (.fdx) --> DocFieldCount  <FieldValuesPosition> SegSize

   DocFieldCount --> UInt32   记录单个document存储的域的个数

   FieldValuesPosition  -->  UInt64  记录当前document的域数据在.fdt文件中的偏移位置。

   SegSize 指当前段中有多少个document

2、fdt 存放域的数据值。注意:fdt中只存放那些需要被存储(Stored)的域数据,而不被存储的(Stored)域是不存放在这里面的。下面有一个实例会详细解释这一点。

    FieldIndex (.fdt) -->  DocFieldCount  <DocFieldData> SegSize

    DocFieldData --> FieldCount, <FieldNum, Bits, Value> FieldCount

    FieldCount --> VInt    当前document包含的域的个数

    FieldNum --> VInt   当前域号

    Bits --> Byte 记录域的标志位 (下面从低位开始)

    (1) No.1 bit: 1表示分词后的field,0表示没有分词的field

    (2) No.2 bit: 域包含二进制数据

    (3) No.3 bit: 表示该field的压缩选项被开启,如果压缩选项开启,采用的压缩算法是ZLIB。

    Value --> String  当前域的数据值

【Lucene3.0 初窥】索引文件格式(3):Field数据[.fdx/.fdt/.fnm]

★  专题用例 :

关于例子的详细信息参见《索引文件格式(2):文件结构总体框架》最后的说明。

【Lucene3.0 初窥】索引文件格式(3):Field数据[.fdx/.fdt/.fnm]

(1) 解释一下fnm文件中的数据

◆ 前五个字节(-2,-1,-1,-1,15,)表示fnm文件的版本号(粉红色区域)。上面我们提到了FNMVersion是VInt类型,而VInt类型是可变类型,我们怎么知道FNMVersion有5个字节呢。

   在《索引文件格式(1):基础知识》我们讲到了VInt类型每个Byte的最高位是不表示数值的,而表示后面是否还有一个字节。显然第1个byte=-2的最高位为1,后面有一个字节。知道第5个byte=15的最高位为0,因此后面没有字节了。则FNMVersion就用5个字节表示的,值为:(-2,-1,-1,-1,15,)

◆ 绿色区域的3表示Field的数量。该实例确实只有3个Fields:name、path、content。

◆ 浅蓝色部分就是这三个Fields的名字。拿其中一个来说明:(4,110,97,109,101,1)。在上面我们已经讲到了每个域名数据都有两部分构成<FieldName, FieldBits>。

    先说说FieldName,它是一个String类型。在《索引文件格式(1):基础知识》中我们讲到String由一个VInt来表示字符的数量,而后面的Chars分别用UTF-8编码每个字符。显然(4,110,97,109,101,)中的第一个4表示后面有4个字符,分别是110('n'),97('a'),109('m'),101('e')。

    再说说FieldBits,在这里实例中最后一个字节byte=1表示这个数据。说明标志位的最低bit=1,而其他bit=0。因此说明"name"域是需要索引的域(Indexed)。

(2) 解释一下fdx文件中的数据

◆  前4个字节(0,0,0,2)表示单个Document存储于多少个域,UInt32类型说明是固定4个字节大小。但是有个疑问了,在这个实例中,Document明明有3个Field,为什么这个值是2呢。原因就是第三个Field("content")的Stored is false。这个域是要分词并建立倒排索引的,但并不存储在.fdx和.fdt中。

◆  后面每8个字节(UInt64类型)表示一个数据项。其数据值表示该Document的域数据值在.fdt文件中的存储位置。比如第一项(0,0,0,0,0,0,0,4)表示doc1中要存储的两个域(name和path)从.fdt数据中的第4个字节开始,到下一项(0,0,0,0,0,0,0,49)第49个字节为止。

(3) 解释一下fdt文件中的数据

◆  前4个字节(0,0,0,2)和上面fdx文件一样。

◆  后面正好有4个DocFieldData,记录了每个Document对象的name和path域的内容值。上面也讲到了Document有3个域,为什么content不存储进来呢。content是文档内容,远比name,path域的内容大的多。如果都想这样存储进fdt数据的话,这个文件将无比庞大。另外,content内容提供了查询倒排索引文件的关键字,我们在实际应用中,通过content中的词可以查询到包含此词的文档的path,也就能够进一步读取文档内容。没有必要再去存储一遍全部的原文档内容。这也就是为什么在建立索引的时候content域的Stored=false的原因了。

相关推荐