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