LUCENE-10469: Fix score mode propagation in ConstantScoreQuery. (#750)
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 2c4ec5f..8283442 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -270,6 +270,8 @@
calculation, as this could cause problems with wrapper queries like BooleanQuery which
expect their child queries hashcodes to be stable. (Alan Woodward)
+* LUCENE-10469: Fix ScoreMode propagation by ConstantScoreQuery. (Adrien Grand)
+
Other
---------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
index fec12c6..2f75a56 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
@@ -114,7 +114,16 @@
@Override
public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost)
throws IOException {
- final Weight innerWeight = searcher.createWeight(query, ScoreMode.COMPLETE_NO_SCORES, 1f);
+ // If the score mode is exhaustive then pass COMPLETE_NO_SCORES, otherwise pass TOP_DOCS to make
+ // sure to not disable any of the dynamic pruning optimizations for queries sorted by field or
+ // top scores.
+ final ScoreMode innerScoreMode;
+ if (scoreMode.isExhaustive()) {
+ innerScoreMode = ScoreMode.COMPLETE_NO_SCORES;
+ } else {
+ innerScoreMode = ScoreMode.TOP_DOCS;
+ }
+ final Weight innerWeight = searcher.createWeight(query, innerScoreMode, 1f);
if (scoreMode.needsScores()) {
return new ConstantScoreWeight(this, boost) {
@Override
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestNeedsScores.java b/lucene/core/src/test/org/apache/lucene/search/TestNeedsScores.java
index 57ecad5..c4c6b06 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestNeedsScores.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestNeedsScores.java
@@ -70,9 +70,31 @@
/** nested inside constant score query */
public void testConstantScoreQuery() throws Exception {
Query term = new TermQuery(new Term("field", "this"));
+
+ // Counting queries and top-score queries that compute the hit count should use
+ // COMPLETE_NO_SCORES
Query constantScore =
new ConstantScoreQuery(new AssertNeedsScores(term, ScoreMode.COMPLETE_NO_SCORES));
+ assertEquals(5, searcher.count(constantScore));
+
+ TopDocs hits =
+ searcher.search(
+ constantScore, TopScoreDocCollector.createSharedManager(5, null, Integer.MAX_VALUE));
+ assertEquals(5, hits.totalHits.value);
+
+ // Queries that support dynamic pruning like top-score or top-doc queries that do not compute
+ // the hit count should use TOP_DOCS
+ constantScore = new ConstantScoreQuery(new AssertNeedsScores(term, ScoreMode.TOP_DOCS));
assertEquals(5, searcher.search(constantScore, 5).totalHits.value);
+
+ assertEquals(
+ 5, searcher.search(constantScore, 5, new Sort(SortField.FIELD_DOC)).totalHits.value);
+
+ assertEquals(
+ 5,
+ searcher.search(constantScore, 5, new Sort(SortField.FIELD_DOC, SortField.FIELD_SCORE))
+ .totalHits
+ .value);
}
/** when not sorting by score */