You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ji...@apache.org on 2026/07/29 06:44:22 UTC

(lucene) branch main updated: used WANDScorer.advanceShallow() to enable block-max optimizations when nested in conjunctions (#16220)

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

jimczi 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 62f29b6c915 used WANDScorer.advanceShallow() to enable block-max optimizations when nested in conjunctions (#16220)
62f29b6c915 is described below

commit 62f29b6c91520ddd2f58528eff055c7a7366be9b
Author: Prithvi S <pr...@gmail.com>
AuthorDate: Wed Jul 29 12:14:16 2026 +0530

    used WANDScorer.advanceShallow() to enable block-max optimizations when nested in conjunctions (#16220)
    
    Implement WANDScorer.advanceShallow() to enable block-max optimizations when nested in conjunctions
---
 lucene/CHANGES.txt                                 |  2 +
 .../java/org/apache/lucene/search/WANDScorer.java  | 17 ++++----
 .../org/apache/lucene/search/TestWANDScorer.java   | 47 ++++++++++++++++++++++
 3 files changed, 58 insertions(+), 8 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 426775464c2..d7aedd4d954 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -356,6 +356,8 @@ Optimizations
 
 * GITHUG#16280: Single-pass writeString fast path for short strings in ByteBuffersDataOutput (neoremind)
 
+* GITHUB#16220: Implement WANDScorer.advanceShallow() to enable block-max optimizations when nested in conjunctions. (Prithvi S)
+
 * GITHUB#16146: Speed up no-score filtered-optional Boolean queries by routing dense conjunctions with cached FixedBitSet filters
   through ConstantScoreBulkScorer, which batches matches in bulk via DocIdStream. (Costin Leau)
 
diff --git a/lucene/core/src/java/org/apache/lucene/search/WANDScorer.java b/lucene/core/src/java/org/apache/lucene/search/WANDScorer.java
index 94c82a92d8a..46a7369f65c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/WANDScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/WANDScorer.java
@@ -566,17 +566,18 @@ final class WANDScorer extends Scorer {
 
   @Override
   public int advanceShallow(int target) throws IOException {
-    // Propagate to improve score bounds
+    // Propagate to sub-scorers. Scorers past target constrain the boundary to docID - 1.
+    int newUpTo = DocIdSetIterator.NO_MORE_DOCS;
     for (Scorer scorer : allScorers) {
-      if (scorer.docID() < target) {
-        scorer.advanceShallow(target);
+      // Use <= instead of < so we still propagate and use the block boundary when already at
+      // target.
+      if (scorer.docID() <= target) {
+        newUpTo = Math.min(newUpTo, scorer.advanceShallow(target));
+      } else if (scorer.docID() != DocIdSetIterator.NO_MORE_DOCS) {
+        newUpTo = Math.min(newUpTo, scorer.docID() - 1);
       }
     }
-    if (target <= upTo) {
-      return upTo;
-    }
-    // TODO: implement
-    return DocIdSetIterator.NO_MORE_DOCS;
+    return newUpTo;
   }
 
   @Override
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java
index 32d11daa44e..5dd0fe6b2f2 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java
@@ -895,6 +895,53 @@ public class TestWANDScorer extends LuceneTestCase {
     dir.close();
   }
 
+  /** Test advanceShallow when WAND is nested inside a conjunction with BlockScoreQueryWrapper. */
+  public void testRandomNestedWAND() throws IOException {
+    Directory dir = newDirectory();
+    IndexWriter w = new IndexWriter(dir, newIndexWriterConfig());
+    int numDocs = atLeast(1000);
+    for (int i = 0; i < numDocs; ++i) {
+      Document doc = new Document();
+      int numValues = random().nextInt(1 << random().nextInt(5));
+      int start = random().nextInt(10);
+      for (int j = 0; j < numValues; ++j) {
+        doc.add(new StringField("foo", Integer.toString(start + j), Store.NO));
+      }
+      w.addDocument(doc);
+    }
+    IndexReader reader = DirectoryReader.open(w);
+    w.close();
+    // turn off concurrent search to avoid Random object used across threads resulting into
+    // RuntimeException, as WANDScorerQuery#createWeight has reference to this searcher,
+    // but will be called during searching
+    IndexSearcher searcher = newSearcher(reader, true, true, false);
+
+    for (int iter = 0; iter < 10; ++iter) {
+      int start = random().nextInt(10);
+      int numClauses = random().nextInt(1 << random().nextInt(5));
+      BooleanQuery.Builder builder = new BooleanQuery.Builder();
+      for (int i = 0; i < numClauses; ++i) {
+        builder.add(
+            maybeWrap(new TermQuery(new Term("foo", Integer.toString(start + i)))), Occur.SHOULD);
+      }
+      Query wandQuery = new WANDScorerQuery(builder.build(), random().nextBoolean());
+
+      int blockLength = TestUtil.nextInt(random(), 2, 16);
+      Query wrappedWand = new BlockScoreQueryWrapper(wandQuery, blockLength);
+
+      int filterTerm = random().nextInt(30);
+      Query nestedQuery =
+          new BooleanQuery.Builder()
+              .add(wrappedWand, Occur.MUST)
+              .add(new TermQuery(new Term("foo", Integer.toString(filterTerm))), Occur.MUST)
+              .build();
+
+      CheckHits.checkTopScores(random(), nestedQuery, searcher);
+    }
+    reader.close();
+    dir.close();
+  }
+
   private static class MaxScoreWrapperScorer extends FilterScorer {
 
     private final int maxRange;