Skip to content

Commit fd1553c

Browse files
committed
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 0bcf0f4 commit fd1553c

File tree

4 files changed

+35
-1
lines changed

4 files changed

+35
-1
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import java.util.Objects;
4040
import java.util.Optional;
4141
import java.util.function.Consumer;
42+
import java.util.stream.Stream;
43+
import java.util.stream.StreamSupport;
4244

4345
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
4446

@@ -479,7 +481,12 @@ protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws
479481
changed |= rewriteClauses(queryRewriteContext, mustNotClauses, newBuilder::mustNot);
480482
changed |= rewriteClauses(queryRewriteContext, filterClauses, newBuilder::filter);
481483
changed |= rewriteClauses(queryRewriteContext, shouldClauses, newBuilder::should);
482-
484+
// lets do some early termination and prevent any kind of rewriting if we have a mandatory query that is a MatchNoneQueryBuilder
485+
Optional<QueryBuilder> any = Stream.concat(newBuilder.mustClauses.stream(), newBuilder.filterClauses.stream())
486+
.filter(b -> b instanceof MatchNoneQueryBuilder).findAny();
487+
if (any.isPresent()) {
488+
return any.get();
489+
}
483490
if (changed) {
484491
newBuilder.adjustPureNegative = adjustPureNegative;
485492
newBuilder.disableCoord = disableCoord;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ protected boolean doEquals(ConstantScoreQueryBuilder other) {
165165
@Override
166166
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
167167
QueryBuilder rewrite = filterBuilder.rewrite(queryRewriteContext);
168+
if (rewrite instanceof MatchNoneQueryBuilder) {
169+
return rewrite; // we won't match anyway
170+
}
168171
if (rewrite != filterBuilder) {
169172
return new ConstantScoreQueryBuilder(rewrite);
170173
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,4 +470,23 @@ public void testRewriteMultipleTimes() throws IOException {
470470
assertEquals(rewrittenAgain, expected);
471471
assertEquals(QueryBuilder.rewriteQuery(boolQueryBuilder, createShardContext()), expected);
472472
}
473+
474+
public void testRewriteWithMatchNone() throws IOException {
475+
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
476+
boolQueryBuilder.must(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchNoneQueryBuilder().toString()).toString()));
477+
QueryBuilder rewritten = boolQueryBuilder.rewrite(createShardContext());
478+
assertEquals(new MatchNoneQueryBuilder(), rewritten);
479+
480+
boolQueryBuilder = new BoolQueryBuilder();
481+
boolQueryBuilder.must(new TermQueryBuilder("foo","bar"));
482+
boolQueryBuilder.filter(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchNoneQueryBuilder().toString()).toString()));
483+
rewritten = boolQueryBuilder.rewrite(createShardContext());
484+
assertEquals(new MatchNoneQueryBuilder(), rewritten);
485+
486+
boolQueryBuilder = new BoolQueryBuilder();
487+
boolQueryBuilder.must(new TermQueryBuilder("foo","bar"));
488+
boolQueryBuilder.filter(new BoolQueryBuilder().should(new TermQueryBuilder("foo","bar")).filter(new MatchNoneQueryBuilder()));
489+
rewritten = QueryBuilder.rewriteQuery(boolQueryBuilder, createShardContext());
490+
assertEquals(new MatchNoneQueryBuilder(), rewritten);
491+
}
473492
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,9 @@ public void testFromJsonEmptyQueryBody() throws IOException {
134134
assertWarnings("query malformed, empty clause found at [1:40]");
135135
}
136136

137+
public void testRewriteToMatchNone() throws IOException {
138+
ConstantScoreQueryBuilder constantScoreQueryBuilder = new ConstantScoreQueryBuilder(new MatchNoneQueryBuilder());
139+
QueryBuilder rewrite = constantScoreQueryBuilder.rewrite(createShardContext());
140+
assertEquals(rewrite, new MatchNoneQueryBuilder());
141+
}
137142
}

0 commit comments

Comments
 (0)