在索引(Indexing)阶段,当某个域被设置为需要记录词向量(term vector)信息后,那么随后在flush阶段,该域对应的词向量将被写入到索引文件.tvd&&tvx&&tvm三个文件中。
图1:
图1中,域名"content"跟"title"都被设置为需要记录词向量信息,而域名"author"则没有。
数据结构
索引文件.tvd
图2:
PackedIntsVersion
PackedIntsVersion描述了压缩使用的方式,当前版本中是VERSION_MONOTONIC_WITHOUT_ZIGZAG。
Chunk
图3:
在索引阶段,每当处理128篇文档或者已经处理的域值的总长度达到4096,就生成一个chunk。
DocBase
该字段描述的是chunk中第一篇文档的文档号。
ChunkDocs
该字段描述的是Chunk中的文档数量。
NumFields
该字段描述的是Chunk中每篇文档中记录词向量的域的数量。例如图1中只有一篇文档,这篇文档中的就包含了2个记录词向量的域。
根据Chunk中包含的文档数量,NumFields字段的数据结构各不相同
Chunk中只包含一篇文档
图4:
如果图1所示,那么NumFields的值为2,并且不会使用压缩存储。
Chunk中包含多篇文档
图5:
当包含多篇文档,那么需要记录每一篇文档中记录词向量的域的数量,然后使用PackedInts存储。
FieldNums
该字段描述的是Chunk中记录词向量的域的种类,根据域的编号来获得域的种类。
(域的种类 - 1) ≤ 7
图6:
token
token是一个组合值,并且大小是一个字节:
- numDistinctFields:Chunk中记录词向量的域的种类
- bitsRequired:存储每个域的编号(因为使用了固定位数按位存储)需要的bit数量
- 左移5位描述了bitsRequired的值最多可以是31
- 由于一个字节的低五位被用来描述bitsRequired,所以还剩余3个bit可以用来表示numDistinctFields,所以numDistinctFields的值小于等于7时可以跟bitsRequired使用一个字节存储。
FieldNum
FieldNum即域的编号, 用PackedInts存储。
(域的种类 - 1) > 7
图7:
token
token是一个组合值,并且大小是一个字节:
- bitsRequired:存储每一个域的编号需要最少bit位个数
- 由于numDistinctFields的值大于7,那么在token的高三位用来描述numDistinctFields一部分值,即固定值7,低5位用来描述bitsRequired
- numDistinctFields - 0x07:存储剩余的差值,例如假设numDistinctFields的值为13,那么7存储在token中、6存储在当前字段
FieldNumOffs
图8:
FieldNumOffs中存放了chunk中每一篇文档包含的所有域的编号的索引值FieldNumIndex,并且使用PackedInts存储。该索引其实就是fieldNums[ ]数组的下标值,fieldNums[ ]数组的数组元素是Chunk中的域的编号,数组长度是域的种类数。通过这种方式使得不直接存储域的编号,因为域的编号可能跨度很大,由于使用固定位数按位存储,每个域的编号占用的bit数量取决编号最大的,那会导致较大的存储空间,而存储下标值就缓解这个问题。
图9:
图9中有4个域的编号,如果直接存储域的编号,其中域的编号最大值为255,占用8个bit,故需要 8 *4 = 32个bit。若只存储索引值,那么需要 1 (0) + 1 (1) + 2 (2) + 2 (3) = 6个bit位。
Flags
Flags用来描述记录词向量的域具体记录哪些信息,这些信息包括:位置position、偏移offset、负载payload信息。flag的值可以是下面3个值的组合:
- 0x01:包含位置position信息
- 0x02:包含偏移offset信息
- 0x04:包含负载payload信息
比如说 flag = 3,二进制即0b00000011,即该域会记录位置跟偏移信息。
根据同一个域名在不同的文档中是否有相同的Flag分为不同的情况:
图10:
图10中,域名"content"、"title"在文档0中会记录位置position、偏移offset、负载payload信息,然而在文档1中只记录位置position、负载payload信息。那么这种情况就称为相同的域名有不相同的flag,反之如果所有记录词向量的域在所有文档中对应的flag都是相同的,那么这种情况称为相同的域名有相同的flag。
相同的域名有相同的flag
图11:
对于某个记录词向量的域名来说,无论它在哪个文档中都记录相同的flag信息,所以只要只要记录一次即可,并且用PackedInts存储,固定值0为标志位,在读取阶段用来区分Flags的不同数据结构。图11中每个flag字段对应一种域,即flag的数量等于记录词向量的域的种类数量。
相同的域名有不相同的flag
图12:
对于一个域名来说,它在不同文档中的flag可能不一样(例如当前文档中,某个记录词向量的域名只记录位置信息,而在下一篇文档中,该域名记录了位置信息跟偏移信息),那么只能所有文档中的所有域的flag,并且用PackedInts存储,固定值1为标志位,在读取阶段用来区分Flags的不同数据结构。图12中,每个Flag对应为某篇文档中的某个记录词向量的域。
TermData
TermData记录了域值以及Payload信息。
图13:
NumTerms
NumTerms描述了每一篇文档的每一个域包含的term个数,使用PackedInts存储。
TermLengths
TermLengths描述了每一篇文档的每一个域中的每一个term的长度,使用PackedInts存储。
TermFreqs
TermFreqs描述了每一篇文档的每一个域中的每一个term在当前文档中的词频,使用PackedInts存储。
Positions
Positions描述了每一篇文档的每一个域中的每一个term在当前文档中的所有位置position信息,使用PackedInts存储。
StartOffset
StartOffset描述了每一篇文档的每一个域中的每一个term的startOffset,使用PackedInts存储。
Lengths
Lengths描述了每一篇文档的每一个域中的每一个term的偏移长度,使用PackedInts存储。
TermAndPayloads
使用LZ4算法存储每一篇文档的每一个域中的每一个term值跟payload(如果有的话)。
索引文件.tvd整体数据结构
图14:
索引文件.tvx
图15:
索引文件.tvx中的字段含义同索引文件.fdx, 不赘述。
索引文件.tvm
图16:
索引文件.tvm中的字段含义同索引文件.fdm, 不赘述。
结语
看完这篇文章后,如果感到一脸懵逼, 木有关系,在随后的文章将会详细介绍索引文件tvd&&tvx&&tvm的生成过程。
点击下载Markdown文件