diff --git a/core/src/main/java/org/elasticsearch/cluster/metadata/AliasValidator.java b/core/src/main/java/org/elasticsearch/cluster/metadata/AliasValidator.java index 7df82adc2c6..6b1c64c8f0e 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/AliasValidator.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/AliasValidator.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.indices.InvalidAliasNameException; import java.io.IOException; @@ -143,7 +144,7 @@ public class AliasValidator extends AbstractComponent { private static void validateAliasFilter(XContentParser parser, QueryShardContext queryShardContext) throws IOException { QueryBuilder parseInnerQueryBuilder = parseInnerQueryBuilder(parser); - QueryBuilder queryBuilder = QueryBuilder.rewriteQuery(parseInnerQueryBuilder, queryShardContext); + QueryBuilder queryBuilder = Rewriteable.rewrite(parseInnerQueryBuilder, queryShardContext); queryBuilder.toFilter(queryShardContext); } } diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/QueryBuilder.java index 7c6b332f4aa..b6ee368b488 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryBuilder.java @@ -25,7 +25,7 @@ import org.elasticsearch.common.xcontent.ToXContentObject; import java.io.IOException; -public interface QueryBuilder extends NamedWriteable, ToXContentObject { +public interface QueryBuilder extends NamedWriteable, ToXContentObject, Rewriteable { /** * Converts this QueryBuilder to a lucene {@link Query}. @@ -86,20 +86,4 @@ public interface QueryBuilder extends NamedWriteable, ToXContentObject { default QueryBuilder rewrite(QueryRewriteContext queryShardContext) throws IOException { return this; } - - /** - * Rewrites the given query into its primitive form. Queries that for instance fetch resources from remote hosts or - * can simplify / optimize itself should do their heavy lifting during {@link #rewrite(QueryRewriteContext)}. This method - * rewrites the query until it doesn't change anymore. - * @throws IOException if an {@link IOException} occurs - */ - static QueryBuilder rewriteQuery(QueryBuilder original, QueryRewriteContext context) throws IOException { - QueryBuilder builder = original; - for (QueryBuilder rewrittenBuilder = builder.rewrite(context); rewrittenBuilder != builder; - rewrittenBuilder = builder.rewrite(context)) { - builder = rewrittenBuilder; - } - return builder; - } - } diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java index cd7c87c4de1..8f1615cde7d 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java @@ -304,7 +304,7 @@ public class QueryShardContext extends QueryRewriteContext { private ParsedQuery toQuery(QueryBuilder queryBuilder, CheckedFunction filterOrQuery) { reset(); try { - QueryBuilder rewriteQuery = QueryBuilder.rewriteQuery(queryBuilder, this); + QueryBuilder rewriteQuery = Rewriteable.rewrite(queryBuilder, this); return new ParsedQuery(filterOrQuery.apply(rewriteQuery), copyNamedQueries()); } catch(QueryShardException | ParsingException e ) { throw e; diff --git a/core/src/main/java/org/elasticsearch/index/query/Rewriteable.java b/core/src/main/java/org/elasticsearch/index/query/Rewriteable.java new file mode 100644 index 00000000000..2e935817231 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/index/query/Rewriteable.java @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.index.query; + +import java.io.IOException; + +/** + * A basic interface for rewriteable classes. + */ +public interface Rewriteable { + + /** + * Rewrites this instance based on the provided context. The returned + * objects will be the same instance as this if no changes during the + * rewrite were applied. + */ + T rewrite(QueryRewriteContext ctx) throws IOException; + + /** + * Rewrites the given {@link Rewriteable} into its primitive form. Rewriteables that for instance fetch resources from remote hosts or + * can simplify / optimize itself should do their heavy lifting during {@link #rewrite(QueryRewriteContext)}. This method + * rewrites the rewriteable until it doesn't change anymore. + * @throws IOException if an {@link IOException} occurs + */ + static > T rewrite(T original, QueryRewriteContext context) throws IOException { + T builder = original; + for (T rewrittenBuilder = builder.rewrite(context); rewrittenBuilder != builder; + rewrittenBuilder = builder.rewrite(context)) { + builder = rewrittenBuilder; + } + return builder; + } +} diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java index 1a9e5904579..10ec2905943 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/adjacency/AdjacencyMatrixAggregationBuilder.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; @@ -195,7 +196,7 @@ public class AdjacencyMatrixAggregationBuilder extends AbstractAggregationBuilde List rewrittenFilters = new ArrayList<>(filters.size()); for (KeyedFilter kf : filters) { - rewrittenFilters.add(new KeyedFilter(kf.key(), QueryBuilder.rewriteQuery(kf.filter(), context.getQueryShardContext()))); + rewrittenFilters.add(new KeyedFilter(kf.key(), Rewriteable.rewrite(kf.filter(), context.getQueryShardContext()))); } return new AdjacencyMatrixAggregatorFactory(name, rewrittenFilters, separator, context, parent, diff --git a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterAggregationBuilder.java b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterAggregationBuilder.java index a26dc67ce9d..705501b22f2 100644 --- a/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterAggregationBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/aggregations/bucket/filter/FilterAggregationBuilder.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryRewriteContext; +import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregatorFactories; @@ -72,7 +73,7 @@ public class FilterAggregationBuilder extends AbstractAggregationBuilder rewrittenFilters = new ArrayList<>(filters.size()); boolean changed = false; for (KeyedFilter kf : filters) { - QueryBuilder result = QueryBuilder.rewriteQuery(kf.filter(), queryShardContext); + QueryBuilder result = Rewriteable.rewrite(kf.filter(), queryShardContext); rewrittenFilters.add(new KeyedFilter(kf.key(), result)); if (result != kf.filter()) { changed = true; diff --git a/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java index aa5a5baa0ff..c55fa7f793b 100644 --- a/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -36,7 +36,9 @@ import org.elasticsearch.common.xcontent.ToXContentObject; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.script.Script; import org.elasticsearch.search.SearchExtBuilder; import org.elasticsearch.search.aggregations.AggregationBuilder; @@ -71,7 +73,7 @@ import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQuery * * @see org.elasticsearch.action.search.SearchRequest#source(SearchSourceBuilder) */ -public final class SearchSourceBuilder extends ToXContentToBytes implements Writeable, ToXContentObject { +public final class SearchSourceBuilder extends ToXContentToBytes implements Writeable, ToXContentObject, Rewriteable { private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(SearchSourceBuilder.class)); @@ -870,7 +872,8 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ * reference must be returned otherwise the builder will be rewritten * infinitely. */ - public SearchSourceBuilder rewrite(QueryShardContext context) throws IOException { + @Override + public SearchSourceBuilder rewrite(QueryRewriteContext context) throws IOException { assert (this.equals(shallowCopy(queryBuilder, postQueryBuilder, aggregations, sliceBuilder))); QueryBuilder queryBuilder = null; if (this.queryBuilder != null) { @@ -901,7 +904,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ /** * Create a shallow copy of this source replaced {@link #queryBuilder}, {@link #postQueryBuilder}, and {@link #sliceBuilder}. Used by - * {@link #rewrite(QueryShardContext)} and {@link #copyWithNewSlice(SliceBuilder)}. + * {@link #rewrite(QueryRewriteContext)} and {@link #copyWithNewSlice(SliceBuilder)}. */ private SearchSourceBuilder shallowCopy(QueryBuilder queryBuilder, QueryBuilder postQueryBuilder, AggregatorFactories.Builder aggregations, SliceBuilder slice) { diff --git a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java index ff45b9889fe..62985aaedad 100644 --- a/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilder.java @@ -31,6 +31,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.search.fetch.subphase.highlight.SearchContextHighlight.FieldOptions; import java.io.IOException; @@ -357,7 +358,7 @@ public class HighlightBuilder extends AbstractHighlighterBuilder { private final String[] aliases; private final QueryBuilder filter; @@ -50,12 +51,16 @@ public final class AliasFilter implements Writeable { filter = input.readOptionalNamedWriteable(QueryBuilder.class); } - AliasFilter rewrite(QueryRewriteContext context) throws IOException { + @Override + public AliasFilter rewrite(QueryRewriteContext context) throws IOException { QueryBuilder queryBuilder = this.filter; if (queryBuilder != null) { - return new AliasFilter(QueryBuilder.rewriteQuery(queryBuilder, context), aliases); + QueryBuilder rewrite = Rewriteable.rewrite(queryBuilder, context); + if (rewrite != queryBuilder) { + return new AliasFilter(rewrite, aliases); + } } - return new AliasFilter(filter, aliases); + return this; } @Override diff --git a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java index 5a8d91fbaa8..77c520b3e47 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java +++ b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchLocalRequest.java @@ -29,7 +29,9 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.Scroll; import org.elasticsearch.search.builder.SearchSourceBuilder; @@ -236,15 +238,9 @@ public class ShardSearchLocalRequest implements ShardSearchRequest { } @Override - public void rewrite(QueryShardContext context) throws IOException { - SearchSourceBuilder source = this.source; - SearchSourceBuilder rewritten = null; - aliasFilter = aliasFilter.rewrite(context); - while (rewritten != source) { - rewritten = source.rewrite(context); - source = rewritten; - } - this.source = source; + public void rewrite(QueryRewriteContext context) throws IOException { + aliasFilter = Rewriteable.rewrite(aliasFilter, context); + source = source == null ? null : Rewriteable.rewrite(source, context); } @Override diff --git a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java index 222e19894c4..70e48d7eb87 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java +++ b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchRequest.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.collect.ImmutableOpenMap; import org.elasticsearch.index.Index; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.indices.AliasFilterParsingException; @@ -87,7 +88,7 @@ public interface ShardSearchRequest { * Rewrites this request into its primitive form. e.g. by rewriting the * QueryBuilder. */ - void rewrite(QueryShardContext context) throws IOException; + void rewrite(QueryRewriteContext context) throws IOException; /** * Returns the filter associated with listed filtering aliases. diff --git a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchTransportRequest.java b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchTransportRequest.java index 95f2f26e9ae..cd8fa4b9696 100644 --- a/core/src/main/java/org/elasticsearch/search/internal/ShardSearchTransportRequest.java +++ b/core/src/main/java/org/elasticsearch/search/internal/ShardSearchTransportRequest.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.Scroll; @@ -167,7 +168,7 @@ public class ShardSearchTransportRequest extends TransportRequest implements Sha } @Override - public void rewrite(QueryShardContext context) throws IOException { + public void rewrite(QueryRewriteContext context) throws IOException { shardSearchLocalRequest.rewrite(context); } diff --git a/core/src/main/java/org/elasticsearch/search/rescore/QueryRescorerBuilder.java b/core/src/main/java/org/elasticsearch/search/rescore/QueryRescorerBuilder.java index 8a02841f343..94e944f9dc6 100644 --- a/core/src/main/java/org/elasticsearch/search/rescore/QueryRescorerBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/rescore/QueryRescorerBuilder.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.search.rescore.QueryRescorer.QueryRescoreContext; import java.io.IOException; @@ -170,7 +171,7 @@ public class QueryRescorerBuilder extends RescoreBuilder { public QueryRescoreContext build(QueryShardContext context) throws IOException { org.elasticsearch.search.rescore.QueryRescorer rescorer = new org.elasticsearch.search.rescore.QueryRescorer(); QueryRescoreContext queryRescoreContext = new QueryRescoreContext(rescorer); - queryRescoreContext.setQuery(QueryBuilder.rewriteQuery(this.queryBuilder, context).toQuery(context)); + queryRescoreContext.setQuery(Rewriteable.rewrite(this.queryBuilder, context).toQuery(context)); queryRescoreContext.setQueryWeight(this.queryWeight); queryRescoreContext.setRescoreQueryWeight(this.rescoreQueryWeight); queryRescoreContext.setScoreMode(this.scoreMode); diff --git a/core/src/main/java/org/elasticsearch/search/sort/SortBuilder.java b/core/src/main/java/org/elasticsearch/search/sort/SortBuilder.java index 984e6b8a983..25bee3a57cd 100644 --- a/core/src/main/java/org/elasticsearch/search/sort/SortBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/sort/SortBuilder.java @@ -34,6 +34,7 @@ import org.elasticsearch.index.mapper.ObjectMapper; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.index.query.Rewriteable; import org.elasticsearch.search.DocValueFormat; import java.io.IOException; @@ -189,7 +190,7 @@ public abstract class SortBuilder> extends ToXContentTo Query innerDocumentsQuery; if (nestedFilter != null) { context.nestedScope().nextLevel(nestedObjectMapper); - innerDocumentsQuery = QueryBuilder.rewriteQuery(nestedFilter, context).toFilter(context); + innerDocumentsQuery = Rewriteable.rewrite(nestedFilter, context).toFilter(context); context.nestedScope().previousLevel(); } else { innerDocumentsQuery = nestedObjectMapper.nestedTypeFilter(); diff --git a/core/src/test/java/org/elasticsearch/index/SearchSlowLogTests.java b/core/src/test/java/org/elasticsearch/index/SearchSlowLogTests.java index 46d24d74818..cebbee41d81 100644 --- a/core/src/test/java/org/elasticsearch/index/SearchSlowLogTests.java +++ b/core/src/test/java/org/elasticsearch/index/SearchSlowLogTests.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.BigArrays; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.search.Scroll; @@ -125,7 +126,7 @@ public class SearchSlowLogTests extends ESSingleNodeTestCase { } @Override - public void rewrite(QueryShardContext context) throws IOException { + public void rewrite(QueryRewriteContext context) throws IOException { } @Override 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 750760a6f30..a417cba13b9 100644 --- a/core/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java @@ -416,7 +416,7 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase> } private QueryBuilder rewriteQuery(QB queryBuilder, QueryRewriteContext rewriteContext) throws IOException { - QueryBuilder rewritten = QueryBuilder.rewriteQuery(queryBuilder, rewriteContext); + QueryBuilder rewritten = Rewriteable.rewrite(queryBuilder, rewriteContext); // extra safety to fail fast - serialize the rewritten version to ensure it's serializable. assertSerialization(rewritten); return rewritten;