读lucene的索引段代码笔记

先理清各个主要类的职责:先从小到大,抽象到具体


org.apache.lucene.index.SegmentInfos


些类封装了某个索引目录下的所有索引段文件信息,主要跟文件系统打交道


org.apache.lucene.index.SegmentReader

此类负责读取某一个索引段的所有文件



org.apache.lucene.index.DirectoryReader


此类负责管理多个索引段org.apache.lucene.index.SegmentReader,可以做为整个索引目录的对名索引段接口。。一般我们的操作都是对该类操作,由该类来管理处理对应的段。


可以将SegmentReader看成是一个只有一个索引段的DirectoryReader,如果文件目录有多个索引段的时候,DirectoryReader以数组方式存储了多个SegmentReader实例,

取数据的时候会将各个SegmentReader比较后的数据。


 SegmentReader[] readers = new SegmentReader[sis.size()];
    for (int i = sis.size()-1; i >= 0; i--) {
      boolean success = false;
      try {
        readers[i] = SegmentReader.get(readOnly, sis.info(i), termInfosIndexDivisor);
        readers[i].readerFinishedListeners = this.readerFinishedListeners;
        success = true;
      } 

构造该类的实例时,会得到该索引目录下的各个索引段对应的SegmentReader,SegmentReader在初始时会加载一些必要的信息:

    try {
      instance.core = new SegmentCoreReaders(instance, dir, si, readBufferSize, termInfosIndexDivisor);
      if (doOpenStores) {
        instance.core.openDocStores(si);
      }
      instance.loadDeletedDocs();
      instance.openNorms(instance.core.cfsDir, readBufferSize);
      success = true;
    } 

1.首先会实例一个org.apache.lucene.index.SegmentCoreReaders类,该类构造时会将该索引段的各个元信息加载进来:

              1)域信息:org.apache.lucene.index.FieldInfos.FieldInfos(Directory, String)

              2)倒排表:org.apache.lucene.index.TermInfosReader.TermInfosReader(Directory, String, FieldInfos, int, int)

2.加载被删除的文档标志文件

3. 实例org.apache.lucene.index.SegmentNorms,主要是加载存储在标准化因子(.nrm)文件

在索引阶段设置Document Boost和Field Boost,存储在(.nrm)文件中


生成的各个索引段SegmentReader后,需要按照一定的关系联系起来构成一个DirectoryReader对外服务。

重新给各个索引段的起始docId计算值,由上一个段的最大id做为本段的起始id相对值。段与段的顺序是由


 private void initialize(SegmentReader[] subReaders) throws IOException {
    this.subReaders = subReaders;
    starts = new int[subReaders.length + 1];    // build starts array
    for (int i = 0; i < subReaders.length; i++) {
      starts[i] = maxDoc;
      maxDoc += subReaders[i].maxDoc();      // compute maxDocs

      if (subReaders[i].hasDeletions())
        hasDeletions = true;
    }
    starts[subReaders.length] = maxDoc;

    if (!readOnly) {
      maxIndexVersion = SegmentInfos.readCurrentVersion(directory);
    }
  }

这样所有的增删改查文档,都会先读取此文档的信息的对应在哪个索引段的reader里。。采用的是二分查找法查找对应的reader


  final static int readerIndex(int n, int[] starts, int numSubReaders) {    // find reader for doc n:
    int lo = 0;                                      // search starts array
    int hi = numSubReaders - 1;                  // for first element less

    while (hi >= lo) {
      int mid = (lo + hi) >>> 1;
      int midValue = starts[mid];
      if (n < midValue)
        hi = mid - 1;
      else if (n > midValue)
        lo = mid + 1;
      else {                                      // found a match
        while (mid+1 < numSubReaders && starts[mid+1] == midValue) {
          mid++;                                  // scan to last match
        }
        return mid;
      }
    }
    return hi;
  }


接下来的操作就是对应于单独的SegmentReader的操作,而主要的计算就是还原要计算的docId的绝对docId值

例如:计算某个词的频率:将各个reader命中的频率总加

  @Override
  public int docFreq(Term t) throws IOException {
    ensureOpen();
    int total = 0;          // sum freqs in segments
    for (int i = 0; i < subReaders.length; i++)
      total += subReaders[i].docFreq(t);
    return total;
  }


先看看查找某个词的时候:


  @Override
  public TermEnum terms(Term term) throws IOException {
    ensureOpen();
    if (subReaders.length == 1) {
      // Optimize single segment case:
      return subReaders[0].terms(term);
    } else {
      return new MultiTermEnum(this, subReaders, starts, term);
    }
  }

使用了org.apache.lucene.index.DirectoryReader.MultiTermEnum进行查找

再看看MultiTermEnum的主要结构是:

SegmentMergeQueue:一个保存了SegmentMergeInfo对象的最大堆

将各个reader命中的TermEnum放进这个堆。最后对外就变成按一定字典顺序吐出。



 public MultiTermEnum(IndexReader topReader, IndexReader[] readers, int[] starts, Term t)
      throws IOException {
      this.topReader = topReader;
      queue = new SegmentMergeQueue(readers.length);
      matchingSegments = new SegmentMergeInfo[readers.length+1];
      for (int i = 0; i < readers.length; i++) {
        IndexReader reader = readers[i];
        TermEnum termEnum;
  
        if (t != null) {
          termEnum = reader.terms(t);
        } else
          termEnum = reader.terms();
  
        SegmentMergeInfo smi = new SegmentMergeInfo(starts[i], termEnum, reader);
        smi.ord = i;
        if (t == null ? smi.next() : termEnum.term() != null)
          queue.add(smi);          // initialize queue
        else
          smi.close();
      }
  
      if (t != null && queue.size() > 0) {
        next();
      }
    }


底层实现的管理DirectoryReader的Directory,主要有三个实现类SimpleFSDirectory,NIOFSDirectory,MMapDirectory

  /** Just like {@link #open(File)}, but allows you to
   *  also specify a custom {@link LockFactory}. */
  public static FSDirectory open(File path, LockFactory lockFactory) throws IOException {
    if ((Constants.WINDOWS || Constants.SUN_OS || Constants.LINUX)
          && Constants.JRE_IS_64BIT && MMapDirectory.UNMAP_SUPPORTED) {
      return new MMapDirectory(path, lockFactory);
    } else if (Constants.WINDOWS) {
      return new SimpleFSDirectory(path, lockFactory);
    } else {
      return new NIOFSDirectory(path, lockFactory);
    }
  }


针对64位的操作系统,且通用的一个Directory是新出的MMapDirectory,主要使用java的直接内存操纵索引文件,性能更佳,不过这还没有跟NIOFSDirectory比较过。。

 













  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值