Skip to content

Commit 831dbbf

Browse files
authored
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.
1 parent de99610 commit 831dbbf

File tree

4 files changed

+37
-1
lines changed

4 files changed

+37
-1
lines changed

core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@
3838
import java.util.List;
3939
import java.util.Map;
4040
import java.util.Objects;
41+
import java.util.Optional;
4142
import java.util.function.Consumer;
43+
import java.util.stream.Stream;
44+
import java.util.stream.StreamSupport;
4245

4346
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
4447

@@ -438,7 +441,12 @@ protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws
438441
changed |= rewriteClauses(queryRewriteContext, mustNotClauses, newBuilder::mustNot);
439442
changed |= rewriteClauses(queryRewriteContext, filterClauses, newBuilder::filter);
440443
changed |= rewriteClauses(queryRewriteContext, shouldClauses, newBuilder::should);
441-
444+
// lets do some early termination and prevent any kind of rewriting if we have a mandatory query that is a MatchNoneQueryBuilder
445+
Optional<QueryBuilder> any = Stream.concat(newBuilder.mustClauses.stream(), newBuilder.filterClauses.stream())
446+
.filter(b -> b instanceof MatchNoneQueryBuilder).findAny();
447+
if (any.isPresent()) {
448+
return any.get();
449+
}
442450
if (changed) {
443451
newBuilder.adjustPureNegative = adjustPureNegative;
444452
newBuilder.minimumShouldMatch = minimumShouldMatch;

core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ protected boolean doEquals(ConstantScoreQueryBuilder other) {
155155
@Override
156156
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
157157
QueryBuilder rewrite = filterBuilder.rewrite(queryRewriteContext);
158+
if (rewrite instanceof MatchNoneQueryBuilder) {
159+
return rewrite; // we won't match anyway
160+
}
158161
if (rewrite != filterBuilder) {
159162
return new ConstantScoreQueryBuilder(rewrite);
160163
}

core/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,23 @@ public void testRewriteMultipleTimes() throws IOException {
418418
assertEquals(rewrittenAgain, expected);
419419
assertEquals(QueryBuilder.rewriteQuery(boolQueryBuilder, createShardContext()), expected);
420420
}
421+
422+
public void testRewriteWithMatchNone() throws IOException {
423+
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
424+
boolQueryBuilder.must(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchNoneQueryBuilder().toString()).toString()));
425+
QueryBuilder rewritten = boolQueryBuilder.rewrite(createShardContext());
426+
assertEquals(new MatchNoneQueryBuilder(), rewritten);
427+
428+
boolQueryBuilder = new BoolQueryBuilder();
429+
boolQueryBuilder.must(new TermQueryBuilder("foo","bar"));
430+
boolQueryBuilder.filter(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchNoneQueryBuilder().toString()).toString()));
431+
rewritten = boolQueryBuilder.rewrite(createShardContext());
432+
assertEquals(new MatchNoneQueryBuilder(), rewritten);
433+
434+
boolQueryBuilder = new BoolQueryBuilder();
435+
boolQueryBuilder.must(new TermQueryBuilder("foo","bar"));
436+
boolQueryBuilder.filter(new BoolQueryBuilder().should(new TermQueryBuilder("foo","bar")).filter(new MatchNoneQueryBuilder()));
437+
rewritten = QueryBuilder.rewriteQuery(boolQueryBuilder, createShardContext());
438+
assertEquals(new MatchNoneQueryBuilder(), rewritten);
439+
}
421440
}

core/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,10 @@ public void testFromJson() throws IOException {
117117
assertEquals(json, 23.0, parsed.boost(), 0.0001);
118118
assertEquals(json, 42.0, parsed.innerQuery().boost(), 0.0001);
119119
}
120+
121+
public void testRewriteToMatchNone() throws IOException {
122+
ConstantScoreQueryBuilder constantScoreQueryBuilder = new ConstantScoreQueryBuilder(new MatchNoneQueryBuilder());
123+
QueryBuilder rewrite = constantScoreQueryBuilder.rewrite(createShardContext());
124+
assertEquals(rewrite, new MatchNoneQueryBuilder());
125+
}
120126
}

0 commit comments

Comments
 (0)