You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by be...@apache.org on 2025/10/30 12:23:00 UTC

(lucene) branch main updated: Utilize iterator and not approximation in MaxScoreBulkScorer (#15380)

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

benwtrent pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/lucene.git


The following commit(s) were added to refs/heads/main by this push:
     new 27abe1c5ebd Utilize iterator and not approximation in MaxScoreBulkScorer (#15380)
27abe1c5ebd is described below

commit 27abe1c5ebd0f9a0b41856aebfe4685dd7cd9fb0
Author: Benjamin Trent <be...@gmail.com>
AuthorDate: Thu Oct 30 08:22:53 2025 -0400

    Utilize iterator and not approximation in MaxScoreBulkScorer (#15380)
    
    * Utilize iterator and not approximation in MaxScoreBulkScorer
    
    * CHANGES
    
    * add assert on verifiedMatches
    
    * adjust assertion
    
    * adding some random approx query wrapping
    
    * iter
---
 lucene/CHANGES.txt                                            |  3 +++
 .../src/java/org/apache/lucene/search/DisjunctionScorer.java  | 11 +++++++++++
 .../src/java/org/apache/lucene/search/MaxScoreBulkScorer.java |  3 ++-
 .../test/org/apache/lucene/search/TestBooleanRewrites.java    |  5 ++++-
 .../test/org/apache/lucene/search/TestMaxScoreBulkScorer.java |  9 +++++++++
 5 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index ea9db0d698b..23a25f5431f 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -224,6 +224,9 @@ Bug Fixes
 * GITHUB#15334: Fix NPE for MemoryIndex when using nearest neighbor search. MemoryIndex does not support
   nearest neighbor search, but it should not throw an NPE (Ben Trent)
 
+* GITHUB#15380: Fix potential EOF introduced by optimized filter iterations in MaxScoreBulkScorer. The
+  approximation and the match verification could get out of sync, resulting in an EOFException. (Ben Trent)
+
 Other
 ---------------------
 * GITHUB#15237: Fix SmartChinese to only deserialize dictionary data from classpath with a native array
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
index 80371e019e6..a15a7f43f49 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
@@ -101,9 +101,20 @@ abstract class DisjunctionScorer extends Scorer {
         }
       }
       unverifiedMatches.clear();
+      assert assertVerifiedMatchesApproximationDocID();
       return verifiedMatches;
     }
 
+    private boolean assertVerifiedMatchesApproximationDocID() {
+      int docID = docID();
+      for (DisiWrapper w = verifiedMatches; w != null; w = w.next) {
+        if (w.doc != docID) {
+          return false;
+        }
+      }
+      return true;
+    }
+
     @Override
     public boolean matches() throws IOException {
       verifiedMatches = null;
diff --git a/lucene/core/src/java/org/apache/lucene/search/MaxScoreBulkScorer.java b/lucene/core/src/java/org/apache/lucene/search/MaxScoreBulkScorer.java
index 343239e8c7a..9bc732ba19e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MaxScoreBulkScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MaxScoreBulkScorer.java
@@ -171,7 +171,8 @@ final class MaxScoreBulkScorer extends BulkScorer {
     DisiWrapper top = essentialQueue.top();
     assert top.doc < max;
     while (top.doc < filter.doc) {
-      top.doc = top.approximation.advance(filter.doc);
+      // Must use the iterator as `top` might be a two-phase iterator
+      top.doc = top.iterator.advance(filter.doc);
       top = essentialQueue.updateTop();
     }
 
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanRewrites.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanRewrites.java
index 89d07f66720..ca2d39a8db5 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanRewrites.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanRewrites.java
@@ -34,6 +34,7 @@ import org.apache.lucene.index.Term;
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.tests.index.RandomIndexWriter;
+import org.apache.lucene.tests.search.RandomApproximationQuery;
 import org.apache.lucene.tests.util.LuceneTestCase;
 import org.apache.lucene.tests.util.TestUtil;
 
@@ -518,11 +519,13 @@ public class TestBooleanRewrites extends LuceneTestCase {
   }
 
   private Query randomWrapper(Random random, Query query) {
-    switch (random.nextInt(2)) {
+    switch (random.nextInt(3)) {
       case 0:
         return new BoostQuery(query, TestUtil.nextInt(random, 0, 4));
       case 1:
         return new ConstantScoreQuery(query);
+      case 2:
+        return new RandomApproximationQuery(query, random);
       default:
         throw new AssertionError();
     }
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMaxScoreBulkScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestMaxScoreBulkScorer.java
index 7ee46a21a58..c49dd6d7da6 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestMaxScoreBulkScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestMaxScoreBulkScorer.java
@@ -32,6 +32,7 @@ import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.store.Directory;
+import org.apache.lucene.tests.search.RandomApproximationQuery;
 import org.apache.lucene.tests.util.LuceneTestCase;
 import org.apache.lucene.util.Bits;
 
@@ -146,6 +147,10 @@ public class TestMaxScoreBulkScorer extends LuceneTestCase {
             new BoostQuery(new ConstantScoreQuery(new TermQuery(new Term("foo", "A"))), 2);
         Query clause2 = new ConstantScoreQuery(new TermQuery(new Term("foo", "C")));
         Query filter = new TermQuery(new Term("foo", "B"));
+        if (random().nextBoolean()) {
+          clause1 = new RandomApproximationQuery(clause1, random());
+          clause2 = new RandomApproximationQuery(clause2, random());
+        }
         LeafReaderContext context = searcher.getIndexReader().leaves().get(0);
         Scorer scorer1 =
             searcher
@@ -214,6 +219,10 @@ public class TestMaxScoreBulkScorer extends LuceneTestCase {
             new BoostQuery(new ConstantScoreQuery(new TermQuery(new Term("foo", "A"))), 2);
         Query clause2 = new ConstantScoreQuery(new TermQuery(new Term("foo", "C")));
         Query filter = new TermQuery(new Term("foo", "B"));
+        if (random().nextBoolean()) {
+          clause1 = new RandomApproximationQuery(clause1, random());
+          clause2 = new RandomApproximationQuery(clause2, random());
+        }
         LeafReaderContext context = searcher.getIndexReader().leaves().get(0);
         Scorer scorer1 =
             searcher