【问题标题】:Lucene keeps adding documents whereas updateDocument is usedLucene 不断添加文档,而使用 updateDocument
【发布时间】:2018-07-07 20:30:12
【问题描述】:

我的项目围绕着 Lucene 6.6.0。实际上它处理一个用java编写的桌面搜索引擎,其中搜索部分与索引部分位于一个单独的应用程序中。有时我必须在索引中添加新字段以满足客户需求,而不必重新索引(即解析文件 + 索引)所有内容。

因此,当应用程序启动时,我使用 IndexWriter,打开与之关联的 IndexReader:

IndexReader reader = DirectoryReader.open(writer, true, false);

然后对于索引中已经存在的每个文档:

StoredField fieldVersion = new StoredField(
            FIELDNAME_VERSION,
            fixedValue // The value is the same for all the documents but may change (for all documents) when upgrading the version.
            );

for (int i = 0; i < idMax; i++) {

    Document currentDoc = reader.document(i);
    // Checks if the field exists in the index
    if (
    // Field does not exist yet
    currentDoc.get(FIELDNAME_VERSION) == null || 
    // Field value is different from what it should be
    !currentDoc.get(FIELDNAME_VERSION).contentEquals(fixedValue))
      {
        // THe field does not exist so we add it to the doc and beforehand removes the field from the currentDoc (also tried without removing first with the same result)
        currentDoc.removeField(FIELDNAME_VERSION);
        currentDoc.add(fieldVersion);
       // Updates the document in the index
       writer.updateDocuments(
       new Term(FIELDNAME_PATH, currentDoc.get(FIELDNAME_PATH),
       currentDoc);

       // also tried with 
       writer.deleteDocuments(new Term(FIELDNAME_PATH, 
       currentDoc.get(FIELDNAME_PATH)));
       writer.addDocument(currentDoc);
      }
}
// When all documents have been checked, write the index
writer.commit();

当我第一次运行此字段时,该字段会按预期添加到所有没有它的文档中。 问题是,当 fixedValue 更改时,会将新文档添加到索引中,而我希望 currentDoc 更新其 fieldVersion,而不是为除 fieldVersion 之外的所有字段创建与原始值相同的另一个文档。

IndexWriter 处于附加模式(也尝试使用附加或创建)。如果我首先索引一个文件,我会在索引中得到 1 个文档,然后在索引更新之后,我会得到 2 个文档,然后是 4 个,然后是 8 个,然后是 16 个,......总是引用同一个文件(只有 fieldVersion有不同的内容)。

This other SO question 没有帮助我。

为什么当我要求 Lucene 更新现有文档时,它会添加一个新文档,以及应该如何解决这个问题(即用相同的文档替换现有文档,只需为 fieldVersion 使用不同的内容?

编辑 1:

调用此方法后,似乎缺少一个字段。该字段通过以下方式初始化:

 new TextField(FIELDNAME_UNSTORED,
            "",
            Field.Store.NO);

所以它没有被存储。

与 FIELDNAME_PATH 关联的字段被初始化为

StringField pathField = new StringField(FIELDNAME_PATH,
            "",
            Field.Store.YES);

编辑 2:

我实际上没有得到的是,如果我只执行deleteDocuments(new Term(...)),那么所有文档都会从索引中删除(如预期的那样),但是如果我在删除 add(currentDoc) 之后添加,那么我会得到两倍的文档。好像该文档在其原始版本中添加了一次,在其更新版本中添加了第二次。

解决方案:

正如@femtoRgon 所指出的,在索引过程中路径字段没有被标记。但随后它被自动标记化了。所以解决方案是在索引期间重新创建路径字段(和其他字段),使用临时Document 来存储字段,然后使用updateDocument() 和这个临时Document

非常感谢任何帮助!

【问题讨论】:

  • 我最好在单独的文件夹中创建带有附加字段的新索引,并在索引完成后删除以前的索引并用新索引重命名文件夹
  • 很好的解决方法,谢谢!在尝试它时,我开始发现它有点夸张(LAN 上 GB 的索引权重),因为在程序的其他区域我使用 updateDocument 并且它有效。您是否介意详细说明为什么您更喜欢创建新索引而不是更新现有索引?

标签: java lucene


【解决方案1】:

lucene 中的更新总是添加一个新文档,它只是先删除所有匹配给定术语的文档,无论找到要删除的文档,它都会愉快地添加新文档。因此,无论出于何种原因,您都没有在该术语上得到匹配。您还没有展示如何对 FIELDNAME_PATH 进行索引,但是对于您在此处的模式,它应该被索引而不是标记(即使用 StringField)。

您可以通过运行TermQuery 来测试您传递给更新的术语是否可以正常工作。如果您从 TermQuery 中得到 0 个结果,那么 IndexWriter.UpdateDocuments 也不会找到要删除的文档。


至于缺少未存储的字段,是的,您在此处使用的模式不能很好地与未存储的字段一起使用。从IndexReader.document 返回的文档中不包含未存储的字段(这是存储字段的目的,以便可以从索引中检索)。因此,由于结果中缺少它,因此您传递到更新的文档中仍然会丢失它,除非您以其他方式重新创建该值。从您使用的任何源材料重建您的文档,或者确保存储您希望在更新中持久保存的任何内容。

【讨论】:

  • 感谢@femtoRgon,关于你的最后一段,我在阅读stackoverflow.com/questions/1316118/lucene-unstored-fields 后也发现了这一点。它与我在路径项上没有匹配的事实脱钩。所以你的第一段对我非常有用,因为我错过了这一点。
  • 所以我可以运行 TermQuery 并且没有命中。我也尝试了另一个字段,它也没有找到任何东西。
  • 现在我想我找到了解决方案,这要归功于您的回答和卢克。 Path 字段的术语计数在索引后为 1,在运行该方法后为 11。所以路径被标记化,而它不应该被标记化。我想我必须重新创建并填写单独文档中的所有字段,然后更新文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多