Ensure we rewrite common queries to `match_none` if possible (#25650)

In certain situations we can early terminate and just skip the entire
query phase or make the lucene level rewrite very cheap if we can already
tell that a query won't match any documents. For instance if there is a single
`match_none` ie. due to some range rewrite in a filter or must clause of a boolean
query it can just drop all it's other queries since it will never match.
This commit is contained in:
Simon Willnauer 2017-07-11 21:19:14 +02:00 committed by GitHub
parent de99610c4e
commit 831dbbf291
4 changed files with 37 additions and 1 deletions

View File

@ -38,7 +38,10 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
@ -438,7 +441,12 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
changed |= rewriteClauses(queryRewriteContext, mustNotClauses, newBuilder::mustNot);
changed |= rewriteClauses(queryRewriteContext, filterClauses, newBuilder::filter);
changed |= rewriteClauses(queryRewriteContext, shouldClauses, newBuilder::should);
// lets do some early termination and prevent any kind of rewriting if we have a mandatory query that is a MatchNoneQueryBuilder
Optional<QueryBuilder> any = Stream.concat(newBuilder.mustClauses.stream(), newBuilder.filterClauses.stream())
.filter(b -> b instanceof MatchNoneQueryBuilder).findAny();
if (any.isPresent()) {
return any.get();
}
if (changed) {
newBuilder.adjustPureNegative = adjustPureNegative;
newBuilder.minimumShouldMatch = minimumShouldMatch;

View File

@ -155,6 +155,9 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
@Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = filterBuilder.rewrite(queryRewriteContext);
if (rewrite instanceof MatchNoneQueryBuilder) {
return rewrite; // we won't match anyway
}
if (rewrite != filterBuilder) {
return new ConstantScoreQueryBuilder(rewrite);
}

View File

@ -418,4 +418,23 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
assertEquals(rewrittenAgain, expected);
assertEquals(QueryBuilder.rewriteQuery(boolQueryBuilder, createShardContext()), expected);
}
public void testRewriteWithMatchNone() throws IOException {
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchNoneQueryBuilder().toString()).toString()));
QueryBuilder rewritten = boolQueryBuilder.rewrite(createShardContext());
assertEquals(new MatchNoneQueryBuilder(), rewritten);
boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new TermQueryBuilder("foo","bar"));
boolQueryBuilder.filter(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchNoneQueryBuilder().toString()).toString()));
rewritten = boolQueryBuilder.rewrite(createShardContext());
assertEquals(new MatchNoneQueryBuilder(), rewritten);
boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new TermQueryBuilder("foo","bar"));
boolQueryBuilder.filter(new BoolQueryBuilder().should(new TermQueryBuilder("foo","bar")).filter(new MatchNoneQueryBuilder()));
rewritten = QueryBuilder.rewriteQuery(boolQueryBuilder, createShardContext());
assertEquals(new MatchNoneQueryBuilder(), rewritten);
}
}

View File

@ -117,4 +117,10 @@ public class ConstantScoreQueryBuilderTests extends AbstractQueryTestCase<Consta
assertEquals(json, 23.0, parsed.boost(), 0.0001);
assertEquals(json, 42.0, parsed.innerQuery().boost(), 0.0001);
}
public void testRewriteToMatchNone() throws IOException {
ConstantScoreQueryBuilder constantScoreQueryBuilder = new ConstantScoreQueryBuilder(new MatchNoneQueryBuilder());
QueryBuilder rewrite = constantScoreQueryBuilder.rewrite(createShardContext());
assertEquals(rewrite, new MatchNoneQueryBuilder());
}
}