Lucene不断添加文档,而使用updateDocument

时间:2018-07-07 20:30:12

标签: java lucene

我的项目围绕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更新现有文档时,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

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:1)

lucene中的更新总是添加一个新文档,它只会首先删除与给定术语匹配的所有文档,并且无论是否找到要删除的文档,它都会很高兴地添加新文档。因此,无论出于何种原因,您都无法在该词上找到匹配项。您尚未显示如何对FIELDNAME_PATH进行索引,但是对于此处的模式,应该对其进行索引而不是标记化(即,使用StringField)。

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


对于丢失未存储的字段,是的,您在此处使用的模式不适用于未存储的字段。未存储的字段不包含在IndexReader.document返回的文档中(这是存储字段的关键,因此可以从索引中检索该字段)。因此,由于结果中缺少该值,因此除非您以其他方式重新创建该值,否则传递给更新的文档中仍将丢失该值。从正在使用的任何原始资料重建文档,或者确保存储要在更新中保留的所有内容。