Solr.IndexWriter源码分析.4

2021SC@SDUSC

 /**
   * 关闭所有打开的资源并释放写锁。
   *
   * 如果 {@link IndexWriterConfig#commitOnClose} 为 <code>true</code>,
   * 这将尝试通过写入任何内容来正常关闭
   * 更改,等待任何正在运行的合并、提交和关闭。
   * 在这种情况下,请注意:
   * <ul>
   * <li>如果你调用了prepareCommit但是调用commit失败,这个
   * 方法将抛出 {@code IllegalStateException} 和 {@code IndexWriter}
   * 不会被关闭。</li>
   * <li>如果这个方法抛出任何其他异常,{@code IndexWriter}
   * 将关闭,但更改可能已丢失。</li>
   * </ul>
   *
   * <p>
   * 请注意,这可能是一个代价高昂的
   * 操作,因此,尝试重用单个编写器而不是
   *关闭和打开一个新的。见 {@link #commit()}
   * 关于某些 IO 设备完成的写缓存的警告。
   *
   * <p><b>注意</b>:您必须确保没有其他线程仍在制作
   * 在调用此方法的同时更改。</p>
   */
  @Override
  public void close() throws IOException {
    if (config.getCommitOnClose()) {
      shutdown();
    } else {
      rollback();
    }
  }

 // 如果该线程应该尝试关闭,则返回 true,或者
   // 如果 IndexWriter 现在关闭,则为 false; 别的,
   // 等待另一个线程完成关闭
  synchronized private boolean shouldClose(boolean waitForClose) {
    while (true) {
      if (closed == false) {
        if (closing == false) {
          // We get to close
          closing = true;
          return true;
        } else if (waitForClose == false) {
          return false;
        } else {
        // 另一个线程正在尝试关闭;
           // 等到它以一种方式完成(关闭
           // 成功)或其他(未能关闭)
          doWait();
        }
      } else {
        return false;
      }
    }
  }

  /** Returns the Directory used by this index. */
  public Directory getDirectory() {
  // 返回用户提供的原始目录,解包。
    return directoryOrig;
  }

  @Override
  public InfoStream getInfoStream() {
    return infoStream;
  }

  /** Returns the analyzer used by this index. */
  public Analyzer getAnalyzer() {
    ensureOpen();
    return config.getAnalyzer();
  }

/** 如果 {@link SegmentInfos#getVersion} 低于 {@code newVersion},则将其更新为此值。
    *
    * @lucene.internal */
  public synchronized void advanceSegmentInfosVersion(long newVersion) {
    ensureOpen();
    if (segmentInfos.getVersion() < newVersion) {
      segmentInfos.setVersion(newVersion);
    }
    changed();
  }
/**
    * 如果该索引有删除(包括
    * 缓冲删除)。 请注意,这将返回 true
    * 如果有缓冲的 Term/Query 删除,即使它
    * 原来那些缓冲的删除不匹配任何
    * 文件。
    */
  public synchronized boolean hasDeletions() {
    ensureOpen();
    if (bufferedUpdatesStream.any()
        || docWriter.anyDeletions()
        || readerPool.anyDeletions()) {
      return true;
    }
    for (final SegmentCommitInfo info : segmentInfos) {
      if (info.hasDeletions()) {
        return true;
      }
    }
    return false;
  }

 /**
   * 将文档添加到此索引。
   *
   * <p> 请注意,如果遇到异常(例如磁盘已满)
   * 那么索引会一致,但是这个文件
   * 可能尚未添加。此外,还有可能
   * 索引将有一个非复合格式的段
   * 即使使用复合文件(当合并有
   * 部分成功)。</p>
   *
   * <p> 此方法会定期刷新待处理的文档
   * 到目录(见<a href="#flush">以上</a>),和
   * 还定期触发索引中的段合并
   * 根据使用中的 {@link MergePolicy}。</p>
   *
   * <p>合并临时占用空间
   * 目录。所需的空间量高达 1 倍
   * 被合并的所有段的大小,当没有时
   * 读者/搜索者对索引开放,最多
   * 合并时所有段的大小的 2 倍
   * 读者/搜索者对索引开放(见
   * {@link #forceMerge(int)} 详情)。的顺序
   * 执行的原始合并操作由
   * 合并政策。
   *
   * <p>注意文档中的每一项都不能再
   * 比 {@link #MAX_TERM_LENGTH} 以字节为单位,否则为
   * 将抛出 IllegalArgumentException。</p>
   *
   * <p>请注意,可能会创建无效的 Unicode
   * 如果 UTF16 代理对格式不正确,则为 java 中的字符串。
   * 在这种情况下,无效字符被静默
   * 替换为 Unicode 替换字符
   * U+FFFD。</p>
   *
   * @return <a href="#sequence_number">序列号</a>
   * 对于这个操作
   *
   * @throws CorruptIndexException 如果索引损坏
   * @throws IOException 如果存在低级 IO 错误
   */
  public long addDocument(Iterable<? extends IndexableField> doc) throws IOException {
    return updateDocument(null, doc);
  }

/**
   * 原子地依次添加一个文档块
   * 分配的文档 ID,以便外部阅读器
   * 将看到所有文件或不看到任何文件。
   *
   * <p><b>警告</b>:索引当前没有记录
   * 哪些文档被添加为一个块。今天这是
   * 很好,因为合并将保留一个块。的顺序
   * 段内的文档将被保留,即使是子级
   * 块内的文档被删除。大多数搜索功能
   *(如结果分组和块加入)要求您
   * 标记文件;当这些文件被删除时
   * 搜索功能将无法按预期工作。显然添加
   * 现有块的文档将需要您重新索引
   * 整个区块。
   *
   * <p>不过,未来 Lucene 有可能
   * 更积极地合并重新排序文档(例如,
   * 也许是为了获得更好的索引压缩),在这种情况下
   * 那时您可能需要完全重新索引您的文档。
   *
   * <p>有关详细信息,请参阅{@link #addDocument(Iterable)}
   * 异常后的 index 和 IndexWriter 状态,以及
   * 刷新/合并临时可用空间要求。</p>
   *
   * <p><b>注意</b>:离线拆分索引的工具
   *(例如,contrib 中的 IndexSplitter)或
   * 重新排序文档(例如,IndexSorter in
   * contrib) 不知道这些原子添加的文档
   * 并且很可能会破坏它们。在您的身边使用此类工具
   * 个人风险!
   *
   * @return <a href="#sequence_number">序列号</a>
   * 对于这个操作
   *
   * @throws CorruptIndexException 如果索引损坏
   * @throws IOException 如果存在低级 IO 错误
   *
   * @lucene.experimental
   */
  public long addDocuments(Iterable<? extends Iterable<? extends IndexableField>> docs) throws IOException {
    return updateDocuments((DocumentsWriterDeleteQueue.Node<?>) null, docs);
  }

 /**
    * 自动删除与提供的匹配的文档
    * delTerm 并按顺序添加一个文档块
    * 分配的文档 ID,以便外部阅读器
    * 将看到所有文件或不看到任何文件。
    *
    * 参见 {@link #addDocuments(Iterable)}。
    *
    * @return <a href="#sequence_number">序列号</a>
    * 对于这个操作
    *
    * @throws CorruptIndexException 如果索引损坏
    * @throws IOException 如果存在低级 IO 错误
    *
    * @lucene.experimental
    */
  public long updateDocuments(Term delTerm, Iterable<? extends Iterable<? extends IndexableField>> docs) throws IOException {
    return updateDocuments(delTerm == null ? null : DocumentsWriterDeleteQueue.newNode(delTerm), docs);
  }

  private long updateDocuments(final DocumentsWriterDeleteQueue.Node<?> delNode, Iterable<? extends Iterable<? extends IndexableField>> docs) throws IOException {
    ensureOpen();
    boolean success = false;
    try {
      final long seqNo = maybeProcessEvents(docWriter.updateDocuments(docs, delNode));
      success = true;
      return seqNo;
    } catch (VirtualMachineError tragedy) {
      tragicEvent(tragedy, "updateDocuments");
      throw tragedy;
    } finally {
      if (success == false) {
        if (infoStream.isEnabled("IW")) {
          infoStream.message("IW", "hit exception updating document");
        }
        maybeCloseOnTragicEvent();
      }
    }
  }

/**
    * 专家:
    * 自动更新匹配提供的文档
    * 具有给定文档值字段的术语
    * 并按顺序添加一个文档块
    * 分配的文档 ID,以便外部阅读器
    * 将看到所有文件或不看到任何文件。
    *
    * 此 API 的一种用途是保留旧版本的
    * 文件而不是替换它们。 现有的
    * 文件可以更新以反映它们不是
    * 更长的电流同时自动添加新文档
    * 同时。
    *
    * 与 {@link #updateDocuments(Term, Iterable)} 相反
    * 此方法不会删除索引中的文档
    * 匹配给定的术语,而是用
    * 给定的文档值字段可用作
    * 软删除机制。
   *
   * See {@link #addDocuments(Iterable)}
   * and {@link #updateDocuments(Term, Iterable)}.
   *
   *
   * @return The <a href="#sequence_number">sequence number</a>
   * for this operation
   *
   * @throws CorruptIndexException if the index is corrupt
   * @throws IOException if there is a low-level IO error
   *
   * @lucene.experimental
   */
  public long softUpdateDocuments(Term term, Iterable<? extends Iterable<? extends IndexableField>> docs, Field... softDeletes) throws IOException {
    if (term == null) {
      throw new IllegalArgumentException("term must not be null");
    }
    if (softDeletes == null || softDeletes.length == 0) {
      throw new IllegalArgumentException("at least one soft delete must be present");
    }
    return updateDocuments(DocumentsWriterDeleteQueue.newNode(buildDocValuesUpdate(term, softDeletes)), docs);
  }

/** 专家:尝试按文档 ID 删除,只要
    * 提供的阅读器是近乎实时的阅读器(来自 {@link
    * DirectoryReader#open(IndexWriter)})。 如果
    * 提供的阅读器是从这里获得的 NRT 阅读器
    * writer,它的segment还没有被合并掉,那么
    * 删除成功并且此方法返回一个有效的 (> 0) 序列
    *  数字; 否则,它返回-1,然后调用者必须
    * 分别按 Term 或 Query 删除。
    *
    * <b>注意</b>:此方法只能删除文档
    * 对当前打开的 NRT 阅读器可见。 如果你需要
    * 删除打开 NRT 后索引的文档
    * 读者您必须使用 {@link #deleteDocuments(Term...)})。 */
  public synchronized long tryDeleteDocument(IndexReader readerIn, int docID) throws IOException {
    // NOTE: DON'T use docID inside the closure
    return tryModifyDocument(readerIn, docID, (leafDocId, rld) -> {
      if (rld.delete(leafDocId)) {
        if (isFullyDeleted(rld)) {
          dropDeletedSegment(rld.info);
          checkpoint();
        }
// 必须碰撞 changeCount 所以如果没有其他变化
         // 发生了,我们仍然提交这个更改:
        changed();
      }
    });
  }

 /** 专家:尝试通过文档 ID 更新文档值,只要
   * 提供的阅读器是近乎实时的阅读器(来自 {@link
   * DirectoryReader#open(IndexWriter)})。如果
   * 提供的阅读器是从这里获得的 NRT 阅读器
   * writer,它的segment还没有被合并掉,那么
   * 更新成功并且此方法返回一个有效的 (> 0) 序列
   *  数字;否则,它返回-1,然后调用者必须
   * 要么重试更新并再次解析文档。
   * 如果文档值字段数据为 <code>null</code> 现有
   * 值从与该术语匹配的所有文档中删除。这个可以用
   * 取消删除软删除的文档,因为此方法将应用
   * 即使文档被标记为已删除,字段也会更新。
   *
   * <b>注意</b>:此方法只能更新文档
   * 对当前打开的 NRT 阅读器可见。如果你需要
   * 在打开 NRT 后更新索引的文档
   * 读者您必须使用 {@link #updateDocValues(Term, Field...)}。 */
  public synchronized long tryUpdateDocValue(IndexReader readerIn, int docID, Field... fields) throws IOException {
    // NOTE: DON'T use docID inside the closure
    final DocValuesUpdate[] dvUpdates = buildDocValuesUpdate(null, fields);
    return tryModifyDocument(readerIn, docID, (leafDocId, rld) -> {
      long nextGen = bufferedUpdatesStream.getNextGen();
      try {
        Map<String, DocValuesFieldUpdates> fieldUpdatesMap = new HashMap<>();
        for (DocValuesUpdate update : dvUpdates) {
          DocValuesFieldUpdates docValuesFieldUpdates = fieldUpdatesMap.computeIfAbsent(update.field, k -> {
            switch (update.type) {
              case NUMERIC:
                return new NumericDocValuesFieldUpdates(nextGen, k, rld.info.info.maxDoc());
              case BINARY:
                return new BinaryDocValuesFieldUpdates(nextGen, k, rld.info.info.maxDoc());
              default:
                throw new AssertionError("type: " + update.type + " is not supported");
            }
          });
          if (update.hasValue()) {
            switch (update.type) {
              case NUMERIC:
                docValuesFieldUpdates.add(leafDocId, ((NumericDocValuesUpdate) update).getValue());
                break;
              case BINARY:
                docValuesFieldUpdates.add(leafDocId, ((BinaryDocValuesUpdate) update).getValue());
                break;
              default:
                throw new AssertionError("type: " + update.type + " is not supported");
            }
          } else {
            docValuesFieldUpdates.reset(leafDocId);
          }
        }
        for (DocValuesFieldUpdates updates : fieldUpdatesMap.values()) {
          updates.finish();
          rld.addDVUpdate(updates);
        }
      } finally {
        bufferedUpdatesStream.finishedSegment(nextGen);
      }
    // 必须碰撞 changeCount 所以如果没有其他变化
       // 发生了,我们仍然提交这个更改:
      changed();
    });
  }

  @FunctionalInterface
  private interface DocModifier {
    void run(int docId, ReadersAndUpdates readersAndUpdates) throws IOException;
  }

  private synchronized long tryModifyDocument(IndexReader readerIn, int docID, DocModifier toApply) throws IOException {
    final LeafReader reader;
    if (readerIn instanceof LeafReader) {
      // Reader is already atomic: use the incoming docID:
      reader = (LeafReader) readerIn;
    } else {
      // Composite reader: lookup sub-reader and re-base docID:
      List<LeafReaderContext> leaves = readerIn.leaves();
      int subIndex = ReaderUtil.subIndex(docID, leaves);
      reader = leaves.get(subIndex).reader();
      docID -= leaves.get(subIndex).docBase;
      assert docID >= 0;
      assert docID < reader.maxDoc();
    }

    if (!(reader instanceof SegmentReader)) {
      throw new IllegalArgumentException("the reader must be a SegmentReader or composite reader containing only SegmentReaders");
    }

    final SegmentCommitInfo info = ((SegmentReader) reader).getOriginalSegmentInfo();

// TODO:这是一个缓慢的线性搜索,但是,数量
     // 除非有东西,否则应该包含段
     // 索引严重错误,所以应该是次要的
     // 成本:

    if (segmentInfos.indexOf(info) != -1) {
      ReadersAndUpdates rld = getPooledInstance(info, false);
      if (rld != null) {
        synchronized(bufferedUpdatesStream) {
          toApply.run(docID, rld);
          return docWriter.getNextSequenceNumber();
        }
      }
    }
    return -1;
  }

/** 删除包含 100% 已删除文档的段。 */
  private synchronized void dropDeletedSegment(SegmentCommitInfo info) throws IOException {
    // If a merge has already registered for this
    // segment, we leave it in the readerPool; the
    // merge will skip merging it and will then drop
    // it once it's done:
    if (mergingSegments.contains(info) == false) {
      // it's possible that we invoke this method more than once for the same SCI
      // we must only remove the docs once!
      boolean dropPendingDocs = segmentInfos.remove(info);
      try {
      // 这是偷偷摸摸的 - 我们可能会在删除阅读器时遇到异常,但我们已经
         // 删除了 segmentInfo 的分段,因此我们丢失了 pendingDocs 更新。
         // 因此我们在 finally 块中执行 adjustPendingNumDocs 来解决这个问题。
        dropPendingDocs |= readerPool.drop(info);
      } finally {
        if (dropPendingDocs) {
          adjustPendingNumDocs(-info.info.maxDoc());
        }
      }
    }
  }

 /**
    * 删除包含任何
    * 条款。 所有给定的删除都以原子方式应用和刷新
    * 同时。
    *
    * @return <a href="#sequence_number">序列号</a>
    * 对于这个操作
    *
    * @param terms 用于识别文档的术语数组
    * 待删除
    * @throws CorruptIndexException 如果索引损坏
    * @throws IOException 如果存在低级 IO 错误
    */
  public long deleteDocuments(Term... terms) throws IOException {
    ensureOpen();
    try {
      return maybeProcessEvents(docWriter.deleteTerms(terms));
    } catch (VirtualMachineError tragedy) {
      tragicEvent(tragedy, "deleteDocuments(Term..)");
      throw tragedy;
    }
  }

 /**
    * 删除与任何提供的查询匹配的文档。
    * 所有给定的删除都同时以原子方式应用和刷新。
   *
   * @return The <a href="#sequence_number">sequence number</a>
   * for this operation
   *
   * @param queries array of queries to identify the documents
   * to be deleted
   * @throws CorruptIndexException if the index is corrupt
   * @throws IOException if there is a low-level IO error
   */
  public long deleteDocuments(Query... queries) throws IOException {
    ensureOpen();

    // LUCENE-6379: Specialize MatchAllDocsQuery
    for(Query query : queries) {
      if (query.getClass() == MatchAllDocsQuery.class) {
        return deleteAll();
      }
    }

    try {
      return maybeProcessEvents(docWriter.deleteQueries(queries));
    } catch (VirtualMachineError tragedy) {
      tragicEvent(tragedy, "deleteDocuments(Query..)");
      throw tragedy;
    }
  }

 /**
    * 通过首先删除文档来更新文档
    * 包含 <code>term</code> 然后添加新的
    * 文档。 删除然后添加是原子的,如所见
    * 由同一索引上的读者(刷新可能仅在
    * 添加)。
    *
    * @return <a href="#sequence_number">序列号</a>
    * 对于这个操作
    *
   * @param term the term to identify the document(s) to be
   * deleted
   * @param doc the document to be added
   * @throws CorruptIndexException if the index is corrupt
   * @throws IOException if there is a low-level IO error
   */
  public long updateDocument(Term term, Iterable<? extends IndexableField> doc) throws IOException {
    return updateDocuments(term == null ? null : DocumentsWriterDeleteQueue.newNode(term), Collections.singletonList(doc));
  }

/**
    * 专家:
    * 通过首先更新文档来更新文档
    * 包含带有给定文档值字段的 <code>term</code>
    * 然后添加新文档。 文档值更新和
    * 然后添加是原子的,就像读者在同一索引上看到的那样
    *(只有在添加后才会刷新)。
    *
    * 此 API 的一种用途是保留旧版本的
    * 文件而不是替换它们。 现有的
    * 文件可以更新以反映它们不是
    * 更长的电流同时自动添加新文档
    * 同时。
    *
    * 与 {@link #updateDocument(Term, Iterable)} 相反
    * 此方法不会删除索引中的文档
    * 匹配给定的术语,而是用
    * 给定的文档值字段可用作
    * 软删除机制。
   *
   * See {@link #addDocuments(Iterable)}
   * and {@link #updateDocuments(Term, Iterable)}.
   *
   *
   * @return The <a href="#sequence_number">sequence number</a>
   * for this operation
   *
   * @throws CorruptIndexException if the index is corrupt
   * @throws IOException if there is a low-level IO error
   *
   * @lucene.experimental
   */
  public long softUpdateDocument(Term term, Iterable<? extends IndexableField> doc, Field... softDeletes) throws IOException {
    if (term == null) {
      throw new IllegalArgumentException("term must not be null");
    }
    if (softDeletes == null || softDeletes.length == 0) {
      throw new IllegalArgumentException("at least one soft delete must be present");
    }
    return updateDocuments(DocumentsWriterDeleteQueue.newNode(buildDocValuesUpdate(term, softDeletes)), Collections.singletonList(doc));
  }


 /**
    * 将文档的 <code>field</code> 的 {@link NumericDocValues} 更新为
    * 给定 <code>value</code>。 您只能更新已存在的字段
    * 索引,不通过此方法添加新字段。
   * 
   * @param term
   *          the term to identify the document(s) to be updated
   * @param field
   *          field name of the {@link NumericDocValues} field
   * @param value
   *          new value for the field
   *
   * @return The <a href="#sequence_number">sequence number</a>
   * for this operation
   *
   * @throws CorruptIndexException
   *           if the index is corrupt
   * @throws IOException
   *           if there is a low-level IO error
   */
  public long updateNumericDocValue(Term term, String field, long value) throws IOException {
    ensureOpen();
    if (!globalFieldNumberMap.contains(field, DocValuesType.NUMERIC)) {
      throw new IllegalArgumentException("can only update existing numeric-docvalues fields!");
    }
    if (config.getIndexSortFields().contains(field)) {
      throw new IllegalArgumentException("cannot update docvalues field involved in the index sort, field=" + field + ", sort=" + config.getIndexSort());
    }
    try {
      return maybeProcessEvents(docWriter.updateDocValues(new NumericDocValuesUpdate(term, field, value)));
    } catch (VirtualMachineError tragedy) {
      tragicEvent(tragedy, "updateNumericDocValue");
      throw tragedy;
    }
  }

/**
    * 将文档的 <code>field</code> 的 {@link BinaryDocValues} 更新为
    * 给定 <code>value</code>。 您只能更新已存在的字段
    * 索引,不通过此方法添加新字段。
   * 
   * <p>
   * <b>NOTE:</b> this method currently replaces the existing value of all
   * affected documents with the new value.
   * 
   * @param term
   *          the term to identify the document(s) to be updated
   * @param field
   *          field name of the {@link BinaryDocValues} field
   * @param value
   *          new value for the field
   *
   * @return The <a href="#sequence_number">sequence number</a>
   * for this operation
   *
   * @throws CorruptIndexException
   *           if the index is corrupt
   * @throws IOException
   *           if there is a low-level IO error
   */
  public long updateBinaryDocValue(Term term, String field, BytesRef value) throws IOException {
    ensureOpen();
    if (value == null) {
      throw new IllegalArgumentException("cannot update a field to a null value: " + field);
    }
    if (!globalFieldNumberMap.contains(field, DocValuesType.BINARY)) {
      throw new IllegalArgumentException("can only update existing binary-docvalues fields!");
    }
    try {
      return maybeProcessEvents(docWriter.updateDocValues(new BinaryDocValuesUpdate(term, field, value)));
    } catch (VirtualMachineError tragedy) {
      tragicEvent(tragedy, "updateBinaryDocValue");
      throw tragedy;
    }
  }
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值