在前面的文章中,介绍了大部分的索引文件的数据结构,而从这篇文章开始,用两篇文章的篇幅来介绍例如索引文件是如何生成的,索引文件之间生成的先后顺序等内容。
索引文件两个阶段生成:
本篇文章介绍第一阶段,建议大家可以先看下索引文件的索引结构,因为在下文的介绍中,会根据随着流程进度介绍对应索引文件的数据结构。
term指的是对域值分词后的token,例如下图中域名为"content"的域(Field),它的域值为"it is good":
图1:
在使用空格分词器后,我们就可以得到3个term:"it"、"is"、"good"。
索引选项(IndexOptions),如果你希望定义的域在搜索阶段能根据该域进行搜索,那么必须至少设置一个非NONE的索引选项,这样才能将这个域的写入到倒排表中,并最后写入到索引文件,索引选项有以下几种:
文档号、词频、位置、偏移的概念在倒排表(上)中有详细介绍。
图2:
图3:
Document即我们定义的Document对象,一个Document对象中可以有多种域,下图中一个Document中包含了域名为"content"的存储域、域名为"space"的点数据域、域名为"author"的DocValues域。
图4:
图5:
只有IndexOptions不为NONE的域才会被写入到倒排表中,在搜索阶段,这样的域才能作为Query的条件进行搜索,下图是一个最基本的根据域进行查询的Query:
图6:
上图中,我们的查询条件是 满足条件的文档必须包含一个叫"good"的term,并且该term是域名为"content"的域值中的完整值或部分值。
写入倒排表的过程在前面的文章中已经介绍,不赘述。
图7:
类型为Store.YES的域才会存储其域值,上文中被写入到倒排表的域能在搜索阶段根据该域进行搜索,并获得文档,但是如果该域的域值没有设置为Store.YES,那仅仅只可以拿到文档号,而拿不到文档的内容,即取不到域值。
在当前流程中,需要将域的域值跟域值类型写入到一个bufferedDocs[ ]数组(在源码中bufferedDocs其实是一个对象,它用来将数据写入到buffer[ ]中,为了方便介绍,所以我们直接认为bufferedDocs是一个数组)中,目前Lucene7.5.0支持的存储域的域值类型fieldType为下面几种:
bufferedDocs[ ]数组数据结构如下:
图8:
FieldNumAndType是域的编号fieldNum跟域值类型FieldType的组合值, 域的编号是一个从开0开始的递增值,根据域名来获得一个域的编号,FieldNumAndType = fieldNum << 3 | FieldType。比如有以下的一个Document:
Value即域值,如果是数值类型,那么通过zigZag编码后存储,如果不是,那么按字节存储存储域值,并存储一个length来表示字节的个数。
图9:
上图中两个Document,共5个存储域,假设当前域"content"的编号是0,域"author"的编号是1,并且根据域值类型,他们的FieldType分别是(0x00)、(0x00)、(0x02)、(0x00)、(0x00),所以他们FieldNumAndType分别是:(0 << 3 | 0x00 = 0)、(0 << 3 | 0x00 = 0)、(0 << 3 | 0x02 = 2)、(1 << 3 | 0x00 = 8)、(0 << 3 | 0x00 = 0)。故bufferedDocs[ ]数组如下:
图10:
存储域的域值的信息对应在.fdx、.fdt索引文件中的位置如下图,强调的是下图只是其中一种情况的.fxt的索引文件,详情请看.fdx、.fdt的介绍:
图11:
图12:
在生成索引文件的第一阶段,负责收集一些数据,根据这些数据在生成索引文件的第二阶段生成.dvm、,dvd索引文件,在下一篇文章中,会详细介绍在第一阶段收集了哪些数据。
图13:
在生成索引文件的第一阶段,负责收集一些数据,根据这些数据在生成索引文件的第二阶段生成.dim、.dii索引文件,在两阶段生成索引文件之第二阶段的文章中,会详细介绍在第一阶段收集了哪些数据。
图14:
索引域描述的是,该域具有非NONE的IndexOptions,并且该域可以不是存储域。
在生成索引文件的第一阶段,负责收集一些数据,根据这些数据在生成索引文件的第二阶段生成nvd、.nvm索引文件,在两阶段生成索引文件之第二阶段的文章中,会详细介绍在第一阶段收集了哪些数据。
图15:
在这个流程点,需要增量的记录下面的数据:
图16:
图17:
IndexWriter每次添加完一篇文档,就会判断两个条件,如果满足其中任意一个,那么需要生成一个chunk。
通过numBufferedDocs来判断是否满足条件1,通过bufferedDocs[ ]数组来判断是否满足条件2。chunkSize的值根据存储域的配置选项(Configuration option for stored fields)有两种值:
在创建IndexWriter对象时,用户可以通过IndexWriterConfig来自行选择一种模式,默认是BEST_SPEED模式。
存储域的一个chunk信息对应在.fdx、.fdt索引文件中的位置如下图:
图18:
这里需要强调的是,endOffsets[ ]中记录的是每个Document的域值在bufferedDocs[ ]数组中的偏移位置,也就是告诉我们每个Document的存储域的域值长度,另外DocBase的值是上一个chunk中最后一篇文档的文档号加1。
另外每生成一个chunk,我们需要记录跟chunk相关的信息,在后面的流程需要用到这些信息来生成索引文件,即.fdx文件。
图19:
当chunk的个数达到blockSize,那么需要在索引文件.fdx中生成一个block,用该block来描述这blockSize个chunk在.fdt中的位置。
blockSize在Lucene7.5.0版本中是固定值1024,跟BEST_SPEED还是BEST_COMPRESSION模式无关。
图20:
当chunk的个数达到1024个,那么把这1024个chunk在.fdx中的偏移位置及包含的文档数生成一个block,写到.fdx文件中。
图21:
大家通过看TermVector的.tvd、tvx索引文件会发现其索引结构跟存储域的索引文件及其相似,故其逻辑是类似的,所以就不赘述了。
没啥好说的。
点击下载Markdown文件