From 5bf876db9d9cf50d56b6c74d3c9d7aa895b5ffbc Mon Sep 17 00:00:00 2001 From: Simon Willnauer Date: Tue, 11 Jul 2017 16:58:02 +0200 Subject: [PATCH] Ensure we rewrite common queries to `match_none` if possible 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. --- .../index/query/BoolQueryBuilder.java | 10 +++++++++- .../query/ConstantScoreQueryBuilder.java | 3 +++ .../index/query/BoolQueryBuilderTests.java | 19 +++++++++++++++++++ .../query/ConstantScoreQueryBuilderTests.java | 6 ++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java index d20361419c7d5..ac57c2abd585b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java @@ -38,7 +38,10 @@ 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 @@ protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws 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 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; diff --git a/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java index 324759c9c6761..f1e3f7aea7780 100644 --- a/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java @@ -155,6 +155,9 @@ protected boolean doEquals(ConstantScoreQueryBuilder other) { @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); } diff --git a/core/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java index 17cb4d0c4f759..750760a6f30b0 100644 --- a/core/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java @@ -418,4 +418,23 @@ public void testRewriteMultipleTimes() throws IOException { 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); + } } diff --git a/core/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java index 3e4f0caabad65..08d234cc54bb3 100644 --- a/core/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java @@ -117,4 +117,10 @@ public void testFromJson() throws IOException { 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()); + } }