LUCENE-8935: BooleanQuery with no scoring clause can now early terminate the query when the total hits is not requested.

This commit is contained in:
jimczi 2019-07-29 10:04:49 +02:00
parent 9f74cb10d3
commit b8289abeeb
3 changed files with 100 additions and 0 deletions

View File

@ -80,6 +80,9 @@ Optimizations
* LUCENE-8922: DisjunctionMaxQuery more efficiently leverages impacts to skip
non-competitive hits. (Adrien Grand)
* LUCENE-8935: BooleanQuery with no scoring clause can now early terminate the query when
the total hits is not requested.
Other
* LUCENE-8778 LUCENE-8911: Define analyzer SPI names as static final fields and document the names in Javadocs.

View File

@ -85,6 +85,19 @@ final class Boolean2ScorerSupplier extends ScorerSupplier {
@Override
public Scorer get(long leadCost) throws IOException {
Scorer scorer = getInternal(leadCost);
if (scoreMode == ScoreMode.TOP_SCORES &&
subs.get(Occur.SHOULD).isEmpty() && subs.get(Occur.MUST).isEmpty()) {
// no scoring clauses but scores are needed so we wrap the scorer in
// a constant score in order to allow early termination
return scorer.twoPhaseIterator() != null ?
new ConstantScoreScorer(weight, 0f, scoreMode, scorer.twoPhaseIterator()) :
new ConstantScoreScorer(weight, 0f, scoreMode, scorer.iterator());
}
return scorer;
}
private Scorer getInternal(long leadCost) throws IOException {
// three cases: conjunction, disjunction, or mix
leadCost = Math.min(leadCost, cost());

View File

@ -283,4 +283,88 @@ public class TestBooleanScorer extends LuceneTestCase {
w.close();
dir.close();
}
public void testFilterConstantScore() throws Exception {
Directory dir = newDirectory();
RandomIndexWriter w = new RandomIndexWriter(random(), dir);
Document doc = new Document();
doc.add(new StringField("foo", "bar", Store.NO));
doc.add(new StringField("foo", "bat", Store.NO));
doc.add(new StringField("foo", "baz", Store.NO));
w.addDocument(doc);
IndexReader reader = w.getReader();
IndexSearcher searcher = new IndexSearcher(reader);
searcher.setQueryCache(null);
{
// single filter rewrites to a constant score query
Query query = new BooleanQuery.Builder().add(new TermQuery(new Term("foo", "bar")), Occur.FILTER).build();
Query rewrite = searcher.rewrite(query);
assertTrue(rewrite instanceof BoostQuery);
assertTrue(((BoostQuery) rewrite).getQuery() instanceof ConstantScoreQuery);
}
Query[] queries = new Query[] {
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "bar")), Occur.FILTER)
.add(new TermQuery(new Term("foo", "baz")), Occur.FILTER)
.build(),
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "baz")), Occur.FILTER)
// non-existing term
.add(new TermQuery(new Term("foo", "arf")), Occur.SHOULD)
.build(),
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "baz")), Occur.FILTER)
.add(new TermQuery(new Term("foo", "baz")), Occur.FILTER)
// non-existing term
.add(new TermQuery(new Term("foo", "arf")), Occur.SHOULD)
.add(new TermQuery(new Term("foo", "arw")), Occur.SHOULD)
.build()
};
for (Query query : queries) {
Query rewrite = searcher.rewrite(query);
for (ScoreMode scoreMode : ScoreMode.values()) {
Weight weight = searcher.createWeight(rewrite, scoreMode, 1f);
Scorer scorer = weight.scorer(reader.leaves().get(0));
if (scoreMode == ScoreMode.TOP_SCORES) {
assertTrue(scorer instanceof ConstantScoreScorer);
} else {
assertFalse(scorer instanceof ConstantScoreScorer);
}
}
}
queries = new Query[]{
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "bar")), Occur.FILTER)
.add(new TermQuery(new Term("foo", "baz")), Occur.SHOULD)
.build(),
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "bar")), Occur.FILTER)
.add(new TermQuery(new Term("foo", "baz")), Occur.MUST)
// non-existing term
.add(new TermQuery(new Term("foo", "arf")), Occur.SHOULD)
.build(),
new BooleanQuery.Builder()
// non-existing term
.add(new TermQuery(new Term("foo", "bar")), Occur.FILTER)
.add(new TermQuery(new Term("foo", "baz")), Occur.SHOULD)
// non-existing term
.add(new TermQuery(new Term("foo", "arf")), Occur.MUST)
.build()
};
for (Query query : queries) {
Query rewrite = searcher.rewrite(query);
for (ScoreMode scoreMode : ScoreMode.values()) {
Weight weight = searcher.createWeight(rewrite, scoreMode, 1f);
Scorer scorer = weight.scorer(reader.leaves().get(0));
assertFalse(scorer instanceof ConstantScoreScorer);
}
}
reader.close();
w.close();
dir.close();
}
}