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());
+ }
+ }
+ }
}