LUCENE-10412: Improve handling of MatchNoDocsQuery in rewrites. (#664)

This commit is contained in:
Adrien Grand 2022-02-09 19:02:54 +01:00 committed by GitHub
parent 2183756f1c
commit 69d3a1d6af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 95 additions and 5 deletions

View File

@ -204,6 +204,9 @@ Optimizations
* LUCENE-10367: Optimize CoveringQuery for the case when the minimum number of
matching clauses is a constant. (LuYunCheng via Adrien Grand)
* LUCENE-10412: More `Query#rewrite` optimizations for MatchNoDocsQuery.
(Adrien Grand)
Changes in runtime behavior
---------------------

View File

@ -275,10 +275,22 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
for (BooleanClause clause : this) {
Query query = clause.getQuery();
Query rewritten = query.rewrite(reader);
if (rewritten != query) {
if (rewritten != query || query.getClass() == MatchNoDocsQuery.class) {
// rewrite clause
actuallyRewritten = true;
builder.add(rewritten, clause.getOccur());
if (rewritten.getClass() == MatchNoDocsQuery.class) {
switch (clause.getOccur()) {
case SHOULD:
case MUST_NOT:
// the clause can be safely ignored
break;
case MUST:
case FILTER:
return rewritten;
}
} else {
builder.add(rewritten, clause.getOccur());
}
} else {
// leave as-is
builder.add(clause);

View File

@ -85,6 +85,11 @@ public final class BoostQuery extends Query {
return new BoostQuery(in.query, boost * in.boost);
}
if (rewritten.getClass() == MatchNoDocsQuery.class) {
// bubble up MatchNoDocsQuery
return rewritten;
}
if (boost == 0f && rewritten.getClass() != ConstantScoreQuery.class) {
// so that we pass needScores=false
return new BoostQuery(new ConstantScoreQuery(rewritten), 0f);

View File

@ -43,6 +43,11 @@ public final class ConstantScoreQuery extends Query {
public Query rewrite(IndexReader reader) throws IOException {
Query rewritten = query.rewrite(reader);
if (rewritten.getClass() == MatchNoDocsQuery.class) {
// bubble up MatchNoDocsQuery
return rewritten;
}
if (rewritten != query) {
return new ConstantScoreQuery(rewritten);
}

View File

@ -663,4 +663,54 @@ public class TestBooleanRewrites extends LuceneTestCase {
w.close();
dir.close();
}
public void testShouldMatchNoDocsQuery() throws IOException {
IndexSearcher searcher = newSearcher(new MultiReader());
BooleanQuery query =
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "bar")), Occur.SHOULD)
.add(new MatchNoDocsQuery(), Occur.SHOULD)
.build();
assertEquals(new TermQuery(new Term("foo", "bar")), searcher.rewrite(query));
}
public void testMustNotMatchNoDocsQuery() throws IOException {
IndexSearcher searcher = newSearcher(new MultiReader());
BooleanQuery query =
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "bar")), Occur.SHOULD)
.add(new MatchNoDocsQuery(), Occur.MUST_NOT)
.build();
assertEquals(new TermQuery(new Term("foo", "bar")), searcher.rewrite(query));
}
public void testMustMatchNoDocsQuery() throws IOException {
IndexSearcher searcher = newSearcher(new MultiReader());
BooleanQuery query =
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "bar")), Occur.MUST)
.add(new MatchNoDocsQuery(), Occur.MUST)
.build();
assertEquals(new MatchNoDocsQuery(), searcher.rewrite(query));
}
public void testFilterMatchNoDocsQuery() throws IOException {
IndexSearcher searcher = newSearcher(new MultiReader());
BooleanQuery query =
new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo", "bar")), Occur.MUST)
.add(new MatchNoDocsQuery(), Occur.FILTER)
.build();
assertEquals(new MatchNoDocsQuery(), searcher.rewrite(query));
}
public void testEmptyBoolean() throws IOException {
IndexSearcher searcher = newSearcher(new MultiReader());
BooleanQuery query = new BooleanQuery.Builder().build();
assertEquals(new MatchNoDocsQuery(), searcher.rewrite(query));
}
}

View File

@ -73,8 +73,8 @@ public class TestBoostQuery extends LuceneTestCase {
IndexSearcher searcher = new IndexSearcher(new MultiReader());
// inner queries are rewritten
Query q = new BoostQuery(new BooleanQuery.Builder().build(), 2);
assertEquals(new BoostQuery(new MatchNoDocsQuery(), 2), searcher.rewrite(q));
Query q = new BoostQuery(new PhraseQuery("foo", "bar"), 2);
assertEquals(new BoostQuery(new TermQuery(new Term("foo", "bar")), 2), searcher.rewrite(q));
// boosts are merged
q = new BoostQuery(new BoostQuery(new MatchAllDocsQuery(), 3), 2);
@ -85,4 +85,11 @@ public class TestBoostQuery extends LuceneTestCase {
assertEquals(
new BoostQuery(new ConstantScoreQuery(new MatchAllDocsQuery()), 0), searcher.rewrite(q));
}
public void testRewriteBubblesUpMatchNoDocsQuery() throws IOException {
IndexSearcher searcher = newSearcher(new MultiReader());
Query query = new BoostQuery(new MatchNoDocsQuery(), 2f);
assertEquals(new MatchNoDocsQuery(), searcher.rewrite(query));
}
}

View File

@ -23,6 +23,7 @@ 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.MultiReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.store.Directory;
@ -254,4 +255,11 @@ public class TestConstantScoreQuery extends LuceneTestCase {
w.close();
dir.close();
}
public void testRewriteBubblesUpMatchNoDocsQuery() throws IOException {
IndexSearcher searcher = newSearcher(new MultiReader());
Query query = new ConstantScoreQuery(new MatchNoDocsQuery());
assertEquals(new MatchNoDocsQuery(), searcher.rewrite(query));
}
}

View File

@ -89,7 +89,7 @@ public class TestMatchNoDocsQuery extends LuceneTestCase {
hits = searcher.search(query, 1000).scoreDocs;
Query rewrite = query.rewrite(ir);
assertEquals(1, hits.length);
assertEquals(rewrite.toString(), "key:one MatchNoDocsQuery(\"field not found\")");
assertEquals(rewrite.toString(), "key:one");
iw.close();
ir.close();