Fork me on GitHub

构造 IndexWriter 对象(五)

系列文章:
构造 IndexWriter 对象(四)
构造 IndexWriter 对象(三)
构造 IndexWriter 对象(二)
构造 IndexWriter 对象(一)

  本文承接构造 IndexWriter 对象(四),继续介绍调用 IndexWriter 的构造函数的流程。

调用 IndexWriter 的构造函数的流程图

图 1:

1.png

根据不同的 OpenMode 执行对应的工作

  在上一篇文章中,我们介绍了执行 APPEND 模式下的工作的部分流程点,故继续对剩余的流程进行介绍。

执行 APPEND 模式下的工作的流程图

图 2:

2.png

用索引目录中最新的提交初始化一个新 SegmentInfos 对象

图 3:

3.png

  由于 StandardDirectoryReader 为空,那么就从索引目录中初始化一个新 SegmentInfos 对象(见构造 IndexWriter 对象(三)),即通过找到索引目录中的 segments_N 文件读取索引信息。

  当索引目录中有多个 segments_N 文件时该如何选择

  • Lucene 设定为读取最新的一次提交,即选取 segments_N 的 N 值最大的那个,因为 N 越大意味着更新的提交(commit()操作)

IndexCommit 是否为空?

图 4:

4.png

  在构造 IndexWriter 对象(四)文章中我们说到,图 2 中 StandardDirectoryReader 为空的情况分为下面两种:

  • 用户没有设置 IndexCommit
  • 用户设置了 IndexCommit,但是 IndexCommit 中没有 StandardDirectoryReader 对象的信息

  如果是第一种情况的进入到当前流程点,那么当前流程点的出口为是,那么以 APPEND 模式打开的 IndexWriter 追加的索引信息为索引目录中最新的一次提交。

配置检查 3

图 5:

5.png

 如果 IndexCommit 不为空,那么 IndexCommit 必定是 CommitPoint 或者 SnapshotCommitPoint 对象,接着就需要执行下面的配置检查:

if (commit.getDirectory() != directoryOrig) {
            throw new IllegalArgumentException("IndexCommit's directory doesn't match my directory, expected=" + directoryOrig + ", got=" + commit.getDirectory());
          }

其中 commit 即 IndexCommit 对象、directoryOrg 为 IndexWriter 的工作目录,这个配置检查意味着要求当前构造的 IndexWriter 的工作目录必须和 IndexCommit 对应的索引信息所在的目录必须一致

用 IndexCommit 更新 SegmentInfos 对象

图 6:

6.png

  通过 IndexCommit 读取对应的索引信息,然后更新到上文中已经完成初始化的 SegmentInfos 对象中。

更新 SegmentInfos 的 version

图 7:

7.png

  对一个已经初始化的 SegmentInfos 进行更新操作必然需要更新 version,version 的概念在构造 IndexWriter 对象(三)的文章中介绍,这里不赘述。

  至此,我们介绍完了分别在 CREATE、APPEND、CREATE_AND_APPEND 模式下的执行流程,接着我们根据图 1 介绍剩余的流程点。

检查 IndexSort 合法性

图 8:

8.png

  如果设置了 IndexSort,那么在生成一个段的过程中,Lucene 会根据用户提供的排序规则对段内的文档进行排序,关于 IndexSort 的详细介绍见文章构造 IndexWriter 对象(一), 如果用户通过 IndexWriterConfig.setIndexSort(Sort sort)设置了 IndexSort 配置,那么需要对参数 Sort 进行合法性检查,检查逻辑如下所示:

检查 IndexSort 合法性的流程图

图 9:

9.png

SegmentInfos 对象

图 10:

10.png

为什么检查 IndexSort 合法性的准备数据是 SegmentInfos 对象

  SegmentInfos 对象是索引文件 segments_N 跟。si 文件在内存中的描述,如下图所示:

图 11:

11.png

  由图 11 可以看出,我们只能通过 SegmentInfos 找到每一个段(图 11 中的 SegmentCommitInfo)的段内排序规则 IndexSort(图 11 总红色标注)。

是否还有未处理的 SegmentCommitInfo?

图 12:

12.png

  对每一个 SegmentCommitInfo(见图 11)进行 IndexSort 合法性检查,只要有一个段判断为非法,那么就抛出异常,即构造 IndexWriter 对象失败。

比较 Lucene 版本

图 13:

13.png

 如果通过图 11 的[索引文件。si](https://www.amazingkoala.com.cn/Lucene/suoyinwenjian/2019/0605/63.html)中的 IndexSort 字段来判断出段中没有排序规则,那么需要判断生成该段的 Lucene 版本号,代码如下:

if (segmentIndexSort == null && info.info.getVersion().onOrAfter(Version.LUCENE_6_5_0)){
throw new CorruptIndexException("segment not sorted with indexSort=" + segmentIndexSort, info.info.toString());
}  上述代码中,segmentIndexSort 为段中的排序规则,info.info.getVersion( )中,第一个 info 是 SegmentCommitInfo,第二个 info 为 segmentInfo 对象(即索引文件。si 在内存中的描述),getVersion( )获得值即图 11 中 SegVersion。

  图 13 的流程描述的是,如果正在构造的 IndexWriter 对象设置了 IndexSort 配置,并且旧索引(旧索引指的是图 1 中执行三种打开模式流程获得的索引)中一个或多个段中没有排序规则,并且生成这些段的版本号大于等于 6.5.0,那么就不能通过调用 IndexWriter 构造函数来读取旧的索引。

如何读取没有排序规则的段,并且生成这些段的 Lucene 版本号大于等于 6.5.0 的旧索引

比较排序规则

图 14:

14.png

如果旧索引中的段包含排序规则,那么需要判断是否与正在构造中的 IndexWriter 设置的排序规则一致,不一致则抛出异常,如下所示:

if (segmentIndexSort != null && indexSort.equals(segmentIndexSort) == false) {
    throw new IllegalArgumentException("cannot change previous indexSort=" + segmentIndexSort + " (from segment=" + info + ") to new indexSort=" + indexSort);
}

结语

  基于篇幅,剩余的流程点将在下一篇文章中展开。


本文地址:https://www.6aiq.com/article/1586279369002
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出