java - 如果在任何写入之前打开,为什么 Lucene DirectoryReader 看不到 IndexWriter 所做的任何更改?

标签 java lucene

我正在尝试创建 Lucene IndexWriterDirectoryReader 并保持打开以供将来使用。问题 - 如果在提交之前实例化 DirectoryReader,DirectoryReader 看不到对索引提交的任何更改。

请看下面的代码:

public class SearchLayer1 {

   private final RAMDirectory directory;
   private final IndexWriter indexWriter;
   private final DirectoryReader directoryReader;

   public SearchLayer1() throws IOException {
      this.directory = new RAMDirectory();
      IndexWriterConfig config = new IndexWriterConfig(new SimpleAnalyzer());
      this.indexWriter = new IndexWriter(directory, config);
      this.directoryReader = DirectoryReader.open(indexWriter, false);
   }

   public void add() throws IOException, InterruptedException {
      Document doc = new Document();
      String text = "This is the text to be indexed.";
      doc.add(new StringField("fieldname", text, Field.Store.YES));
      indexWriter.addDocument(doc);
      indexWriter.commit();

      doc = new Document();
      doc.add(new StringField("fieldname", text, Field.Store.YES));
      indexWriter.addDocument(doc);
      indexWriter.commit();
   }


   public void experiment() throws IOException, ParseException {
      //IT WORKS IF THE DirectoryReader IS OPENED AFTER SOME DOCUMENTS ARE ADDED TO THE INDEX 
      //DirectoryReader directoryReader = DirectoryReader.open(indexWriter, false);
      IndexSearcher isearcher = new IndexSearcher(directoryReader);
      Query query = new TermQuery(new Term("fieldname", "This is the text to be indexed."));
      ScoreDoc[] hits = isearcher.search(query, null, 1000).scoreDocs;

      for (int i = 0; i < hits.length; i++) {
         Document hitDoc = isearcher.doc(hits[i].doc);
         System.out.println("==========> " + hitDoc.get("fieldname"));
      }
      directoryReader.close();
   }

   public void close() throws IOException {
      indexWriter.close();

      directory.close();
   }
}

然后在单元测试中执行:

@Test
public void experiment() throws Exception {
   SearchLayer1 searchLayer1 = new SearchLayer1();
   searchLayer1.add();
   searchLayer1.experiment();

   searchLayer1.close();
}

我希望“==========> This is the text to be indexed.”打印两次,但它不会打印,除非我将 DirectoryReader 实例化向下移动到 experiment() 方法。

那么为什么 DirectoryReader 看不到任何提交? 我正在使用 lucene 5.3.1

附言 是的,我知道一些方法的弃用以及 QueryParsers 的存在,请不要费心对此发表评论。

最佳答案

基本上,这就是 Lucene 的工作原理。 如果您打开 IndexReader(例如通过 DirectoryReader.open),您将获得在该特定时刻存在的索引的时间点 View ,并且在您再次打开 IndexReader 之前它不会改变,无论 IndexWriter 上的索引 Activity 如何。 但不要只是关闭然后再次打开 IndexReader。您要做的是重新打开现有阅读器。这样,只有新的段被打开,现有的段可以被重用,而不是总是读取完整的索引(这是一个昂贵的操作)。 重新打开看起来像这样(省略未更改的代码):

public class SearchLayer1 {
  // ...
  private DirectoryReader directoryReader;
  private IndexSearcher indexSearcher;

  public SearchLayer1() throws IOException {
    // ...
    this.directoryReader = DirectoryReader.open(indexWriter, false);
    this.indexSearcher = new IndexSearcher(directoryReader);
  }

  // ...

  private void refreshReader() throws IOException {
    DirectoryReader newReader = DirectoryReader.openIfChanged(this.directoryReader);
    if (newReader != null && newReader != this.directoryReader) {
      this.directoryReader.close();
      this.directoryReader = newReader;
      this.indexSearcher = new IndexSearcher(this.directoryReader);
    }
  }

  public void experiment() throws IOException {
    refreshReader();
    IndexSearcher isearcher = this.indexSearcher;
    // ...
  }

  public void close() throws IOException {
    directoryReader.close();
    // ...
  }
}

在每次搜索之前刷新确保您始终可以看到 最新的更改,但是刷新操作可能会非常昂贵,尤其是在发生合并并且必须打开大段的情况下。 通常,您会有一个按特定时间间隔(例如每秒)运行刷新的计划线程。

此外,处理刷新操作本身是相当低级的。 我建议的代码在这种情况下不涉及任何异常处理 必须关闭旧阅读器或新阅读器的正确实例 如果你有一个专门的刷新线程,你可能不会关闭而是关闭 decRef。更容易并推荐使用 SearcherManager 代替:

import org.apache.lucene.search.SearcherManager;
// ...

public class SearchLayer1 {
  // ...
  private final SearcherManager searcherManager;

  public SearchLayer1() throws IOException {
    // ...
    this.searcherManager = new SearcherManager(indexWriter, false, null);
  }

  // ...

  public void experiment() throws IOException {
    searcherManager.maybeRefresh();
    IndexSearcher isearcher = searcherManager.acquire();
    try {
      // ...
    } finally {
      searcherManager.release(isearcher);
    }
  }

  public void close() throws IOException {
    searcherManager.close();
    // ...
  }
}

同样,最好使用单独的线程进行定期刷新。 无论哪种方式都会为您提供预期的输出。

关于java - 如果在任何写入之前打开,为什么 Lucene DirectoryReader 看不到 IndexWriter 所做的任何更改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34251372/

相关文章:

mysql - 快速实现非常大的索引文本搜索?

java - 使用 Lucene 获得更好的搜索结果

java - 使用 iText 从 PCKS7 签名的 PDF 文件中获取哈希/摘要

java - 使用 BinaryTree 将字符编码为二进制

mysql - 如何使用 Solr 数据导入处理程序来索引 MySQL 表?

java - Lucene:使用 RAMDictionary 进行拼写检查似乎不起作用

search - 使用 DIH (DataImportHandler) 的动态列名

java - 如何在 spring 中初始化应用程序?

java - 如何使用 MuPDF 打开受密码保护的 PDF

java - 计算 10 以下的数字并打印每一个