You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jp...@apache.org on 2024/08/26 12:54:04 UTC

(lucene) branch branch_9x updated: Use Max WAND optimizations with ToParentBlockJoinQuery when using ScoreMode.Max (#13587)

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

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


The following commit(s) were added to refs/heads/branch_9x by this push:
     new 4d8ac2b0a02 Use Max WAND optimizations with ToParentBlockJoinQuery when using ScoreMode.Max (#13587)
4d8ac2b0a02 is described below

commit 4d8ac2b0a02cf3fd50ec5f4c56409982df50e317
Author: Mike Pellegrini <mi...@elastic.co>
AuthorDate: Mon Aug 26 08:43:48 2024 -0400

    Use Max WAND optimizations with ToParentBlockJoinQuery when using ScoreMode.Max (#13587)
---
 lucene/CHANGES.txt                                 |   2 +
 .../lucene/search/join/ToParentBlockJoinQuery.java |  22 +++-
 .../lucene/search/join/TestBlockJoinScorer.java    | 129 ++++++++++++++++++++-
 3 files changed, 144 insertions(+), 9 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 3267fb59eb4..6f61297e258 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -116,6 +116,8 @@ Optimizations
 * GITHUB##13644: Improve NumericComparator competitive iterator logic by comparing the missing value with the top
   value even after the hit queue is full (Pan Guixin)
 
+* GITHUB#13587: Use Max WAND optimizations with ToParentBlockJoinQuery when using ScoreMode.Max (Mike Pellegrini)
+
 Changes in runtime behavior
 ---------------------
 
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
index 4aa3b64aeb4..bc3970d4d8e 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
@@ -101,12 +101,17 @@ public class ToParentBlockJoinQuery extends Query {
               .rewrite(new ConstantScoreQuery(childQuery))
               .createWeight(searcher, weightScoreMode, 0f);
     } else {
-      // if the score is needed we force the collection mode to COMPLETE because the child query
-      // cannot skip
-      // non-competitive documents.
+      // if the score is needed and the score mode is not max, we force the collection mode to
+      // COMPLETE because the child query cannot skip non-competitive documents.
+      // weightScoreMode.needsScores() will always be true here, but keep the check to make the
+      // logic clearer.
       childWeight =
           childQuery.createWeight(
-              searcher, weightScoreMode.needsScores() ? COMPLETE : weightScoreMode, boost);
+              searcher,
+              weightScoreMode.needsScores() && childScoreMode != ScoreMode.Max
+                  ? COMPLETE
+                  : weightScoreMode,
+              boost);
     }
     return new BlockJoinWeight(this, childWeight, parentsFilter, childScoreMode);
   }
@@ -165,6 +170,13 @@ public class ToParentBlockJoinQuery extends Query {
         public long cost() {
           return childScorerSupplier.cost();
         }
+
+        @Override
+        public void setTopLevelScoringClause() throws IOException {
+          if (scoreMode == ScoreMode.Max) {
+            childScorerSupplier.setTopLevelScoringClause();
+          }
+        }
       };
     }
 
@@ -343,7 +355,7 @@ public class ToParentBlockJoinQuery extends Query {
 
     @Override
     public void setMinCompetitiveScore(float minScore) throws IOException {
-      if (scoreMode == ScoreMode.None) {
+      if (scoreMode == ScoreMode.None || scoreMode == ScoreMode.Max) {
         childScorer.setMinCompetitiveScore(minScore);
       }
     }
diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinScorer.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinScorer.java
index 6acca015c0e..4f7b13e8a6a 100644
--- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinScorer.java
+++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinScorer.java
@@ -16,19 +16,26 @@
  */
 package org.apache.lucene.search.join;
 
+import static org.apache.lucene.search.ScoreMode.TOP_SCORES;
+
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.document.Field;
+import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.BoostQuery;
+import org.apache.lucene.search.ConstantScoreQuery;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreMode;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.ScorerSupplier;
 import org.apache.lucene.search.TermQuery;
@@ -76,10 +83,9 @@ public class TestBlockJoinScorer extends LuceneTestCase {
 
     Query childQuery = new MatchAllDocsQuery();
     ToParentBlockJoinQuery query =
-        new ToParentBlockJoinQuery(
-            childQuery, parentsFilter, org.apache.lucene.search.join.ScoreMode.None);
+        new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.None);
 
-    Weight weight = searcher.createWeight(searcher.rewrite(query), ScoreMode.TOP_SCORES, 1);
+    Weight weight = searcher.createWeight(searcher.rewrite(query), TOP_SCORES, 1);
     LeafReaderContext context = searcher.getIndexReader().leaves().get(0);
 
     Scorer scorer = weight.scorer(context);
@@ -118,4 +124,119 @@ public class TestBlockJoinScorer extends LuceneTestCase {
     reader.close();
     dir.close();
   }
+
+  public void testScoreMax() throws IOException {
+    try (Directory dir = newDirectory()) {
+      try (RandomIndexWriter w =
+          new RandomIndexWriter(
+              random(),
+              dir,
+              newIndexWriterConfig()
+                  .setMergePolicy(
+                      // retain doc id order
+                      newLogMergePolicy(random().nextBoolean())))) {
+
+        for (String[][] values :
+            Arrays.asList(
+                new String[][] {{"A", "B"}, {"A", "B", "C"}},
+                new String[][] {{"A"}, {"B"}},
+                new String[][] {{}},
+                new String[][] {{"A", "B", "C"}, {"A", "B", "C", "D"}},
+                new String[][] {{"B"}},
+                new String[][] {{"B", "C"}, {"A", "B"}, {"A", "C"}})) {
+
+          List<Document> docs = new ArrayList<>();
+          for (String[] value : values) {
+            Document childDoc = new Document();
+            childDoc.add(newStringField("type", "child", Field.Store.NO));
+            for (String v : value) {
+              childDoc.add(newStringField("value", v, Field.Store.NO));
+            }
+            docs.add(childDoc);
+          }
+
+          Document parentDoc = new Document();
+          parentDoc.add(newStringField("type", "parent", Field.Store.NO));
+          docs.add(parentDoc);
+
+          w.addDocuments(docs);
+        }
+
+        w.forceMerge(1);
+      }
+
+      try (IndexReader reader = DirectoryReader.open(dir)) {
+        IndexSearcher searcher = newSearcher(reader);
+
+        BooleanQuery childQuery =
+            new BooleanQuery.Builder()
+                .add(
+                    new BoostQuery(
+                        new ConstantScoreQuery(new TermQuery(new Term("value", "A"))), 2),
+                    BooleanClause.Occur.SHOULD)
+                .add(
+                    new ConstantScoreQuery(new TermQuery(new Term("value", "B"))),
+                    BooleanClause.Occur.SHOULD)
+                .add(
+                    new BoostQuery(
+                        new ConstantScoreQuery(new TermQuery(new Term("value", "C"))), 3),
+                    BooleanClause.Occur.SHOULD)
+                .add(
+                    new BoostQuery(
+                        new ConstantScoreQuery(new TermQuery(new Term("value", "D"))), 4),
+                    BooleanClause.Occur.SHOULD)
+                .build();
+        BitSetProducer parentsFilter =
+            new QueryBitSetProducer(new TermQuery(new Term("type", "parent")));
+        ToParentBlockJoinQuery parentQuery =
+            new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Max);
+
+        Weight weight = searcher.createWeight(searcher.rewrite(parentQuery), TOP_SCORES, 1);
+        ScorerSupplier ss = weight.scorerSupplier(searcher.getIndexReader().leaves().get(0));
+        ss.setTopLevelScoringClause();
+        Scorer scorer = ss.get(Long.MAX_VALUE);
+
+        assertEquals(2, scorer.iterator().nextDoc());
+        assertEquals(2 + 1 + 3, scorer.score(), 0);
+
+        assertEquals(5, scorer.iterator().nextDoc());
+        assertEquals(2, scorer.score(), 0);
+
+        assertEquals(10, scorer.iterator().nextDoc());
+        assertEquals(2 + 1 + 3 + 4, scorer.score(), 0);
+
+        assertEquals(12, scorer.iterator().nextDoc());
+        assertEquals(1, scorer.score(), 0);
+
+        assertEquals(16, scorer.iterator().nextDoc());
+        assertEquals(2 + 3, scorer.score(), 0);
+
+        assertEquals(DocIdSetIterator.NO_MORE_DOCS, scorer.iterator().nextDoc());
+
+        ss = weight.scorerSupplier(searcher.getIndexReader().leaves().get(0));
+        ss.setTopLevelScoringClause();
+        scorer = ss.get(Long.MAX_VALUE);
+        scorer.setMinCompetitiveScore(6);
+
+        assertEquals(2, scorer.iterator().nextDoc());
+        assertEquals(2 + 1 + 3, scorer.score(), 0);
+
+        assertEquals(10, scorer.iterator().nextDoc());
+        assertEquals(2 + 1 + 3 + 4, scorer.score(), 0);
+
+        assertEquals(DocIdSetIterator.NO_MORE_DOCS, scorer.iterator().nextDoc());
+
+        ss = weight.scorerSupplier(searcher.getIndexReader().leaves().get(0));
+        ss.setTopLevelScoringClause();
+        scorer = ss.get(Long.MAX_VALUE);
+
+        assertEquals(2, scorer.iterator().nextDoc());
+        assertEquals(2 + 1 + 3, scorer.score(), 0);
+
+        scorer.setMinCompetitiveScore(11);
+
+        assertEquals(DocIdSetIterator.NO_MORE_DOCS, scorer.iterator().nextDoc());
+      }
+    }
+  }
 }