diff options
Diffstat (limited to 'core')
38 files changed, 414 insertions, 149 deletions
diff --git a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java index c106cd1d4e..77afd12364 100644 --- a/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java +++ b/core/src/main/java/org/elasticsearch/action/search/TransportSearchAction.java @@ -33,6 +33,7 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.IndexNotFoundException; import org.elasticsearch.indices.IndexClosedException; +import org.elasticsearch.search.SearchService; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportService; @@ -90,7 +91,6 @@ public class TransportSearchAction extends HandledTransportAction<SearchRequest, logger.debug("failed to optimize search type, continue as normal", e); } } - if (searchRequest.searchType() == DFS_QUERY_THEN_FETCH) { dfsQueryThenFetchAction.execute(searchRequest, listener); } else if (searchRequest.searchType() == SearchType.QUERY_THEN_FETCH) { 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 091fde6dec..e60bafedd6 100644 --- a/core/src/main/java/org/elasticsearch/cluster/metadata/AliasValidator.java +++ b/core/src/main/java/org/elasticsearch/cluster/metadata/AliasValidator.java @@ -27,6 +27,8 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.Index; +import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.indices.InvalidAliasNameException; @@ -143,7 +145,9 @@ public class AliasValidator extends AbstractComponent { private void validateAliasFilter(XContentParser parser, QueryShardContext queryShardContext) throws IOException { try { queryShardContext.reset(parser); - queryShardContext.parseContext().parseInnerQueryBuilder().toFilter(queryShardContext); + QueryParseContext queryParseContext = queryShardContext.parseContext(); + QueryBuilder<?> queryBuilder = QueryBuilder.rewriteQuery(queryParseContext.parseInnerQueryBuilder(), queryShardContext); + queryBuilder.toFilter(queryShardContext); } finally { queryShardContext.reset(null); parser.close(); diff --git a/core/src/main/java/org/elasticsearch/index/IndexService.java b/core/src/main/java/org/elasticsearch/index/IndexService.java index 9ffb6c4d56..0048f2a98a 100644 --- a/core/src/main/java/org/elasticsearch/index/IndexService.java +++ b/core/src/main/java/org/elasticsearch/index/IndexService.java @@ -421,7 +421,7 @@ public final class IndexService extends AbstractIndexComponent implements IndexC * Creates a new QueryShardContext. The context has not types set yet, if types are required set them via {@link QueryShardContext#setTypes(String...)} */ public QueryShardContext newQueryShardContext() { - return new QueryShardContext(indexSettings, nodeServicesProvider.getClient(), indexCache.bitsetFilterCache(), indexFieldData, mapperService(), similarityService(), nodeServicesProvider.getScriptService(), nodeServicesProvider.getIndicesQueriesRegistry()); + return new QueryShardContext(indexSettings, indexCache.bitsetFilterCache(), indexFieldData, mapperService(), similarityService(), nodeServicesProvider.getScriptService(), nodeServicesProvider.getIndicesQueriesRegistry()); } ThreadPool getThreadPool() { 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 f7f4926d95..72e13c2c31 100644 --- a/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/BoolQueryBuilder.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.function.Consumer; import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded; @@ -346,4 +347,40 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> { out.writeBoolean(disableCoord); out.writeOptionalString(minimumShouldMatch); } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryRewriteContext) throws IOException { + BoolQueryBuilder newBuilder = new BoolQueryBuilder(); + boolean changed = false; + final int clauses = mustClauses.size() + mustNotClauses.size() + filterClauses.size() + shouldClauses.size(); + if (clauses == 0) { + return new MatchAllQueryBuilder().boost(boost()).queryName(queryName()); + } + changed |= rewriteClauses(queryRewriteContext, mustClauses, newBuilder::must); + changed |= rewriteClauses(queryRewriteContext, mustNotClauses, newBuilder::mustNot); + changed |= rewriteClauses(queryRewriteContext, filterClauses, newBuilder::filter); + changed |= rewriteClauses(queryRewriteContext, shouldClauses, newBuilder::should); + + if (changed) { + newBuilder.adjustPureNegative = adjustPureNegative; + newBuilder.disableCoord = disableCoord; + newBuilder.minimumShouldMatch = minimumShouldMatch; + newBuilder.boost(boost()); + newBuilder.queryName(queryName()); + return newBuilder; + } + return this; + } + + private static boolean rewriteClauses(QueryRewriteContext queryRewriteContext, List<QueryBuilder<?>> builders, Consumer<QueryBuilder<?>> conusmer) throws IOException { + boolean changed = false; + for (QueryBuilder builder : builders) { + QueryBuilder result = builder.rewrite(queryRewriteContext); + if (result != builder) { + changed = true; + } + conusmer.accept(result); + } + return changed; + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/BoostingQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/BoostingQueryBuilder.java index 50346c2d36..507e85687d 100644 --- a/core/src/main/java/org/elasticsearch/index/query/BoostingQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/BoostingQueryBuilder.java @@ -158,4 +158,17 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil out.writeQuery(negativeQuery); out.writeFloat(negativeBoost); } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + QueryBuilder positiveQuery = this.positiveQuery.rewrite(queryShardContext); + QueryBuilder negativeQuery = this.negativeQuery.rewrite(queryShardContext); + if (positiveQuery != this.positiveQuery || negativeQuery != this.negativeQuery) { + BoostingQueryBuilder newQueryBuilder = new BoostingQueryBuilder(positiveQuery, negativeQuery) + .boost(boost()).queryName(queryName()); + newQueryBuilder.negativeBoost = negativeBoost; + return newQueryBuilder; + } + return this; + } } 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 4b054a34d4..ad00bba28b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/ConstantScoreQueryBuilder.java @@ -104,4 +104,13 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor protected void doWriteTo(StreamOutput out) throws IOException { out.writeQuery(filterBuilder); } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + QueryBuilder rewrite = filterBuilder.rewrite(queryShardContext); + if (rewrite != filterBuilder) { + return new ConstantScoreQueryBuilder(rewrite).boost(boost()).queryName(queryName()); + } + return this; + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/EmptyQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/EmptyQueryBuilder.java index 7d1761a44b..83f7abae7a 100644 --- a/core/src/main/java/org/elasticsearch/index/query/EmptyQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/EmptyQueryBuilder.java @@ -23,10 +23,12 @@ import org.apache.lucene.search.Query; import org.elasticsearch.action.support.ToXContentToBytes; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentType; import java.io.IOException; +import java.util.Objects; /** * A {@link QueryBuilder} that is a stand in replacement for an empty query clause in the DSL. @@ -37,75 +39,49 @@ import java.io.IOException; * intended to be used internally as a stand-in for nested queries that are left empty and should * be ignored upstream. */ -public class EmptyQueryBuilder extends ToXContentToBytes implements QueryBuilder<EmptyQueryBuilder> { +public final class EmptyQueryBuilder extends AbstractQueryBuilder<EmptyQueryBuilder> { public static final String NAME = "empty_query"; /** the one and only empty query builder */ public static final EmptyQueryBuilder PROTOTYPE = new EmptyQueryBuilder(); - // prevent instances other than prototype - private EmptyQueryBuilder() { - super(XContentType.JSON); - } - @Override public String getWriteableName() { return NAME; } @Override - public String getName() { - return getWriteableName(); - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - builder.startObject(); - builder.endObject(); - return builder; - } - - @Override - public Query toQuery(QueryShardContext context) throws IOException { - // empty + protected Query doToQuery(QueryShardContext context) throws IOException { return null; } @Override - public Query toFilter(QueryShardContext context) throws IOException { - // empty - return null; + public String getName() { + return getWriteableName(); } @Override - public void writeTo(StreamOutput out) throws IOException { + protected void doXContent(XContentBuilder builder, Params params) throws IOException { } @Override - public EmptyQueryBuilder readFrom(StreamInput in) throws IOException { - return EmptyQueryBuilder.PROTOTYPE; + protected void doWriteTo(StreamOutput out) throws IOException { } - @Override - public EmptyQueryBuilder queryName(String queryName) { - //no-op - return this; - } @Override - public String queryName() { - return null; + protected EmptyQueryBuilder doReadFrom(StreamInput in) throws IOException { + return new EmptyQueryBuilder(); } @Override - public float boost() { - return -1; + protected int doHashCode() { + return 31; } @Override - public EmptyQueryBuilder boost(float boost) { - //no-op - return this; + protected boolean doEquals(EmptyQueryBuilder other) { + return true; } } diff --git a/core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java index f7d8b22d78..db64aa3a7d 100644 --- a/core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/GeoShapeQueryBuilder.java @@ -43,7 +43,6 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper; -import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.Objects; @@ -62,7 +61,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil private final String fieldName; - private ShapeBuilder shape; + private final ShapeBuilder shape; private SpatialStrategy strategy; @@ -237,12 +236,11 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil @Override protected Query doToQuery(QueryShardContext context) throws IOException { - ShapeBuilder shapeToQuery = shape; - if (shapeToQuery == null) { - GetRequest getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId); - shapeToQuery = fetch(context.getClient(), getRequest, indexedShapePath); + if (shape == null) { + throw new UnsupportedOperationException("query must be rewritten first"); } - MappedFieldType fieldType = context.fieldMapper(fieldName); + final ShapeBuilder shapeToQuery = shape; + final MappedFieldType fieldType = context.fieldMapper(fieldName); if (fieldType == null) { throw new QueryShardException(context, "Failed to find geo_shape field [" + fieldName + "]"); } @@ -252,7 +250,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil throw new QueryShardException(context, "Field [" + fieldName + "] is not a geo_shape"); } - GeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (GeoShapeFieldMapper.GeoShapeFieldType) fieldType; + final GeoShapeFieldMapper.GeoShapeFieldType shapeFieldType = (GeoShapeFieldMapper.GeoShapeFieldType) fieldType; PrefixTreeStrategy strategy = shapeFieldType.defaultStrategy(); if (this.strategy != null) { @@ -449,4 +447,15 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil public String getWriteableName() { return NAME; } + + @Override + public QueryBuilder<GeoShapeQueryBuilder> rewrite(QueryRewriteContext queryShardContext) throws IOException { + if (this.shape == null) { + GetRequest getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId); + ShapeBuilder shape = fetch(queryShardContext.getClient(), getRequest, indexedShapePath); + return new GeoShapeQueryBuilder(this.fieldName, shape).relation(relation).strategy(strategy) + .boost(boost()).queryName(queryName()); + } + return this; + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java index c84883fe73..66c9d77534 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasChildQueryBuilder.java @@ -26,7 +26,6 @@ import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.join.JoinUtil; import org.apache.lucene.search.join.ScoreMode; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.search.Queries; @@ -397,4 +396,18 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil out.writeBoolean(false); } } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + QueryBuilder rewrite = query.rewrite(queryShardContext); + if (rewrite != query) { + HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder(type, rewrite); + hasChildQueryBuilder.minChildren = minChildren; + hasChildQueryBuilder.maxChildren = maxChildren; + hasChildQueryBuilder.scoreMode = scoreMode; + hasChildQueryBuilder.queryInnerHits = queryInnerHits; + return hasChildQueryBuilder.queryName(queryName()).boost(boost()); + } + return this; + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java index 173d6aa00c..10ed269a2b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/HasParentQueryBuilder.java @@ -22,7 +22,6 @@ import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.join.ScoreMode; -import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.search.Queries; @@ -256,4 +255,16 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu protected int doHashCode() { return Objects.hash(query, type, score, innerHit); } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + QueryBuilder rewrite = query.rewrite(queryShardContext); + if (rewrite != query) { + HasParentQueryBuilder hasParentQueryBuilder = new HasParentQueryBuilder(type, rewrite); + hasParentQueryBuilder.score = score; + hasParentQueryBuilder.innerHit = innerHit; + return hasParentQueryBuilder.queryName(queryName()).boost(boost()); + } + return this; + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java index 5185dfda3b..c366394976 100644 --- a/core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/IndicesQueryBuilder.java @@ -140,4 +140,14 @@ public class IndicesQueryBuilder extends AbstractQueryBuilder<IndicesQueryBuilde Arrays.equals(indices, other.indices) && // otherwise we are comparing pointers Objects.equals(noMatchQuery, other.noMatchQuery); } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + QueryBuilder<?> newInnnerQuery = innerQuery.rewrite(queryShardContext); + QueryBuilder<?> newNoMatchQuery = noMatchQuery.rewrite(queryShardContext); + if (newInnnerQuery != innerQuery || newNoMatchQuery != noMatchQuery) { + return new IndicesQueryBuilder(innerQuery, indices).noMatchQuery(noMatchQuery).boost(boost()).queryName(queryName()); + } + return this; + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java index 0e98b2042a..c08cf205e6 100644 --- a/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java @@ -1050,4 +1050,10 @@ public class MoreLikeThisQueryBuilder extends AbstractQueryBuilder<MoreLikeThisQ Objects.equals(include, other.include) && Objects.equals(failOnUnsupportedField, other.failOnUnsupportedField); } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + // TODO this needs heavy cleanups before we can rewrite it + return this; + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java index 103f957b24..461ebdb52a 100644 --- a/core/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java @@ -225,4 +225,12 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder> return new ToParentBlockJoinQuery(Queries.filtered(innerQuery, childFilter), parentFilter, scoreMode); } + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + QueryBuilder rewrite = query.rewrite(queryShardContext); + if (rewrite != query) { + return new NestedQueryBuilder(path, rewrite).queryName(queryName()).boost(boost()).scoreMode(scoreMode); + } + return this; + } } 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 b75406c864..3667905bac 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryBuilder.java @@ -72,4 +72,28 @@ public interface QueryBuilder<QB extends QueryBuilder<QB>> extends NamedWriteabl * Returns the name that identifies uniquely the query */ String getName(); + + /** + * Rewrites this query builder into it's primitive form. By default this method return theb builder itself. If the builder + * did not change the identity reference must be returend otherwise the builder will be rewritten infinitely. + */ + default QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + return this; + } + + /** + * Rewrites the given query into it's primitive form. Queries that for instance fetch resources from remote hosts or + * can simplify / optimize itself should do their heavy lifting duringt {@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/QueryParseContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryParseContext.java index 78d76d8292..439e6533de 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryParseContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryParseContext.java @@ -106,7 +106,7 @@ public class QueryParseContext { token = parser.nextToken(); if (token == XContentParser.Token.END_OBJECT) { // empty query - return EmptyQueryBuilder.PROTOTYPE; + return new EmptyQueryBuilder(); } if (token != XContentParser.Token.FIELD_NAME) { throw new ParsingException(parser.getTokenLocation(), "[_na] query malformed, no field after start_object"); diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java new file mode 100644 index 0000000000..425673cb54 --- /dev/null +++ b/core/src/main/java/org/elasticsearch/index/query/QueryRewriteContext.java @@ -0,0 +1,63 @@ +/* + * 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 org.elasticsearch.client.Client; +import org.elasticsearch.index.IndexSettings; +import org.elasticsearch.indices.query.IndicesQueriesRegistry; +import org.elasticsearch.script.ScriptService; + +/** + */ +public class QueryRewriteContext { + protected final ScriptService scriptService; + protected final IndexSettings indexSettings; + protected final IndicesQueriesRegistry indicesQueriesRegistry; + protected final QueryParseContext parseContext; + + public QueryRewriteContext(IndexSettings indexSettings, ScriptService scriptService, IndicesQueriesRegistry indicesQueriesRegistry) { + this.scriptService = scriptService; + this.indexSettings = indexSettings; + this.indicesQueriesRegistry = indicesQueriesRegistry; + this.parseContext = new QueryParseContext(indicesQueriesRegistry); + } + + public final Client getClient() { + return scriptService.getClient(); + } + + public final IndexSettings getIndexSettings() { + return indexSettings; + } + + public final ScriptService getScriptService() { + return scriptService; + } + + public QueryParseContext newParseContext() { + QueryParseContext queryParseContext = new QueryParseContext(indicesQueriesRegistry); + queryParseContext.parseFieldMatcher(parseContext.parseFieldMatcher()); + return queryParseContext; + } + + public boolean hasIndex() { + return indexSettings != null; + } + +} 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 4701cdfeec..0e3348ac7a 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java @@ -26,7 +26,6 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.join.BitSetProducer; import org.apache.lucene.search.similarities.Similarity; import org.elasticsearch.Version; -import org.elasticsearch.client.Client; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParsingException; @@ -52,10 +51,7 @@ import org.elasticsearch.index.query.support.InnerHitsQueryParserHelper; import org.elasticsearch.index.query.support.NestedScope; import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.indices.query.IndicesQueriesRegistry; -import org.elasticsearch.script.ExecutableScript; -import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; -import org.elasticsearch.script.Template; import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; import org.elasticsearch.search.fetch.innerhits.InnerHitsSubSearchContext; import org.elasticsearch.search.internal.SearchContext; @@ -64,7 +60,6 @@ import org.elasticsearch.search.lookup.SearchLookup; import java.io.IOException; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -73,15 +68,13 @@ import static java.util.Collections.unmodifiableMap; /** * Context object used to create lucene queries on the shard level. */ -public class QueryShardContext { +public class QueryShardContext extends QueryRewriteContext { private final MapperService mapperService; - private final ScriptService scriptService; private final SimilarityService similarityService; private final BitsetFilterCache bitsetFilterCache; private final IndexFieldDataService indexFieldDataService; private final IndexSettings indexSettings; - private final Client client; private String[] types = Strings.EMPTY_ARRAY; public void setTypes(String... types) { @@ -94,35 +87,31 @@ public class QueryShardContext { private final Map<String, Query> namedQueries = new HashMap<>(); private final MapperQueryParser queryParser = new MapperQueryParser(this); - private final IndicesQueriesRegistry indicesQueriesRegistry; private boolean allowUnmappedFields; private boolean mapUnmappedFieldAsString; private NestedScope nestedScope; - private QueryParseContext parseContext; boolean isFilter; // pkg private for testing - public QueryShardContext(IndexSettings indexSettings, Client client, BitsetFilterCache bitsetFilterCache, IndexFieldDataService indexFieldDataService, MapperService mapperService, SimilarityService similarityService, ScriptService scriptService, + public QueryShardContext(IndexSettings indexSettings, BitsetFilterCache bitsetFilterCache, IndexFieldDataService indexFieldDataService, MapperService mapperService, SimilarityService similarityService, ScriptService scriptService, final IndicesQueriesRegistry indicesQueriesRegistry) { + super(indexSettings, scriptService, indicesQueriesRegistry); this.indexSettings = indexSettings; - this.scriptService = scriptService; - this.client = client; this.similarityService = similarityService; this.mapperService = mapperService; this.bitsetFilterCache = bitsetFilterCache; this.indexFieldDataService = indexFieldDataService; this.allowUnmappedFields = indexSettings.isDefaultAllowUnmappedFields(); - this.indicesQueriesRegistry = indicesQueriesRegistry; - this.parseContext = new QueryParseContext(indicesQueriesRegistry); + } public QueryShardContext(QueryShardContext source) { - this(source.indexSettings, source.client, source.bitsetFilterCache, source.indexFieldDataService, source.mapperService, source.similarityService, source.scriptService, source.indicesQueriesRegistry); + this(source.indexSettings, source.bitsetFilterCache, source.indexFieldDataService, source.mapperService, source.similarityService, source.scriptService, source.indicesQueriesRegistry); this.types = source.getTypes(); } public QueryShardContext clone() { - return new QueryShardContext(indexSettings, client, bitsetFilterCache, indexFieldDataService, mapperService, similarityService, scriptService, indicesQueriesRegistry); + return new QueryShardContext(indexSettings, bitsetFilterCache, indexFieldDataService, mapperService, similarityService, scriptService, indicesQueriesRegistry); } public void parseFieldMatcher(ParseFieldMatcher parseFieldMatcher) { @@ -147,10 +136,6 @@ public class QueryShardContext { this.parseContext.reset(jp); } - public Index index() { - return this.mapperService.getIndexSettings().getIndex(); - } - public InnerHitsSubSearchContext getInnerHitsContext(XContentParser parser) throws IOException { return InnerHitsQueryParserHelper.parse(parser); } @@ -159,10 +144,6 @@ public class QueryShardContext { return mapperService.analysisService(); } - public ScriptService getScriptService() { - return scriptService; - } - public MapperService getMapperService() { return mapperService; } @@ -341,18 +322,6 @@ public class QueryShardContext { return false; } - /* - * Executes the given template, and returns the response. - */ - public BytesReference executeQueryTemplate(Template template) { - ExecutableScript executable = getScriptService().executable(template, ScriptContext.Standard.SEARCH, Collections.emptyMap()); - return (BytesReference) executable.run(); - } - - public Client getClient() { - return client; - } - public ParsedQuery parse(BytesReference source) { XContentParser parser = null; try { @@ -385,7 +354,7 @@ public class QueryShardContext { reset(parser); try { parseFieldMatcher(indexSettings.getParseFieldMatcher()); - Query filter = parseContext().parseInnerQueryBuilder().toFilter(this); + Query filter = QueryBuilder.rewriteQuery(parseContext().parseInnerQueryBuilder(), this).toFilter(this); if (filter == null) { return null; } @@ -426,12 +395,16 @@ public class QueryShardContext { } } - private static Query toQuery(QueryBuilder<?> queryBuilder, QueryShardContext context) throws IOException { - Query query = queryBuilder.toQuery(context); + private static Query toQuery(final QueryBuilder<?> queryBuilder, final QueryShardContext context) throws IOException { + final Query query = QueryBuilder.rewriteQuery(queryBuilder, context).toQuery(context); if (query == null) { - query = Queries.newMatchNoDocsQuery(); + return Queries.newMatchNoDocsQuery(); } return query; } + public final Index index() { + return indexSettings.getIndex(); + } + } diff --git a/core/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java index 353dbd668a..31484fe804 100644 --- a/core/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/ScriptQueryBuilder.java @@ -25,6 +25,7 @@ import org.apache.lucene.search.Query; import org.apache.lucene.search.RandomAccessWeight; import org.apache.lucene.search.Weight; import org.apache.lucene.util.Bits; +import org.elasticsearch.client.Client; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; @@ -32,6 +33,7 @@ import org.elasticsearch.script.LeafSearchScript; import org.elasticsearch.script.Script; import org.elasticsearch.script.Script.ScriptField; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.script.ScriptEngineService; import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.lookup.SearchLookup; diff --git a/core/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java index 02a9bc42e3..4067f6d87c 100644 --- a/core/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/TemplateQueryBuilder.java @@ -25,11 +25,13 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.script.ExecutableScript; +import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.Template; -import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; +import java.util.Collections; import java.util.Map; import java.util.Objects; @@ -100,14 +102,7 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil @Override protected Query doToQuery(QueryShardContext context) throws IOException { - BytesReference querySource = context.executeQueryTemplate(template); - try (XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource)) { - final QueryShardContext contextCopy = new QueryShardContext(context); - contextCopy.reset(qSourceParser); - QueryBuilder result = contextCopy.parseContext().parseInnerQueryBuilder(); - context.combineNamedQueries(contextCopy); - return result.toQuery(context); - } + throw new UnsupportedOperationException("this query must be rewritten first"); } @Override @@ -130,4 +125,23 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil protected boolean doEquals(TemplateQueryBuilder other) { return Objects.equals(template, other.template); } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryRewriteContext) throws IOException { + ExecutableScript executable = queryRewriteContext.getScriptService().executable(template, + ScriptContext.Standard.SEARCH, Collections.emptyMap()); + BytesReference querySource = (BytesReference) executable.run(); + final QueryParseContext queryParseContext = queryRewriteContext.newParseContext(); + try (XContentParser qSourceParser = XContentFactory.xContent(querySource).createParser(querySource)) { + queryParseContext.reset(qSourceParser); + final QueryBuilder<?> queryBuilder = queryParseContext.parseInnerQueryBuilder(); + if (queryBuilder.boost() == DEFAULT_BOOST) { + queryBuilder.boost(boost()); // only pass down the boost if it has it's own boost + } + if (queryName() != null) { + queryBuilder.queryName(queryName()); + } + return queryBuilder; + } + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java index 326a6ed8b8..0cf4b7acf0 100644 --- a/core/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java @@ -38,7 +38,6 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.indices.cache.query.terms.TermsLookup; -import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; import java.util.ArrayList; @@ -227,22 +226,13 @@ public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> { @Override protected Query doToQuery(QueryShardContext context) throws IOException { - List<Object> terms; - TermsLookup termsLookup = null; - if (this.termsLookup != null) { - termsLookup = new TermsLookup(this.termsLookup); - if (termsLookup.index() == null) { - termsLookup.index(context.index().getName()); - } - Client client = context.getClient(); - terms = fetch(termsLookup, client); - } else { - terms = values; + if (termsLookup != null) { + throw new UnsupportedOperationException("query must be rewritten first"); } - if (terms == null || terms.isEmpty()) { + if (values == null || values.isEmpty()) { return Queries.newMatchNoDocsQuery(); } - return handleTermsQuery(terms, fieldName, context); + return handleTermsQuery(values, fieldName, context); } private List<Object> fetch(TermsLookup termsLookup, Client client) { @@ -324,4 +314,22 @@ public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> { Objects.equals(values, other.values) && Objects.equals(termsLookup, other.termsLookup); } + + @Override + public QueryBuilder<TermsQueryBuilder> rewrite(QueryRewriteContext queryRewriteContext) throws IOException { + if (this.termsLookup != null) { + TermsLookup termsLookup = new TermsLookup(this.termsLookup); + if (termsLookup.index() == null) { // TODO this should go away? + if (queryRewriteContext.hasIndex()) { + termsLookup.index(queryRewriteContext.getIndexSettings().getIndex().getName()); + } else { + return this; // can't rewrite until we have index scope on the shard + } + } + List<Object> values = fetch(termsLookup, queryRewriteContext.getClient()); + return new TermsQueryBuilder(this.fieldName, values).boost(boost()).queryName(queryName()); + } + return this; + } + } diff --git a/core/src/main/java/org/elasticsearch/index/query/WrapperQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/WrapperQueryBuilder.java index e908d76331..6ba07f766e 100644 --- a/core/src/main/java/org/elasticsearch/index/query/WrapperQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/WrapperQueryBuilder.java @@ -105,14 +105,7 @@ public class WrapperQueryBuilder extends AbstractQueryBuilder<WrapperQueryBuilde @Override protected Query doToQuery(QueryShardContext context) throws IOException { - try (XContentParser qSourceParser = XContentFactory.xContent(source).createParser(source)) { - final QueryShardContext contextCopy = new QueryShardContext(context); - contextCopy.reset(qSourceParser); - contextCopy.parseFieldMatcher(context.parseFieldMatcher()); - QueryBuilder<?> result = contextCopy.parseContext().parseInnerQueryBuilder(); - context.combineNamedQueries(contextCopy); - return result.toQuery(context); - } + throw new UnsupportedOperationException("this query must be rewritten first"); } @Override @@ -134,4 +127,21 @@ public class WrapperQueryBuilder extends AbstractQueryBuilder<WrapperQueryBuilde protected boolean doEquals(WrapperQueryBuilder other) { return Arrays.equals(source, other.source); // otherwise we compare pointers } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext context) throws IOException { + try (XContentParser qSourceParser = XContentFactory.xContent(source).createParser(source)) { + QueryParseContext parseContext = context.newParseContext(); + parseContext.reset(qSourceParser); + + final QueryBuilder<?> queryBuilder = parseContext.parseInnerQueryBuilder(); + if (queryBuilder.boost() == DEFAULT_BOOST) { + queryBuilder.boost(boost()); + } + if (queryName() != null) { // we inherit the name + queryBuilder.queryName(queryName()); + } + return queryBuilder; + } + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilder.java index 766911bb74..14a2826084 100644 --- a/core/src/main/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilder.java @@ -34,6 +34,7 @@ import org.elasticsearch.index.query.AbstractQueryBuilder; import org.elasticsearch.index.query.EmptyQueryBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.functionscore.random.RandomScoreFunctionBuilder; @@ -394,4 +395,17 @@ public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScor return new FilterFunctionBuilder(in.readQuery(), in.readScoreFunction()); } } + + @Override + public QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException { + QueryBuilder<?> queryBuilder = this.query.rewrite(queryShardContext); + if (queryBuilder != query) { + FunctionScoreQueryBuilder newQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder, filterFunctionBuilders); + newQueryBuilder.scoreMode = scoreMode; + newQueryBuilder.minScore = minScore; + newQueryBuilder.maxBoost = maxBoost; + return newQueryBuilder.queryName(queryName()).boost(boost()); + } + return this; + } } diff --git a/core/src/main/java/org/elasticsearch/index/query/support/NestedInnerQueryParseSupport.java b/core/src/main/java/org/elasticsearch/index/query/support/NestedInnerQueryParseSupport.java index 890961dd2a..9923728e3b 100644 --- a/core/src/main/java/org/elasticsearch/index/query/support/NestedInnerQueryParseSupport.java +++ b/core/src/main/java/org/elasticsearch/index/query/support/NestedInnerQueryParseSupport.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.mapper.object.ObjectMapper; +import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardException; @@ -91,7 +92,8 @@ public class NestedInnerQueryParseSupport { if (path != null) { setPathLevel(); try { - innerFilter = parseContext.parseInnerQueryBuilder().toFilter(this.shardContext); + innerFilter = QueryBuilder.rewriteQuery(parseContext.parseInnerQueryBuilder(), + this.shardContext).toFilter(this.shardContext); } finally { resetPathLevel(); } @@ -147,7 +149,8 @@ public class NestedInnerQueryParseSupport { try { XContentParser innerParser = XContentHelper.createParser(source); parseContext.parser(innerParser); - innerFilter = parseContext.parseInnerQueryBuilder().toFilter(this.shardContext); + innerFilter = QueryBuilder.rewriteQuery(parseContext.parseInnerQueryBuilder(), + this.shardContext).toFilter(this.shardContext); filterParsed = true; return innerFilter; } finally { diff --git a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java index f2f1add259..e131fa2dd0 100644 --- a/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/core/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -252,7 +252,7 @@ public class IndexShard extends AbstractIndexShardComponent { this.engineConfig = newEngineConfig(translogConfig, cachingPolicy); this.suspendableRefContainer = new SuspendableRefContainer(); this.searcherWrapper = indexSearcherWrapper; - QueryShardContext queryShardContext = new QueryShardContext(idxSettings, provider.getClient(), indexCache.bitsetFilterCache(), indexFieldDataService, mapperService, similarityService, provider.getScriptService(), provider.getIndicesQueriesRegistry()); + QueryShardContext queryShardContext = new QueryShardContext(idxSettings, indexCache.bitsetFilterCache(), indexFieldDataService, mapperService, similarityService, provider.getScriptService(), provider.getIndicesQueriesRegistry()); this.percolatorQueriesRegistry = new PercolatorQueriesRegistry(shardId, indexSettings, queryShardContext); } diff --git a/core/src/main/java/org/elasticsearch/script/ScriptService.java b/core/src/main/java/org/elasticsearch/script/ScriptService.java index d21283d9cf..8e1ac1c8d7 100644 --- a/core/src/main/java/org/elasticsearch/script/ScriptService.java +++ b/core/src/main/java/org/elasticsearch/script/ScriptService.java @@ -489,6 +489,10 @@ public class ScriptService extends AbstractComponent implements Closeable { return scriptMetrics.stats(); } + public Client getClient() { + return client; + } + /** * A small listener for the script cache that calls each * {@code ScriptEngineService}'s {@code scriptRemoved} method when the diff --git a/core/src/main/java/org/elasticsearch/search/SearchService.java b/core/src/main/java/org/elasticsearch/search/SearchService.java index e0b30a2e34..7c6fa0da9b 100644 --- a/core/src/main/java/org/elasticsearch/search/SearchService.java +++ b/core/src/main/java/org/elasticsearch/search/SearchService.java @@ -536,8 +536,10 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> imp DefaultSearchContext context = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, engineSearcher, indexService, indexShard, scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher, defaultSearchTimeout); SearchContext.setCurrent(context); - try { + if (request.source() != null) { + request.source().rewrite(context.getQueryShardContext()); + } if (request.scroll() != null) { context.scrollContext(new ScrollContext()); context.scrollContext().scroll = request.scroll(); diff --git a/core/src/main/java/org/elasticsearch/search/action/SearchServiceTransportAction.java b/core/src/main/java/org/elasticsearch/search/action/SearchServiceTransportAction.java index 138b215e68..81fa590908 100644 --- a/core/src/main/java/org/elasticsearch/search/action/SearchServiceTransportAction.java +++ b/core/src/main/java/org/elasticsearch/search/action/SearchServiceTransportAction.java @@ -81,7 +81,6 @@ public class SearchServiceTransportAction extends AbstractComponent { super(settings); this.transportService = transportService; this.searchService = searchService; - transportService.registerRequestHandler(FREE_CONTEXT_SCROLL_ACTION_NAME, ScrollFreeContextRequest::new, ThreadPool.Names.SAME, new FreeContextTransportHandler<>()); transportService.registerRequestHandler(FREE_CONTEXT_ACTION_NAME, SearchFreeContextRequest::new, ThreadPool.Names.SAME, new FreeContextTransportHandler<SearchFreeContextRequest>()); transportService.registerRequestHandler(CLEAR_SCROLL_CONTEXTS_ACTION_NAME, ClearScrollContextsRequest::new, ThreadPool.Names.SAME, new ClearScrollContextsTransportHandler()); 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 6a9d95deb3..99ee939d15 100644 --- a/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/builder/SearchSourceBuilder.java @@ -40,6 +40,7 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.script.Script; import org.elasticsearch.search.searchafter.SearchAfterBuilder; import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; @@ -1433,4 +1434,17 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ && Objects.equals(version, other.version) && Objects.equals(profile, other.profile); } + + /** + * Rewrites the internal query builders in-place + */ + public void rewrite(QueryRewriteContext rewriteContext) throws IOException { + if (queryBuilder != null) { + queryBuilder = QueryBuilder.rewriteQuery(queryBuilder, rewriteContext); + } + if (postQueryBuilder != null) { + postQueryBuilder = QueryBuilder.rewriteQuery(postQueryBuilder, rewriteContext); + } + } + } diff --git a/core/src/main/java/org/elasticsearch/search/highlight/HighlightBuilder.java b/core/src/main/java/org/elasticsearch/search/highlight/HighlightBuilder.java index c0b1aeea3b..325541844c 100644 --- a/core/src/main/java/org/elasticsearch/search/highlight/HighlightBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/highlight/HighlightBuilder.java @@ -355,7 +355,7 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde targetOptionsBuilder.options(highlighterBuilder.options); } if (highlighterBuilder.highlightQuery != null) { - targetOptionsBuilder.highlightQuery(highlighterBuilder.highlightQuery.toQuery(context)); + targetOptionsBuilder.highlightQuery(QueryBuilder.rewriteQuery(highlighterBuilder.highlightQuery, context).toQuery(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 10c727a902..c65fca79a9 100644 --- a/core/src/main/java/org/elasticsearch/search/rescore/QueryRescorerBuilder.java +++ b/core/src/main/java/org/elasticsearch/search/rescore/QueryRescorerBuilder.java @@ -149,7 +149,7 @@ public class QueryRescorerBuilder extends RescoreBuilder<QueryRescorerBuilder> { 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(this.queryBuilder.toQuery(context)); + queryRescoreContext.setQuery(QueryBuilder.rewriteQuery(this.queryBuilder, context).toQuery(context)); queryRescoreContext.setQueryWeight(this.queryWeight); queryRescoreContext.setRescoreQueryWeight(this.rescoreQueryWeight); queryRescoreContext.setScoreMode(this.scoreMode); @@ -239,4 +239,4 @@ public class QueryRescorerBuilder extends RescoreBuilder<QueryRescorerBuilder> { this.scoreMode = scoreMode; } } -}
\ No newline at end of file +} diff --git a/core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java b/core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java index e8f20cb855..5b8550b7b1 100644 --- a/core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java +++ b/core/src/test/java/org/elasticsearch/index/query/AbstractQueryTestCase.java @@ -286,7 +286,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> } }); indicesQueriesRegistry = injector.getInstance(IndicesQueriesRegistry.class); - queryShardContext = new QueryShardContext(idxSettings, proxy, bitsetFilterCache, indexFieldDataService, mapperService, similarityService, scriptService, indicesQueriesRegistry); + queryShardContext = new QueryShardContext(idxSettings, bitsetFilterCache, indexFieldDataService, mapperService, similarityService, scriptService, indicesQueriesRegistry); //create some random type with some default field, those types will stick around for all of the subclasses currentTypes = new String[randomIntBetween(0, 5)]; for (int i = 0; i < currentTypes.length; i++) { @@ -516,7 +516,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> QB firstQuery = createTestQueryBuilder(); QB controlQuery = copyQuery(firstQuery); setSearchContext(randomTypes); // only set search context for toQuery to be more realistic - Query firstLuceneQuery = firstQuery.toQuery(context); + Query firstLuceneQuery = rewriteQuery(firstQuery, context).toQuery(context); assertLuceneQuery(firstQuery, firstLuceneQuery, context); SearchContext.removeCurrent(); // remove after assertLuceneQuery since the assertLuceneQuery impl might access the context as well assertTrue( @@ -534,7 +534,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> + randomAsciiOfLengthBetween(1, 10)); } setSearchContext(randomTypes); - Query secondLuceneQuery = secondQuery.toQuery(context); + Query secondLuceneQuery = rewriteQuery(secondQuery, context).toQuery(context); assertLuceneQuery(secondQuery, secondLuceneQuery, context); SearchContext.removeCurrent(); @@ -544,7 +544,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> if (firstLuceneQuery != null && supportsBoostAndQueryName()) { secondQuery.boost(firstQuery.boost() + 1f + randomFloat()); setSearchContext(randomTypes); - Query thirdLuceneQuery = secondQuery.toQuery(context); + Query thirdLuceneQuery = rewriteQuery(secondQuery, context).toQuery(context); SearchContext.removeCurrent(); assertThat("modifying the boost doesn't affect the corresponding lucene query", firstLuceneQuery, not(equalTo(thirdLuceneQuery))); @@ -552,6 +552,12 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> } } + private QueryBuilder<?> rewriteQuery(QB queryBuilder, QueryRewriteContext rewriteContext) throws IOException { + QueryBuilder<?> rewritten = QueryBuilder.rewriteQuery(queryBuilder, rewriteContext); + assertSerialization(rewritten); + return rewritten; + } + /** * Few queries allow you to set the boost and queryName on the java api, although the corresponding parser doesn't parse them as they are not supported. * This method allows to disable boost and queryName related tests for those queries. Those queries are easy to identify: their parsers @@ -625,11 +631,13 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> * Serialize the given query builder and asserts that both are equal */ @SuppressWarnings("unchecked") - protected QB assertSerialization(QB testQuery) throws IOException { + protected <QB extends QueryBuilder> QB assertSerialization(QB testQuery) throws IOException { try (BytesStreamOutput output = new BytesStreamOutput()) { testQuery.writeTo(output); try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) { - QueryBuilder<?> prototype = queryParser(testQuery.getName()).getBuilderPrototype(); + QueryParser<?> queryParser = queryParser(testQuery.getName()); + assertNotNull("queryparser not found for query: [" + testQuery.getName() + "]", queryParser); + QueryBuilder<?> prototype = queryParser.getBuilderPrototype(); QueryBuilder<?> deserializedQuery = prototype.readFrom(in); assertEquals(deserializedQuery, testQuery); assertEquals(deserializedQuery.hashCode(), testQuery.hashCode()); @@ -674,7 +682,26 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> } private QueryParser<?> queryParser(String queryId) { - return indicesQueriesRegistry.queryParsers().get(queryId); + QueryParser<?> queryParser = indicesQueriesRegistry.queryParsers().get(queryId); + if (queryParser == null && EmptyQueryBuilder.NAME.equals(queryId)) { + return new QueryParser() { + @Override + public String[] names() { + return new String[] {EmptyQueryBuilder.NAME}; + } + + @Override + public QueryBuilder<?> fromXContent(QueryParseContext parseContext) throws IOException { + return new EmptyQueryBuilder(); + } + + @Override + public QueryBuilder getBuilderPrototype() { + return EmptyQueryBuilder.PROTOTYPE; + } + }; + } + return queryParser; } //we use the streaming infra to create a copy of the query provided as argument diff --git a/core/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java b/core/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java index f78700d4a1..1c35642c51 100644 --- a/core/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java @@ -21,15 +21,12 @@ package org.elasticsearch.index.query; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.core.StringFieldMapper; import org.elasticsearch.test.ESTestCase; -import java.util.Collections; - import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.notNullValue; @@ -50,7 +47,7 @@ public class QueryShardContextTests extends ESTestCase { MapperService mapperService = mock(MapperService.class); when(mapperService.getIndexSettings()).thenReturn(indexSettings); QueryShardContext context = new QueryShardContext( - indexSettings, null, null, null, mapperService, null, null, null + indexSettings, null, null, mapperService, null, null, null ); context.setAllowUnmappedFields(false); diff --git a/core/src/test/java/org/elasticsearch/index/query/TemplateQueryBuilderTests.java b/core/src/test/java/org/elasticsearch/index/query/TemplateQueryBuilderTests.java index df7eb3c697..7a602f0941 100644 --- a/core/src/test/java/org/elasticsearch/index/query/TemplateQueryBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/index/query/TemplateQueryBuilderTests.java @@ -56,7 +56,7 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue @Override protected void doAssertLuceneQuery(TemplateQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException { - assertEquals(templateBase.toQuery(context), query); + assertEquals(QueryBuilder.rewriteQuery(templateBase, context).toQuery(context), query); } public void testIllegalArgument() { diff --git a/core/src/test/java/org/elasticsearch/percolator/PercolateDocumentParserTests.java b/core/src/test/java/org/elasticsearch/percolator/PercolateDocumentParserTests.java index 91fb8b5cd8..a561c97e3d 100644 --- a/core/src/test/java/org/elasticsearch/percolator/PercolateDocumentParserTests.java +++ b/core/src/test/java/org/elasticsearch/percolator/PercolateDocumentParserTests.java @@ -84,7 +84,7 @@ public class PercolateDocumentParserTests extends ESTestCase { Map<String, QueryParser<?>> parsers = singletonMap("term", new TermQueryParser()); IndicesQueriesRegistry indicesQueriesRegistry = new IndicesQueriesRegistry(indexSettings.getSettings(), parsers); - queryShardContext = new QueryShardContext(indexSettings, null, null, null, mapperService, null, null, indicesQueriesRegistry); + queryShardContext = new QueryShardContext(indexSettings, null, null, mapperService, null, null, indicesQueriesRegistry); HighlightPhase highlightPhase = new HighlightPhase(Settings.EMPTY, new Highlighters()); AggregatorParsers aggregatorParsers = new AggregatorParsers(Collections.emptySet(), Collections.emptySet()); diff --git a/core/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java b/core/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java index 786d594182..3714f6a23f 100644 --- a/core/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/search/builder/SearchSourceBuilderTests.java @@ -41,9 +41,14 @@ import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.env.Environment; import org.elasticsearch.index.query.AbstractQueryTestCase; +import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.EmptyQueryBuilder; +import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryParseContext; +import org.elasticsearch.index.query.QueryRewriteContext; +import org.elasticsearch.index.query.TermQueryBuilder; +import org.elasticsearch.index.query.WrapperQueryBuilder; import org.elasticsearch.index.query.functionscore.ScoreFunctionParser; import org.elasticsearch.indices.query.IndicesQueriesRegistry; import org.elasticsearch.script.Script; @@ -483,4 +488,14 @@ public class SearchSourceBuilderTests extends ESTestCase { String query = "{ \"post_filter\": {} }"; assertParseSearchSource(builder, new BytesArray(query)); } + + public void testRewrite() throws IOException { + SearchSourceBuilder builder = new SearchSourceBuilder(); + builder.query(new BoolQueryBuilder()); + TermQueryBuilder tqb = new TermQueryBuilder("foo", "bar"); + builder.postFilter(new WrapperQueryBuilder(tqb.toString())); + builder.rewrite(new QueryRewriteContext(null, null, indicesQueriesRegistry)); + assertEquals(new MatchAllQueryBuilder(), builder.query()); + assertEquals(tqb, builder.postFilter()); + } } diff --git a/core/src/test/java/org/elasticsearch/search/highlight/HighlightBuilderTests.java b/core/src/test/java/org/elasticsearch/search/highlight/HighlightBuilderTests.java index 5dc8528c00..2cd81b0fde 100644 --- a/core/src/test/java/org/elasticsearch/search/highlight/HighlightBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/search/highlight/HighlightBuilderTests.java @@ -278,7 +278,7 @@ public class HighlightBuilderTests extends ESTestCase { Index index = new Index(randomAsciiOfLengthBetween(1, 10), "_na_"); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(index, indexSettings); // shard context will only need indicesQueriesRegistry for building Query objects nested in highlighter - QueryShardContext mockShardContext = new QueryShardContext(idxSettings, null, null, null, null, null, null, indicesQueriesRegistry) { + QueryShardContext mockShardContext = new QueryShardContext(idxSettings, null, null, null, null, null, indicesQueriesRegistry) { @Override public MappedFieldType fieldMapper(String name) { StringFieldMapper.Builder builder = MapperBuilders.stringField(name); diff --git a/core/src/test/java/org/elasticsearch/search/rescore/QueryRescoreBuilderTests.java b/core/src/test/java/org/elasticsearch/search/rescore/QueryRescoreBuilderTests.java index 01f7e33244..37f81e3d33 100644 --- a/core/src/test/java/org/elasticsearch/search/rescore/QueryRescoreBuilderTests.java +++ b/core/src/test/java/org/elasticsearch/search/rescore/QueryRescoreBuilderTests.java @@ -160,7 +160,7 @@ public class QueryRescoreBuilderTests extends ESTestCase { .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(randomAsciiOfLengthBetween(1, 10), indexSettings); // shard context will only need indicesQueriesRegistry for building Query objects nested in query rescorer - QueryShardContext mockShardContext = new QueryShardContext(idxSettings, null, null, null, null, null, null, indicesQueriesRegistry) { + QueryShardContext mockShardContext = new QueryShardContext(idxSettings, null, null, null, null, null, indicesQueriesRegistry) { @Override public MappedFieldType fieldMapper(String name) { StringFieldMapper.Builder builder = MapperBuilders.stringField(name); @@ -170,7 +170,7 @@ public class QueryRescoreBuilderTests extends ESTestCase { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { RescoreBuilder<?> rescoreBuilder = randomRescoreBuilder(); - QueryRescoreContext rescoreContext = (QueryRescoreContext) rescoreBuilder.build(mockShardContext); + QueryRescoreContext rescoreContext = rescoreBuilder.build(mockShardContext); XContentParser parser = createParser(rescoreBuilder); QueryRescoreContext parsedRescoreContext = (QueryRescoreContext) new RescoreParseElement().parseSingleRescoreContext(parser, mockShardContext); diff --git a/core/src/test/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGeneratorTests.java b/core/src/test/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGeneratorTests.java index 02826b9a7e..ebf903d91a 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGeneratorTests.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/phrase/DirectCandidateGeneratorTests.java @@ -171,7 +171,7 @@ public class DirectCandidateGeneratorTests extends ESTestCase{ } }; - QueryShardContext mockShardContext = new QueryShardContext(idxSettings, null, null, null, mockMapperService, null, null, null) { + QueryShardContext mockShardContext = new QueryShardContext(idxSettings, null, null, mockMapperService, null, null, null) { @Override public MappedFieldType fieldMapper(String name) { StringFieldMapper.Builder builder = MapperBuilders.stringField(name); |