You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dn...@apache.org on 2026/07/14 23:18:47 UTC

(lucene) 01/01: Handle inconsistent schema on flush with index sorts (#15125)

This is an automated email from the ASF dual-hosted git repository.

dnhatn pushed a commit to branch 10_5_backport_15125
in repository https://gitbox.apache.org/repos/asf/lucene.git

commit 0fec5b6a292404f0d46ba4da983399e63d50ff6e
Author: Nhat Nguyen <nh...@elastic.co>
AuthorDate: Mon Sep 29 10:05:29 2025 -0700

    Handle inconsistent schema on flush with index sorts (#15125)
    
    If index sorts are configured and a document's schema is inconsistent
    with existing segments, IndexWriter may encounter a NullPointerException
    when building sort comparators.
---
 lucene/CHANGES.txt                                 |  2 +-
 .../org/apache/lucene/index/IndexingChain.java     | 10 ++--
 .../org/apache/lucene/index/TestIndexSorting.java  | 63 ++++++++++++++++++++++
 3 files changed, 69 insertions(+), 6 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 11580688c04..01ccbf80678 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -7,7 +7,7 @@ http://s.apache.org/luceneversions
 
 Bug Fixes
 ---------------------
-(No changes)
+* GITHUB#15125: Handle inconsistent schema on flush with index sorts (Nhat Nguyen)
 
 ======================= Lucene 10.5.0 =======================
 
diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexingChain.java b/lucene/core/src/java/org/apache/lucene/index/IndexingChain.java
index 02f6580f9c1..8de567704a3 100644
--- a/lucene/core/src/java/org/apache/lucene/index/IndexingChain.java
+++ b/lucene/core/src/java/org/apache/lucene/index/IndexingChain.java
@@ -181,7 +181,7 @@ final class IndexingChain implements Accountable {
       @Override
       public NumericDocValues getNumericDocValues(String field) {
         PerField pf = getPerField(field);
-        if (pf == null) {
+        if (pf == null || pf.fieldInfo == null) {
           return null;
         }
         if (pf.fieldInfo.getDocValuesType() == DocValuesType.NUMERIC) {
@@ -193,7 +193,7 @@ final class IndexingChain implements Accountable {
       @Override
       public BinaryDocValues getBinaryDocValues(String field) {
         PerField pf = getPerField(field);
-        if (pf == null) {
+        if (pf == null || pf.fieldInfo == null) {
           return null;
         }
         if (pf.fieldInfo.getDocValuesType() == DocValuesType.BINARY) {
@@ -205,7 +205,7 @@ final class IndexingChain implements Accountable {
       @Override
       public SortedDocValues getSortedDocValues(String field) throws IOException {
         PerField pf = getPerField(field);
-        if (pf == null) {
+        if (pf == null || pf.fieldInfo == null) {
           return null;
         }
         if (pf.fieldInfo.getDocValuesType() == DocValuesType.SORTED) {
@@ -217,7 +217,7 @@ final class IndexingChain implements Accountable {
       @Override
       public SortedNumericDocValues getSortedNumericDocValues(String field) throws IOException {
         PerField pf = getPerField(field);
-        if (pf == null) {
+        if (pf == null || pf.fieldInfo == null) {
           return null;
         }
         if (pf.fieldInfo.getDocValuesType() == DocValuesType.SORTED_NUMERIC) {
@@ -229,7 +229,7 @@ final class IndexingChain implements Accountable {
       @Override
       public SortedSetDocValues getSortedSetDocValues(String field) throws IOException {
         PerField pf = getPerField(field);
-        if (pf == null) {
+        if (pf == null || pf.fieldInfo == null) {
           return null;
         }
         if (pf.fieldInfo.getDocValuesType() == DocValuesType.SORTED_SET) {
diff --git a/lucene/core/src/test/org/apache/lucene/index/TestIndexSorting.java b/lucene/core/src/test/org/apache/lucene/index/TestIndexSorting.java
index f174fba07a9..fff8623e3f7 100644
--- a/lucene/core/src/test/org/apache/lucene/index/TestIndexSorting.java
+++ b/lucene/core/src/test/org/apache/lucene/index/TestIndexSorting.java
@@ -54,6 +54,8 @@ import org.apache.lucene.document.Field.Store;
 import org.apache.lucene.document.FieldType;
 import org.apache.lucene.document.FloatDocValuesField;
 import org.apache.lucene.document.IntPoint;
+import org.apache.lucene.document.KeywordField;
+import org.apache.lucene.document.LongField;
 import org.apache.lucene.document.NumericDocValuesField;
 import org.apache.lucene.document.SortedDocValuesField;
 import org.apache.lucene.document.SortedNumericDocValuesField;
@@ -3478,4 +3480,65 @@ public class TestIndexSorting extends LuceneTestCase {
       }
     }
   }
+
+  public void testFlushAfterInconsistentSchema() throws IOException {
+    IndexWriterConfig iwc = new IndexWriterConfig(new MockAnalyzer(random()));
+    if (random().nextBoolean()) {
+      Sort indexSort = new Sort(new SortField("host.name", SortField.Type.STRING));
+      iwc.setIndexSort(indexSort);
+    }
+    try (Directory dir = newDirectory();
+        IndexWriter w = new IndexWriter(dir, iwc)) {
+      Consumer<Document> addExtraFields =
+          doc -> {
+            int extraFields = random().nextInt(100);
+            for (int i = 0; i < extraFields; i++) {
+              if (random().nextBoolean()) {
+                doc.add(new NumericDocValuesField("extra-dv-" + i, i));
+              }
+              if (random().nextBoolean()) {
+                doc.add(new LongField("extra-point-" + i, i, Store.NO));
+              }
+              if (random().nextBoolean()) {
+                doc.add(
+                    new KeywordField(
+                        "extra-posting-" + i, new BytesRef(Integer.toString(i)), Store.NO));
+              }
+            }
+          };
+      // first segment
+      {
+        Document doc = new Document();
+        doc.add(new SortedDocValuesField("host.name", newBytesRef("h2")));
+        doc.add(new LongField("@timestamp", 1, Store.NO));
+        addExtraFields.accept(doc);
+        w.addDocument(doc);
+        w.commit();
+      }
+      // second segment
+      {
+        var doc = new Document();
+        doc.add(new NumericDocValuesField("@timestamp", 2));
+        addExtraFields.accept(doc);
+        doc.add(new SortedDocValuesField("host.name", newBytesRef("h1")));
+        expectThrows(IllegalArgumentException.class, () -> w.addDocument(doc));
+        w.commit();
+      }
+      try (DirectoryReader reader = DirectoryReader.open(w)) {
+        assertEquals(1, reader.numDocs());
+      }
+      // third segment
+      {
+        var doc = new Document();
+        addExtraFields.accept(doc);
+        doc.add(new LongField("@timestamp", 3, Store.NO));
+        doc.add(new SortedDocValuesField("host.name", newBytesRef("h1")));
+        w.addDocument(doc);
+        w.commit();
+      }
+      try (DirectoryReader reader = DirectoryReader.open(w)) {
+        assertEquals(2, reader.numDocs());
+      }
+    }
+  }
 }