diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 42ea9730f0d..9c04ce90c5e 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -136,6 +136,8 @@ Optimizations * GITHUB#12361: Faster top-level disjunctions sorted by descending score. (Adrien Grand) +* GITHUB#12383: Assign a dummy simScorer in TermsWeight if score is not needed. (Sagar Upadhyaya) + * GITHUB#12372: Reduce allocation during HNSW construction (Jonathan Ellis) * GITHUB#12385: Restore parallel knn query rewrite across segments rather than slices (Luca Cavanna) diff --git a/lucene/core/src/java/org/apache/lucene/search/TermQuery.java b/lucene/core/src/java/org/apache/lucene/search/TermQuery.java index f08855abe5c..c7d5ac325b9 100644 --- a/lucene/core/src/java/org/apache/lucene/search/TermQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/TermQuery.java @@ -72,7 +72,22 @@ public class TermQuery extends Query { if (termStats == null) { this.simScorer = null; // term doesn't exist in any segment, we won't use similarity at all } else { - this.simScorer = similarity.scorer(boost, collectionStats, termStats); + // Assigning a dummy simScorer in case score is not needed to avoid unnecessary float[] + // allocations in case default BM25Scorer is used. + // See: https://github.com/apache/lucene/issues/12297 + if (scoreMode.needsScores()) { + this.simScorer = similarity.scorer(boost, collectionStats, termStats); + } else { + // Assigning a dummy scorer as this is not expected to be called since scores are not + // needed. + this.simScorer = + new Similarity.SimScorer() { + @Override + public float score(float freq, long norm) { + return 0f; + } + }; + } } } diff --git a/lucene/core/src/test/org/apache/lucene/search/TestTermQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestTermQuery.java index 9a6f39de726..473f6c9a8f0 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestTermQuery.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestTermQuery.java @@ -17,11 +17,14 @@ package org.apache.lucene.search; import java.io.IOException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field.Store; import org.apache.lucene.document.StringField; import org.apache.lucene.index.CompositeReaderContext; import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.FieldInvertState; import org.apache.lucene.index.FilterDirectoryReader; import org.apache.lucene.index.FilterLeafReader; import org.apache.lucene.index.LeafReader; @@ -32,6 +35,7 @@ import org.apache.lucene.index.TermState; import org.apache.lucene.index.TermStates; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; +import org.apache.lucene.search.similarities.Similarity; import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.tests.search.DummyTotalHitCountCollector; @@ -164,6 +168,53 @@ public class TestTermQuery extends LuceneTestCase { IOUtils.close(reader, w, dir); } + public void testWithWithDifferentScoreModes() throws Exception { + Directory dir = newDirectory(); + RandomIndexWriter w = + new RandomIndexWriter( + random(), dir, newIndexWriterConfig().setMergePolicy(NoMergePolicy.INSTANCE)); + // segment that contains the term + Document doc = new Document(); + doc.add(new StringField("foo", "bar", Store.NO)); + w.addDocument(doc); + w.getReader().close(); + DirectoryReader reader = w.getReader(); + IndexSearcher searcher = new IndexSearcher(reader); + Similarity existingSimilarity = searcher.getSimilarity(); + + for (ScoreMode scoreMode : ScoreMode.values()) { + final AtomicReference scoreModeInWeight = new AtomicReference(); + final AtomicBoolean scorerCalled = new AtomicBoolean(); + searcher.setSimilarity( + new Similarity() { // Wrapping existing similarity for testing + @Override + public long computeNorm(FieldInvertState state) { + return existingSimilarity.computeNorm(state); + } + + @Override + public SimScorer scorer( + float boost, CollectionStatistics collectionStats, TermStatistics... termStats) { + scorerCalled.set(true); + return existingSimilarity.scorer(boost, collectionStats, termStats); + } + }); + TermQuery termQuery = + new TermQuery(new Term("foo", "bar")) { + @Override + public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) + throws IOException { + scoreModeInWeight.set(scoreMode); + return super.createWeight(searcher, scoreMode, boost); + } + }; + termQuery.createWeight(searcher, scoreMode, 1f); + assertEquals(scoreMode, scoreModeInWeight.get()); + assertEquals(scoreMode.needsScores(), scorerCalled.get()); + } + IOUtils.close(reader, w, dir); + } + private static class NoSeekDirectoryReader extends FilterDirectoryReader { public NoSeekDirectoryReader(DirectoryReader in) throws IOException {