Lucene无法添加具有相同FieldType的多个字段

xdnvmnnf  于 2022-11-07  发布在  Lucene
关注(0)|答案(1)|浏览(155)

在将一些遗留代码从lucene 3.0.0升级到8.5.2的过程中,我遇到了一个使用自定义FieldType向现有文档添加字段的问题。下面的片段演示了这个问题:

import java.io.IOException;
    import java.nio.file.Path;

    import org.apache.lucene.analysis.en.EnglishAnalyzer;
    import org.apache.lucene.document.Document;
    import org.apache.lucene.document.Field;
    import org.apache.lucene.document.FieldType;
    import org.apache.lucene.index.DirectoryReader;
    import org.apache.lucene.index.IndexOptions;
    import org.apache.lucene.index.IndexWriter;
    import org.apache.lucene.index.IndexWriterConfig;
    import org.apache.lucene.search.IndexSearcher;
    import org.apache.lucene.store.Directory;
    import org.apache.lucene.store.MMapDirectory;

    public class LuceneTest {

        private static FieldType FIELD_TYPE = new FieldType();

        static {
            FIELD_TYPE.setStored(true);
            FIELD_TYPE.setTokenized(true);
            FIELD_TYPE.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
            FIELD_TYPE.setStoreTermVectors(true);
            FIELD_TYPE.setStoreTermVectorPayloads(true);
            FIELD_TYPE.setStoreTermVectorPositions(true);
            FIELD_TYPE.setStoreTermVectorOffsets(true);
            FIELD_TYPE.freeze();
        }

        public static void main(String[] args) throws IOException {
            testLucene();
        }       

        public static void testLucene() throws IOException {
            Document doc = new Document();
            doc.add(new Field("f1", "foo", FIELD_TYPE));
            writeDoc(doc);        
            IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(getDirectory()));
            doc = searcher.doc(0);      

            doc.add(new Field("f1", "bar", FIELD_TYPE));
            writeDoc(doc);
        }

        private static void writeDoc(Document doc)
                throws IOException {
            Directory directory = getDirectory();
            IndexWriterConfig conf = new IndexWriterConfig(new EnglishAnalyzer());
            IndexWriter writer = new IndexWriter(directory , conf);
            writer.addDocument(doc);
            writer.flush();
            writer.close();
        }

        private static Directory getDirectory() throws IOException {
            return new MMapDirectory(Path.of("lucenttest"));
        }
    }

并导致以下异常:

java.lang.IllegalArgumentException: all instances of a given field name must have the same term vectors settings (storeTermVectorPositions changed for field="f1")
    at org.apache.lucene.index.TermVectorsConsumerPerField.start(TermVectorsConsumerPerField.java:166)
    at org.apache.lucene.index.TermsHashPerField.start(TermsHashPerField.java:294)
    at org.apache.lucene.index.FreqProxTermsWriterPerField.start(FreqProxTermsWriterPerField.java:72)
    at org.apache.lucene.index.DefaultIndexingChain$PerField.invert(DefaultIndexingChain.java:810)
    at org.apache.lucene.index.DefaultIndexingChain.processField(DefaultIndexingChain.java:442)
    at org.apache.lucene.index.DefaultIndexingChain.processDocument(DefaultIndexingChain.java:406)
    at org.apache.lucene.index.DocumentsWriterPerThread.updateDocument(DocumentsWriterPerThread.java:250)
    at org.apache.lucene.index.DocumentsWriter.updateDocument(DocumentsWriter.java:495)
    at org.apache.lucene.index.IndexWriter.updateDocument(IndexWriter.java:1594)
    at org.apache.lucene.index.IndexWriter.addDocument(IndexWriter.java:1213)
    at com.profium.sir.LuceneTest.writeDoc(LuceneTest.java:66)
    at com.profium.sir.LuceneTest.testLucene(LuceneTest.java:58)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at junit.framework.TestCase.runTest(TestCase.java:154)
    at junit.framework.TestCase.runBare(TestCase.java:127)
    at junit.framework.TestResult$1.protect(TestResult.java:106)
    at junit.framework.TestResult.runProtected(TestResult.java:124)
    at junit.framework.TestResult.run(TestResult.java:109)
    at junit.framework.TestCase.run(TestCase.java:118)
    at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:126)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)

如果从FieldType中删除以下部分,它可以正常工作,但是从我的遗留代码中删除它会导致许多依赖于位置和偏移量的测试失败。

FIELD_TYPE.setStoreTermVectorPayloads(true);
        FIELD_TYPE.setStoreTermVectorPositions(true);
        FIELD_TYPE.setStoreTermVectorOffsets(true);
q0qdq0h2

q0qdq0h21#

问题中的示例看起来很奇怪,因为它执行了以下操作:
1.它会将字段加入至文件。
1.然后为文档编制索引。
1.然后,它向原始文档添加一个 second 字段,其中第二个字段与第一个字段同名(这本身并不是问题)。
1.然后,它使用第二个字段(使用与第一个字段相同的字段类型)* 再次 * 索引同一文档。
查看Fieldadd()方法的javadoc,它声明:

  • 可以添加多个具有相同名称的字段。在这种情况下,如果对字段进行了索引,则其文本将被视为是为了进行搜索而追加的。*

这意味着您实际上没有添加新字段。新数据将追加到原始字段的数据中。
javadoc继续说:

  • 请注意,add方法仅在将文档添加到索引之前才有意义。这些方法不能用于更改现有索引的内容!要实现此目的,必须从索引中删除文档,并添加该文档的新更改版本。*

范例程式码会在文件编制索引之后加入第二个字段。
根据这些字段的用途,可能有2种(或更多)可能性:
1.第二个字段的名称(f2)应该与第一个字段的名称不同。
1.第二个字段应该已添加到另一个文档(doc2)。
如果您针对这两种情况中的任何一种重新排列示例代码(并在索引文档之前设置所有字段),则将不再看到该错误。
我不太熟悉术语向量有效负载、位置和偏移量的使用--但我可以想象,在一个已经索引的文档上添加第二个同名字段可能会导致此类数据无效(因此出现错误)。
Lucene的早期版本(例如3.0.0)可能没有很好地处理这种情况(但这只是猜测)。
下面是一种可能的代码重新排列:

public static void testLucene() throws IOException {
        Document doc = new Document();
        doc.add(new Field("f1", "foo", FIELD_TYPE));
        //writeDoc(doc);

        //Document doc2 = new Document();
        doc.add(new Field("f1", "bar", FIELD_TYPE));
        writeDoc(doc);

        IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(getDirectory()));
        Document foundDoc = searcher.doc(0);
    }

使用Luke查看生成的索引数据,结果如下:

相关问题