diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 13a5e1f4b3a..b54fa3ff262 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -82,6 +82,9 @@ Improvements * LUCENE-8631: The Korean's user dictionary now picks the longest-matching word and discards the other matches. (Yeongsu Kim via Jim Ferenczi) +* LUCENE-8732: ConstantScoreQuery can now early terminate the query if the minimum score is + greater than the constant score and total hits are not requested. (Jim Ferenczi) + Changes in Runtime Behavior * LUCENE-8671: Load FST off-heap also for ID-like fields if reader is not opened 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 9ad256d59fa..74e1f3abd4a 100644 --- a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java @@ -18,8 +18,6 @@ package org.apache.lucene.search; import java.io.IOException; -import java.util.Collection; -import java.util.Collections; import java.util.Objects; import org.apache.lucene.index.IndexReader; @@ -115,9 +113,11 @@ public final class ConstantScoreQuery extends Query { final Weight innerWeight = searcher.createWeight(query, ScoreMode.COMPLETE_NO_SCORES, 1f); if (scoreMode.needsScores()) { return new ConstantScoreWeight(this, boost) { - @Override public BulkScorer bulkScorer(LeafReaderContext context) throws IOException { + if (scoreMode == ScoreMode.TOP_SCORES) { + return super.bulkScorer(context); + } final BulkScorer innerScorer = innerWeight.bulkScorer(context); if (innerScorer == null) { return null; @@ -135,21 +135,12 @@ public final class ConstantScoreQuery extends Query { @Override public Scorer get(long leadCost) throws IOException { final Scorer innerScorer = innerScorerSupplier.get(leadCost); - final float score = score(); - return new FilterScorer(innerScorer) { - @Override - public float score() throws IOException { - return score; - } - @Override - public float getMaxScore(int upTo) throws IOException { - return score; - } - @Override - public Collection getChildren() { - return Collections.singleton(new ChildScorable(innerScorer, "constant")); - } - }; + final TwoPhaseIterator twoPhaseIterator = innerScorer.twoPhaseIterator(); + if (twoPhaseIterator == null) { + return new ConstantScoreScorer(innerWeight, score(), scoreMode, innerScorer.iterator()); + } else { + return new ConstantScoreScorer(innerWeight, score(), scoreMode, twoPhaseIterator); + } } @Override diff --git a/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreScorer.java index 723023137e1..82d6632330f 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreScorer.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreScorer.java @@ -19,9 +19,13 @@ package org.apache.lucene.search; import java.io.IOException; import java.util.List; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.MockAnalyzer; 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.IndexWriter; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.Term; @@ -216,4 +220,38 @@ public class TestConstantScoreScorer extends LuceneTestCase { directory.close(); } } + + public void testEarlyTermination() throws IOException { + Analyzer analyzer = new MockAnalyzer(random()); + Directory dir = newDirectory(); + IndexWriter iw = new IndexWriter(dir, newIndexWriterConfig(analyzer).setMaxBufferedDocs(2).setMergePolicy(newLogMergePolicy())); + final int numDocs = 50; + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + Field f = newTextField("key", i % 2 == 0 ? "foo bar" : "baz", Field.Store.YES); + doc.add(f); + iw.addDocument(doc); + } + IndexReader ir = DirectoryReader.open(iw); + + IndexSearcher is = newSearcher(ir); + + TopScoreDocCollector c = TopScoreDocCollector.create(10, null, 10); + is.search(new ConstantScoreQuery(new TermQuery(new Term("key", "foo"))), c); + assertEquals(11, c.totalHits); + assertEquals(TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO, c.totalHitsRelation); + + c = TopScoreDocCollector.create(10, null, 10); + Query query = new BooleanQuery.Builder() + .add(new ConstantScoreQuery(new TermQuery(new Term("key", "foo"))), Occur.SHOULD) + .add(new ConstantScoreQuery(new TermQuery(new Term("key", "bar"))), Occur.FILTER) + .build(); + is.search(query, c); + assertEquals(11, c.totalHits); + assertEquals(TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO, c.totalHitsRelation); + + iw.close(); + ir.close(); + dir.close(); + } } diff --git a/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java b/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java index 441675c9e47..7a4367a7fe7 100644 --- a/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java +++ b/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java @@ -496,7 +496,7 @@ public class TestRangeFacetCounts extends FacetTestCase { } else { ddq.add("field", range.getQuery(fastMatchQuery, vs)); } - assertEquals(expectedCounts[rangeID], s.search(ddq, 10).totalHits.value); + assertEquals(expectedCounts[rangeID], s.count(ddq)); } } @@ -640,7 +640,7 @@ public class TestRangeFacetCounts extends FacetTestCase { ddq.add("field", range.getQuery(fastMatchFilter, vs)); } - assertEquals(expectedCounts[rangeID], s.search(ddq, 10).totalHits.value); + assertEquals(expectedCounts[rangeID], s.count(ddq)); } }