Merge pull request #16599 from s1monw/add_rewrite_infra

Add infrastructure to rewrite query builders
This commit is contained in:
Simon Willnauer 2016-02-12 22:35:41 +01:00
commit b679b8bf9a
50 changed files with 799 additions and 205 deletions

View File

@ -90,7 +90,6 @@ public class TransportSearchAction extends HandledTransportAction<SearchRequest,
logger.debug("failed to optimize search type, continue as normal", e); logger.debug("failed to optimize search type, continue as normal", e);
} }
} }
if (searchRequest.searchType() == DFS_QUERY_THEN_FETCH) { if (searchRequest.searchType() == DFS_QUERY_THEN_FETCH) {
dfsQueryThenFetchAction.execute(searchRequest, listener); dfsQueryThenFetchAction.execute(searchRequest, listener);
} else if (searchRequest.searchType() == SearchType.QUERY_THEN_FETCH) { } else if (searchRequest.searchType() == SearchType.QUERY_THEN_FETCH) {

View File

@ -27,6 +27,8 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index; 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.index.query.QueryShardContext;
import org.elasticsearch.indices.InvalidAliasNameException; import org.elasticsearch.indices.InvalidAliasNameException;
@ -143,7 +145,9 @@ public class AliasValidator extends AbstractComponent {
private void validateAliasFilter(XContentParser parser, QueryShardContext queryShardContext) throws IOException { private void validateAliasFilter(XContentParser parser, QueryShardContext queryShardContext) throws IOException {
try { try {
queryShardContext.reset(parser); queryShardContext.reset(parser);
queryShardContext.parseContext().parseInnerQueryBuilder().toFilter(queryShardContext); QueryParseContext queryParseContext = queryShardContext.parseContext();
QueryBuilder<?> queryBuilder = QueryBuilder.rewriteQuery(queryParseContext.parseInnerQueryBuilder(), queryShardContext);
queryBuilder.toFilter(queryShardContext);
} finally { } finally {
queryShardContext.reset(null); queryShardContext.reset(null);
parser.close(); parser.close();

View File

@ -420,7 +420,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...)} * 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() { 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() { ThreadPool getThreadPool() {

View File

@ -258,4 +258,23 @@ public abstract class AbstractQueryBuilder<QB extends AbstractQueryBuilder<QB>>
} }
return queries; return queries;
} }
@Override
public final QueryBuilder<?> rewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder rewritten = doRewrite(queryShardContext);
if (rewritten == this) {
return rewritten;
}
if (queryName() != null && rewritten.queryName() == null) { // we inherit the name
rewritten.queryName(queryName());
}
if (boost() != DEFAULT_BOOST && rewritten.boost() == DEFAULT_BOOST) {
rewritten.boost(boost());
}
return rewritten;
}
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryShardContext) throws IOException {
return this;
}
} }

View File

@ -33,6 +33,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded; import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
@ -272,6 +273,7 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
if (booleanQuery.clauses().isEmpty()) { if (booleanQuery.clauses().isEmpty()) {
return new MatchAllDocsQuery(); return new MatchAllDocsQuery();
} }
final String minimumShouldMatch; final String minimumShouldMatch;
if (context.isFilter() && this.minimumShouldMatch == null && shouldClauses.size() > 0) { if (context.isFilter() && this.minimumShouldMatch == null && shouldClauses.size() > 0) {
minimumShouldMatch = "1"; minimumShouldMatch = "1";
@ -346,4 +348,40 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
out.writeBoolean(disableCoord); out.writeBoolean(disableCoord);
out.writeOptionalString(minimumShouldMatch); out.writeOptionalString(minimumShouldMatch);
} }
@Override
protected QueryBuilder<?> doRewrite(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<?>> consumer) throws IOException {
boolean changed = false;
for (QueryBuilder builder : builders) {
QueryBuilder result = builder.rewrite(queryRewriteContext);
if (result != builder) {
changed = true;
}
consumer.accept(result);
}
return changed;
}
} }

View File

@ -158,4 +158,16 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
out.writeQuery(negativeQuery); out.writeQuery(negativeQuery);
out.writeFloat(negativeBoost); out.writeFloat(negativeBoost);
} }
@Override
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder positiveQuery = this.positiveQuery.rewrite(queryRewriteContext);
QueryBuilder negativeQuery = this.negativeQuery.rewrite(queryRewriteContext);
if (positiveQuery != this.positiveQuery || negativeQuery != this.negativeQuery) {
BoostingQueryBuilder newQueryBuilder = new BoostingQueryBuilder(positiveQuery, negativeQuery);
newQueryBuilder.negativeBoost = negativeBoost;
return newQueryBuilder;
}
return this;
}
} }

View File

@ -104,4 +104,13 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
protected void doWriteTo(StreamOutput out) throws IOException { protected void doWriteTo(StreamOutput out) throws IOException {
out.writeQuery(filterBuilder); out.writeQuery(filterBuilder);
} }
@Override
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = filterBuilder.rewrite(queryRewriteContext);
if (rewrite != filterBuilder) {
return new ConstantScoreQueryBuilder(rewrite);
}
return this;
}
} }

View File

@ -19,12 +19,11 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query; 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.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException; import java.io.IOException;
@ -32,80 +31,50 @@ import java.io.IOException;
* A {@link QueryBuilder} that is a stand in replacement for an empty query clause in the DSL. * A {@link QueryBuilder} that is a stand in replacement for an empty query clause in the DSL.
* The current DSL allows parsing inner queries / filters like "{ }", in order to have a * The current DSL allows parsing inner queries / filters like "{ }", in order to have a
* valid non-null representation of these clauses that actually do nothing we can use this class. * valid non-null representation of these clauses that actually do nothing we can use this class.
*
* This builder has no corresponding parser and it is not registered under the query name. It is
* 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"; public static final String NAME = "empty_query";
/** the one and only empty query builder */ /** the one and only empty query builder */
public static final EmptyQueryBuilder PROTOTYPE = new EmptyQueryBuilder(); public static final EmptyQueryBuilder PROTOTYPE = new EmptyQueryBuilder();
// prevent instances other than prototype
private EmptyQueryBuilder() {
super(XContentType.JSON);
}
@Override @Override
public String getWriteableName() { public String getWriteableName() {
return NAME; return NAME;
} }
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
return null;
}
@Override @Override
public String getName() { public String getName() {
return getWriteableName(); return getWriteableName();
} }
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
builder.endObject();
return builder;
} }
@Override @Override
public Query toQuery(QueryShardContext context) throws IOException { protected void doWriteTo(StreamOutput out) throws IOException {
// empty }
return null;
@Override
protected EmptyQueryBuilder doReadFrom(StreamInput in) throws IOException {
return new EmptyQueryBuilder();
} }
@Override @Override
public Query toFilter(QueryShardContext context) throws IOException { protected int doHashCode() {
// empty return 31;
return null;
} }
@Override @Override
public void writeTo(StreamOutput out) throws IOException { protected boolean doEquals(EmptyQueryBuilder other) {
} return true;
@Override
public EmptyQueryBuilder readFrom(StreamInput in) throws IOException {
return EmptyQueryBuilder.PROTOTYPE;
}
@Override
public EmptyQueryBuilder queryName(String queryName) {
//no-op
return this;
}
@Override
public String queryName() {
return null;
}
@Override
public float boost() {
return -1;
}
@Override
public EmptyQueryBuilder boost(float boost) {
//no-op
return this;
} }
} }

View File

@ -43,7 +43,6 @@ import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper; import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper;
import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
@ -62,7 +61,7 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
private final String fieldName; private final String fieldName;
private ShapeBuilder shape; private final ShapeBuilder shape;
private SpatialStrategy strategy; private SpatialStrategy strategy;
@ -236,13 +235,12 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
} }
@Override @Override
protected Query doToQuery(QueryShardContext context) throws IOException { protected Query doToQuery(QueryShardContext context) {
ShapeBuilder shapeToQuery = shape; if (shape == null) {
if (shapeToQuery == null) { throw new UnsupportedOperationException("query must be rewritten first");
GetRequest getRequest = new GetRequest(indexedShapeIndex, indexedShapeType, indexedShapeId);
shapeToQuery = fetch(context.getClient(), getRequest, indexedShapePath);
} }
MappedFieldType fieldType = context.fieldMapper(fieldName); final ShapeBuilder shapeToQuery = shape;
final MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType == null) { if (fieldType == null) {
throw new QueryShardException(context, "Failed to find geo_shape field [" + fieldName + "]"); 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"); 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(); PrefixTreeStrategy strategy = shapeFieldType.defaultStrategy();
if (this.strategy != null) { if (this.strategy != null) {
@ -449,4 +447,14 @@ public class GeoShapeQueryBuilder extends AbstractQueryBuilder<GeoShapeQueryBuil
public String getWriteableName() { public String getWriteableName() {
return NAME; return NAME;
} }
@Override
protected QueryBuilder<GeoShapeQueryBuilder> doRewrite(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);
}
return this;
}
} }

View File

@ -26,7 +26,6 @@ import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.JoinUtil; import org.apache.lucene.search.join.JoinUtil;
import org.apache.lucene.search.join.ScoreMode; 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.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.lucene.search.Queries;
@ -397,4 +396,18 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
out.writeBoolean(false); out.writeBoolean(false);
} }
} }
@Override
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryRewriteContext);
if (rewrite != query) {
HasChildQueryBuilder hasChildQueryBuilder = new HasChildQueryBuilder(type, rewrite);
hasChildQueryBuilder.minChildren = minChildren;
hasChildQueryBuilder.maxChildren = maxChildren;
hasChildQueryBuilder.scoreMode = scoreMode;
hasChildQueryBuilder.queryInnerHits = queryInnerHits;
return hasChildQueryBuilder;
}
return this;
}
} }

View File

@ -22,7 +22,6 @@ import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.ScoreMode; 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.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.lucene.search.Queries;
@ -256,4 +255,16 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
protected int doHashCode() { protected int doHashCode() {
return Objects.hash(query, type, score, innerHit); return Objects.hash(query, type, score, innerHit);
} }
@Override
protected QueryBuilder<?> doRewrite(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;
}
return this;
}
} }

View File

@ -140,4 +140,14 @@ public class IndicesQueryBuilder extends AbstractQueryBuilder<IndicesQueryBuilde
Arrays.equals(indices, other.indices) && // otherwise we are comparing pointers Arrays.equals(indices, other.indices) && // otherwise we are comparing pointers
Objects.equals(noMatchQuery, other.noMatchQuery); Objects.equals(noMatchQuery, other.noMatchQuery);
} }
@Override
protected QueryBuilder<?> doRewrite(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);
}
return this;
}
} }

View File

@ -1050,4 +1050,10 @@ public class MoreLikeThisQueryBuilder extends AbstractQueryBuilder<MoreLikeThisQ
Objects.equals(include, other.include) && Objects.equals(include, other.include) &&
Objects.equals(failOnUnsupportedField, other.failOnUnsupportedField); Objects.equals(failOnUnsupportedField, other.failOnUnsupportedField);
} }
@Override
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
// TODO this needs heavy cleanups before we can rewrite it
return this;
}
} }

View File

@ -225,4 +225,12 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
return new ToParentBlockJoinQuery(Queries.filtered(innerQuery, childFilter), parentFilter, scoreMode); return new ToParentBlockJoinQuery(Queries.filtered(innerQuery, childFilter), parentFilter, scoreMode);
} }
@Override
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryRewriteContext);
if (rewrite != query) {
return new NestedQueryBuilder(path, rewrite).scoreMode(scoreMode);
}
return this;
}
} }

View File

@ -72,4 +72,28 @@ public interface QueryBuilder<QB extends QueryBuilder<QB>> extends NamedWriteabl
* Returns the name that identifies uniquely the query * Returns the name that identifies uniquely the query
*/ */
String getName(); String getName();
/**
* Rewrites this query builder into its primitive form. By default this method return the builder itself. If the builder
* did not change the identity reference must be returned otherwise the builder will be rewritten infinitely.
*/
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;
}
} }

View File

@ -106,7 +106,7 @@ public class QueryParseContext {
token = parser.nextToken(); token = parser.nextToken();
if (token == XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.END_OBJECT) {
// empty query // empty query
return EmptyQueryBuilder.PROTOTYPE; return new EmptyQueryBuilder();
} }
if (token != XContentParser.Token.FIELD_NAME) { if (token != XContentParser.Token.FIELD_NAME) {
throw new ParsingException(parser.getTokenLocation(), "[_na] query malformed, no field after start_object"); throw new ParsingException(parser.getTokenLocation(), "[_na] query malformed, no field after start_object");

View File

@ -0,0 +1,72 @@
/*
* 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;
/**
* Context object used to rewrite {@link QueryBuilder} instances into simplified version.
*/
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);
}
/**
* Returns a clients to fetch resources from local or remove nodes.
*/
public final Client getClient() {
return scriptService.getClient();
}
/**
* Returns the index settings for this context. This might return null if the
* context has not index scope.
*/
public final IndexSettings getIndexSettings() {
return indexSettings;
}
/**
* Returns a script service to fetch scripts.
*/
public final ScriptService getScriptService() {
return scriptService;
}
/**
* Returns a new {@link QueryParseContext} to parse template or wrapped queries.
*/
public QueryParseContext newParseContext() {
QueryParseContext queryParseContext = new QueryParseContext(indicesQueriesRegistry);
queryParseContext.parseFieldMatcher(parseContext.parseFieldMatcher());
return queryParseContext;
}
}

View File

@ -26,7 +26,6 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.join.BitSetProducer; import org.apache.lucene.search.join.BitSetProducer;
import org.apache.lucene.search.similarities.Similarity; import org.apache.lucene.search.similarities.Similarity;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
@ -51,10 +50,7 @@ import org.elasticsearch.index.query.support.InnerHitsQueryParserHelper;
import org.elasticsearch.index.query.support.NestedScope; import org.elasticsearch.index.query.support.NestedScope;
import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.indices.query.IndicesQueriesRegistry; 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.ScriptService;
import org.elasticsearch.script.Template;
import org.elasticsearch.search.fetch.innerhits.InnerHitsContext; import org.elasticsearch.search.fetch.innerhits.InnerHitsContext;
import org.elasticsearch.search.fetch.innerhits.InnerHitsSubSearchContext; import org.elasticsearch.search.fetch.innerhits.InnerHitsSubSearchContext;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
@ -63,7 +59,6 @@ import org.elasticsearch.search.lookup.SearchLookup;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -72,15 +67,13 @@ import static java.util.Collections.unmodifiableMap;
/** /**
* Context object used to create lucene queries on the shard level. * 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 MapperService mapperService;
private final ScriptService scriptService;
private final SimilarityService similarityService; private final SimilarityService similarityService;
private final BitsetFilterCache bitsetFilterCache; private final BitsetFilterCache bitsetFilterCache;
private final IndexFieldDataService indexFieldDataService; private final IndexFieldDataService indexFieldDataService;
private final IndexSettings indexSettings; private final IndexSettings indexSettings;
private final Client client;
private String[] types = Strings.EMPTY_ARRAY; private String[] types = Strings.EMPTY_ARRAY;
public void setTypes(String... types) { public void setTypes(String... types) {
@ -93,35 +86,31 @@ public class QueryShardContext {
private final Map<String, Query> namedQueries = new HashMap<>(); private final Map<String, Query> namedQueries = new HashMap<>();
private final MapperQueryParser queryParser = new MapperQueryParser(this); private final MapperQueryParser queryParser = new MapperQueryParser(this);
private final IndicesQueriesRegistry indicesQueriesRegistry;
private boolean allowUnmappedFields; private boolean allowUnmappedFields;
private boolean mapUnmappedFieldAsString; private boolean mapUnmappedFieldAsString;
private NestedScope nestedScope; private NestedScope nestedScope;
private QueryParseContext parseContext;
boolean isFilter; // pkg private for testing 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) { final IndicesQueriesRegistry indicesQueriesRegistry) {
super(indexSettings, scriptService, indicesQueriesRegistry);
this.indexSettings = indexSettings; this.indexSettings = indexSettings;
this.scriptService = scriptService;
this.client = client;
this.similarityService = similarityService; this.similarityService = similarityService;
this.mapperService = mapperService; this.mapperService = mapperService;
this.bitsetFilterCache = bitsetFilterCache; this.bitsetFilterCache = bitsetFilterCache;
this.indexFieldDataService = indexFieldDataService; this.indexFieldDataService = indexFieldDataService;
this.allowUnmappedFields = indexSettings.isDefaultAllowUnmappedFields(); this.allowUnmappedFields = indexSettings.isDefaultAllowUnmappedFields();
this.indicesQueriesRegistry = indicesQueriesRegistry;
this.parseContext = new QueryParseContext(indicesQueriesRegistry);
} }
public QueryShardContext(QueryShardContext source) { 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(); this.types = source.getTypes();
} }
public QueryShardContext clone() { 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) { public void parseFieldMatcher(ParseFieldMatcher parseFieldMatcher) {
@ -146,10 +135,6 @@ public class QueryShardContext {
this.parseContext.reset(jp); this.parseContext.reset(jp);
} }
public Index index() {
return this.mapperService.getIndexSettings().getIndex();
}
public InnerHitsSubSearchContext getInnerHitsContext(XContentParser parser) throws IOException { public InnerHitsSubSearchContext getInnerHitsContext(XContentParser parser) throws IOException {
return InnerHitsQueryParserHelper.parse(parser); return InnerHitsQueryParserHelper.parse(parser);
} }
@ -158,10 +143,6 @@ public class QueryShardContext {
return mapperService.analysisService(); return mapperService.analysisService();
} }
public ScriptService getScriptService() {
return scriptService;
}
public MapperService getMapperService() { public MapperService getMapperService() {
return mapperService; return mapperService;
} }
@ -210,10 +191,6 @@ public class QueryShardContext {
return unmodifiableMap(new HashMap<>(namedQueries)); return unmodifiableMap(new HashMap<>(namedQueries));
} }
public void combineNamedQueries(QueryShardContext context) {
namedQueries.putAll(context.namedQueries);
}
/** /**
* Return whether we are currently parsing a filter or a query. * Return whether we are currently parsing a filter or a query.
*/ */
@ -340,18 +317,6 @@ public class QueryShardContext {
return false; 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) { public ParsedQuery parse(BytesReference source) {
XContentParser parser = null; XContentParser parser = null;
try { try {
@ -384,7 +349,7 @@ public class QueryShardContext {
reset(parser); reset(parser);
try { try {
parseFieldMatcher(indexSettings.getParseFieldMatcher()); parseFieldMatcher(indexSettings.getParseFieldMatcher());
Query filter = parseContext().parseInnerQueryBuilder().toFilter(this); Query filter = QueryBuilder.rewriteQuery(parseContext().parseInnerQueryBuilder(), this).toFilter(this);
if (filter == null) { if (filter == null) {
return null; return null;
} }
@ -425,12 +390,16 @@ public class QueryShardContext {
} }
} }
private static Query toQuery(QueryBuilder<?> queryBuilder, QueryShardContext context) throws IOException { private static Query toQuery(final QueryBuilder<?> queryBuilder, final QueryShardContext context) throws IOException {
Query query = queryBuilder.toQuery(context); final Query query = QueryBuilder.rewriteQuery(queryBuilder, context).toQuery(context);
if (query == null) { if (query == null) {
query = Queries.newMatchNoDocsQuery(); return Queries.newMatchNoDocsQuery();
} }
return query; return query;
} }
public final Index index() {
return indexSettings.getIndex();
}
} }

View File

@ -25,11 +25,13 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; 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.ScriptService;
import org.elasticsearch.script.Template; import org.elasticsearch.script.Template;
import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -100,14 +102,7 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil
@Override @Override
protected Query doToQuery(QueryShardContext context) throws IOException { protected Query doToQuery(QueryShardContext context) throws IOException {
BytesReference querySource = context.executeQueryTemplate(template); throw new UnsupportedOperationException("this query must be rewritten first");
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);
}
} }
@Override @Override
@ -130,4 +125,22 @@ public class TemplateQueryBuilder extends AbstractQueryBuilder<TemplateQueryBuil
protected boolean doEquals(TemplateQueryBuilder other) { protected boolean doEquals(TemplateQueryBuilder other) {
return Objects.equals(template, other.template); return Objects.equals(template, other.template);
} }
@Override
protected QueryBuilder<?> doRewrite(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 (boost() != DEFAULT_BOOST || queryName() != null) {
final BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(queryBuilder);
return boolQueryBuilder;
}
return queryBuilder;
}
}
} }

View File

@ -226,22 +226,13 @@ public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> {
@Override @Override
protected Query doToQuery(QueryShardContext context) throws IOException { protected Query doToQuery(QueryShardContext context) throws IOException {
List<Object> terms; if (termsLookup != null) {
TermsLookup termsLookup = null; throw new UnsupportedOperationException("query must be rewritten first");
if (this.termsLookup != null) {
termsLookup = new TermsLookup(this.termsLookup);
if (termsLookup.index() == null) {
termsLookup.index(context.index().getName());
} }
Client client = context.getClient(); if (values == null || values.isEmpty()) {
terms = fetch(termsLookup, client);
} else {
terms = values;
}
if (terms == null || terms.isEmpty()) {
return Queries.newMatchNoDocsQuery(); return Queries.newMatchNoDocsQuery();
} }
return handleTermsQuery(terms, fieldName, context); return handleTermsQuery(values, fieldName, context);
} }
private List<Object> fetch(TermsLookup termsLookup, Client client) { private List<Object> fetch(TermsLookup termsLookup, Client client) {
@ -323,4 +314,22 @@ public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> {
Objects.equals(values, other.values) && Objects.equals(values, other.values) &&
Objects.equals(termsLookup, other.termsLookup); Objects.equals(termsLookup, other.termsLookup);
} }
@Override
protected QueryBuilder<?> doRewrite(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.getIndexSettings() != null) {
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);
}
return this;
}
} }

View File

@ -105,14 +105,7 @@ public class WrapperQueryBuilder extends AbstractQueryBuilder<WrapperQueryBuilde
@Override @Override
protected Query doToQuery(QueryShardContext context) throws IOException { protected Query doToQuery(QueryShardContext context) throws IOException {
try (XContentParser qSourceParser = XContentFactory.xContent(source).createParser(source)) { throw new UnsupportedOperationException("this query must be rewritten first");
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);
}
} }
@Override @Override
@ -134,4 +127,22 @@ public class WrapperQueryBuilder extends AbstractQueryBuilder<WrapperQueryBuilde
protected boolean doEquals(WrapperQueryBuilder other) { protected boolean doEquals(WrapperQueryBuilder other) {
return Arrays.equals(source, other.source); // otherwise we compare pointers return Arrays.equals(source, other.source); // otherwise we compare pointers
} }
@Override
protected QueryBuilder<?> doRewrite(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 (boost() != DEFAULT_BOOST || queryName() != null) {
final BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(queryBuilder);
return boolQueryBuilder;
}
return queryBuilder;
}
}
} }

View File

@ -34,6 +34,7 @@ import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.EmptyQueryBuilder; import org.elasticsearch.index.query.EmptyQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryRewriteContext;
import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.functionscore.random.RandomScoreFunctionBuilder; import org.elasticsearch.index.query.functionscore.random.RandomScoreFunctionBuilder;
@ -393,5 +394,33 @@ public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScor
public FilterFunctionBuilder readFrom(StreamInput in) throws IOException { public FilterFunctionBuilder readFrom(StreamInput in) throws IOException {
return new FilterFunctionBuilder(in.readQuery(), in.readScoreFunction()); return new FilterFunctionBuilder(in.readQuery(), in.readScoreFunction());
} }
public FilterFunctionBuilder rewrite(QueryRewriteContext context) throws IOException {
QueryBuilder<?> rewrite = filter.rewrite(context);
if (rewrite != filter) {
return new FilterFunctionBuilder(rewrite, scoreFunction);
}
return this;
}
}
@Override
protected QueryBuilder<?> doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder<?> queryBuilder = this.query.rewrite(queryRewriteContext);
FilterFunctionBuilder[] rewrittenBuilders = new FilterFunctionBuilder[this.filterFunctionBuilders.length];
boolean rewritten = false;
for (int i = 0; i < rewrittenBuilders.length; i++) {
FilterFunctionBuilder rewrite = filterFunctionBuilders[i].rewrite(queryRewriteContext);
rewritten |= rewrite != filterFunctionBuilders[i];
rewrittenBuilders[i] = rewrite;
}
if (queryBuilder != query || rewritten) {
FunctionScoreQueryBuilder newQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder, rewrittenBuilders);
newQueryBuilder.scoreMode = scoreMode;
newQueryBuilder.minScore = minScore;
newQueryBuilder.maxBoost = maxBoost;
return newQueryBuilder;
}
return this;
} }
} }

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.object.ObjectMapper; import org.elasticsearch.index.mapper.object.ObjectMapper;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.index.query.QueryShardException;
@ -91,7 +92,8 @@ public class NestedInnerQueryParseSupport {
if (path != null) { if (path != null) {
setPathLevel(); setPathLevel();
try { try {
innerFilter = parseContext.parseInnerQueryBuilder().toFilter(this.shardContext); innerFilter = QueryBuilder.rewriteQuery(parseContext.parseInnerQueryBuilder(),
this.shardContext).toFilter(this.shardContext);
} finally { } finally {
resetPathLevel(); resetPathLevel();
} }
@ -147,7 +149,8 @@ public class NestedInnerQueryParseSupport {
try { try {
XContentParser innerParser = XContentHelper.createParser(source); XContentParser innerParser = XContentHelper.createParser(source);
parseContext.parser(innerParser); parseContext.parser(innerParser);
innerFilter = parseContext.parseInnerQueryBuilder().toFilter(this.shardContext); innerFilter = QueryBuilder.rewriteQuery(parseContext.parseInnerQueryBuilder(),
this.shardContext).toFilter(this.shardContext);
filterParsed = true; filterParsed = true;
return innerFilter; return innerFilter;
} finally { } finally {

View File

@ -248,7 +248,7 @@ public class IndexShard extends AbstractIndexShardComponent {
this.engineConfig = newEngineConfig(translogConfig, cachingPolicy); this.engineConfig = newEngineConfig(translogConfig, cachingPolicy);
this.suspendableRefContainer = new SuspendableRefContainer(); this.suspendableRefContainer = new SuspendableRefContainer();
this.searcherWrapper = indexSearcherWrapper; 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); this.percolatorQueriesRegistry = new PercolatorQueriesRegistry(shardId, indexSettings, queryShardContext);
} }

View File

@ -489,6 +489,10 @@ public class ScriptService extends AbstractComponent implements Closeable {
return scriptMetrics.stats(); return scriptMetrics.stats();
} }
public Client getClient() {
return client;
}
/** /**
* A small listener for the script cache that calls each * A small listener for the script cache that calls each
* {@code ScriptEngineService}'s {@code scriptRemoved} method when the * {@code ScriptEngineService}'s {@code scriptRemoved} method when the

View File

@ -532,8 +532,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); DefaultSearchContext context = new DefaultSearchContext(idGenerator.incrementAndGet(), request, shardTarget, engineSearcher, indexService, indexShard, scriptService, pageCacheRecycler, bigArrays, threadPool.estimatedTimeInMillisCounter(), parseFieldMatcher, defaultSearchTimeout);
SearchContext.setCurrent(context); SearchContext.setCurrent(context);
try { try {
if (request.source() != null) {
request.source().rewrite(context.getQueryShardContext());
}
if (request.scroll() != null) { if (request.scroll() != null) {
context.scrollContext(new ScrollContext()); context.scrollContext(new ScrollContext());
context.scrollContext().scroll = request.scroll(); context.scrollContext().scroll = request.scroll();

View File

@ -81,7 +81,6 @@ public class SearchServiceTransportAction extends AbstractComponent {
super(settings); super(settings);
this.transportService = transportService; this.transportService = transportService;
this.searchService = searchService; this.searchService = searchService;
transportService.registerRequestHandler(FREE_CONTEXT_SCROLL_ACTION_NAME, ScrollFreeContextRequest::new, ThreadPool.Names.SAME, new FreeContextTransportHandler<>()); 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(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()); transportService.registerRequestHandler(CLEAR_SCROLL_CONTEXTS_ACTION_NAME, ClearScrollContextsRequest::new, ThreadPool.Names.SAME, new ClearScrollContextsTransportHandler());

View File

@ -40,6 +40,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.query.QueryRewriteContext;
import org.elasticsearch.script.Script; import org.elasticsearch.script.Script;
import org.elasticsearch.search.searchafter.SearchAfterBuilder; import org.elasticsearch.search.searchafter.SearchAfterBuilder;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder; 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(version, other.version)
&& Objects.equals(profile, other.profile); && 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);
}
}
} }

View File

@ -355,7 +355,7 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
targetOptionsBuilder.options(highlighterBuilder.options); targetOptionsBuilder.options(highlighterBuilder.options);
} }
if (highlighterBuilder.highlightQuery != null) { if (highlighterBuilder.highlightQuery != null) {
targetOptionsBuilder.highlightQuery(highlighterBuilder.highlightQuery.toQuery(context)); targetOptionsBuilder.highlightQuery(QueryBuilder.rewriteQuery(highlighterBuilder.highlightQuery, context).toQuery(context));
} }
} }

View File

@ -149,7 +149,7 @@ public class QueryRescorerBuilder extends RescoreBuilder<QueryRescorerBuilder> {
public QueryRescoreContext build(QueryShardContext context) throws IOException { public QueryRescoreContext build(QueryShardContext context) throws IOException {
org.elasticsearch.search.rescore.QueryRescorer rescorer = new org.elasticsearch.search.rescore.QueryRescorer(); org.elasticsearch.search.rescore.QueryRescorer rescorer = new org.elasticsearch.search.rescore.QueryRescorer();
QueryRescoreContext queryRescoreContext = new QueryRescoreContext(rescorer); 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.setQueryWeight(this.queryWeight);
queryRescoreContext.setRescoreQueryWeight(this.rescoreQueryWeight); queryRescoreContext.setRescoreQueryWeight(this.rescoreQueryWeight);
queryRescoreContext.setScoreMode(this.scoreMode); queryRescoreContext.setScoreMode(this.scoreMode);

View File

@ -23,6 +23,7 @@ import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator;
import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.io.JsonStringEncoder; import com.fasterxml.jackson.core.io.JsonStringEncoder;
import org.apache.lucene.index.memory.MemoryIndex;
import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermQuery;
@ -286,7 +287,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
} }
}); });
indicesQueriesRegistry = injector.getInstance(IndicesQueriesRegistry.class); 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 //create some random type with some default field, those types will stick around for all of the subclasses
currentTypes = new String[randomIntBetween(0, 5)]; currentTypes = new String[randomIntBetween(0, 5)];
for (int i = 0; i < currentTypes.length; i++) { for (int i = 0; i < currentTypes.length; i++) {
@ -516,7 +517,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
QB firstQuery = createTestQueryBuilder(); QB firstQuery = createTestQueryBuilder();
QB controlQuery = copyQuery(firstQuery); QB controlQuery = copyQuery(firstQuery);
setSearchContext(randomTypes); // only set search context for toQuery to be more realistic 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); assertLuceneQuery(firstQuery, firstLuceneQuery, context);
SearchContext.removeCurrent(); // remove after assertLuceneQuery since the assertLuceneQuery impl might access the context as well SearchContext.removeCurrent(); // remove after assertLuceneQuery since the assertLuceneQuery impl might access the context as well
assertTrue( assertTrue(
@ -534,24 +535,31 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
+ randomAsciiOfLengthBetween(1, 10)); + randomAsciiOfLengthBetween(1, 10));
} }
setSearchContext(randomTypes); setSearchContext(randomTypes);
Query secondLuceneQuery = secondQuery.toQuery(context); Query secondLuceneQuery = rewriteQuery(secondQuery, context).toQuery(context);
assertLuceneQuery(secondQuery, secondLuceneQuery, context); assertLuceneQuery(secondQuery, secondLuceneQuery, context);
SearchContext.removeCurrent(); SearchContext.removeCurrent();
assertThat("two equivalent query builders lead to different lucene queries", secondLuceneQuery, equalTo(firstLuceneQuery)); assertEquals("two equivalent query builders lead to different lucene queries", rewrite(secondLuceneQuery), rewrite(firstLuceneQuery));
// if the initial lucene query is null, changing its boost won't have any effect, we shouldn't test that // if the initial lucene query is null, changing its boost won't have any effect, we shouldn't test that
if (firstLuceneQuery != null && supportsBoostAndQueryName()) { if (firstLuceneQuery != null && supportsBoostAndQueryName()) {
secondQuery.boost(firstQuery.boost() + 1f + randomFloat()); secondQuery.boost(firstQuery.boost() + 1f + randomFloat());
setSearchContext(randomTypes); setSearchContext(randomTypes);
Query thirdLuceneQuery = secondQuery.toQuery(context); Query thirdLuceneQuery = rewriteQuery(secondQuery, context).toQuery(context);
SearchContext.removeCurrent(); SearchContext.removeCurrent();
assertThat("modifying the boost doesn't affect the corresponding lucene query", firstLuceneQuery, assertNotEquals("modifying the boost doesn't affect the corresponding lucene query", rewrite(firstLuceneQuery),
not(equalTo(thirdLuceneQuery))); rewrite(thirdLuceneQuery));
} }
} }
} }
private QueryBuilder<?> rewriteQuery(QB queryBuilder, QueryRewriteContext rewriteContext) throws IOException {
QueryBuilder<?> rewritten = QueryBuilder.rewriteQuery(queryBuilder, rewriteContext);
// extra safety to fail fast - serialize the rewritten version to ensure it's serializable.
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. * 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 * This method allows to disable boost and queryName related tests for those queries. Those queries are easy to identify: their parsers
@ -625,11 +633,13 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
* Serialize the given query builder and asserts that both are equal * Serialize the given query builder and asserts that both are equal
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected QB assertSerialization(QB testQuery) throws IOException { protected <QB extends QueryBuilder> QB assertSerialization(QB testQuery) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { try (BytesStreamOutput output = new BytesStreamOutput()) {
testQuery.writeTo(output); testQuery.writeTo(output);
try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) { 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); QueryBuilder<?> deserializedQuery = prototype.readFrom(in);
assertEquals(deserializedQuery, testQuery); assertEquals(deserializedQuery, testQuery);
assertEquals(deserializedQuery.hashCode(), testQuery.hashCode()); assertEquals(deserializedQuery.hashCode(), testQuery.hashCode());
@ -674,7 +684,26 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
} }
private QueryParser<?> queryParser(String queryId) { 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 //we use the streaming infra to create a copy of the query provided as argument
@ -948,4 +977,21 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
} }
return ""; return "";
} }
/**
* This test ensures that queries that need to be rewritten have dedicated tests.
* These queries must override this method accordingly.
*/
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
QB queryBuilder = createTestQueryBuilder();
setSearchContext(randomTypes); // only set search context for toQuery to be more realistic
queryBuilder.toQuery(context);
}
protected Query rewrite(Query query) throws IOException {
return query;
}
} }

View File

@ -343,4 +343,70 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
assertEquals(query, "23", queryBuilder.minimumShouldMatch()); assertEquals(query, "23", queryBuilder.minimumShouldMatch());
assertEquals(query, "kimchy", ((TermQueryBuilder)queryBuilder.must().get(0)).value()); assertEquals(query, "kimchy", ((TermQueryBuilder)queryBuilder.must().get(0)).value());
} }
public void testRewrite() throws IOException {
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolean mustRewrite = false;
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.must(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "must").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.should(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "should").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.filter(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "filter").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.mustNot(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "must_not").toString()));
}
if (mustRewrite == false && randomBoolean()) {
boolQueryBuilder.must(new TermsQueryBuilder("foo", "no_rewrite"));
}
QueryBuilder<?> rewritten = boolQueryBuilder.rewrite(queryShardContext());
if (mustRewrite == false && boolQueryBuilder.must().isEmpty()) {
// if it's empty we rewrite to match all
assertEquals(rewritten, new MatchAllQueryBuilder());
} else {
BoolQueryBuilder rewrite = (BoolQueryBuilder) rewritten;
if (mustRewrite) {
assertNotSame(rewrite, boolQueryBuilder);
if (boolQueryBuilder.must().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "must"), rewrite.must().get(0));
}
if (boolQueryBuilder.should().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "should"), rewrite.should().get(0));
}
if (boolQueryBuilder.mustNot().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "must_not"), rewrite.mustNot().get(0));
}
if (boolQueryBuilder.filter().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "filter"), rewrite.filter().get(0));
}
} else {
assertSame(rewrite, boolQueryBuilder);
if (boolQueryBuilder.must().isEmpty() == false) {
assertSame(boolQueryBuilder.must().get(0), rewrite.must().get(0));
}
}
}
}
public void testRewriteMultipleTimes() throws IOException {
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchAllQueryBuilder().toString()).toString()));
QueryBuilder<?> rewritten = boolQueryBuilder.rewrite(queryShardContext());
BoolQueryBuilder expected = new BoolQueryBuilder();
expected.must(new WrapperQueryBuilder(new MatchAllQueryBuilder().toString()));
assertEquals(expected, rewritten);
expected = new BoolQueryBuilder();
expected.must(new MatchAllQueryBuilder());
QueryBuilder<?> rewrittenAgain = rewritten.rewrite(queryShardContext());
assertEquals(rewrittenAgain, expected);
assertEquals(QueryBuilder.rewriteQuery(boolQueryBuilder, queryShardContext()), expected);
}
} }

View File

@ -20,6 +20,7 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import org.apache.lucene.queries.BoostingQuery; import org.apache.lucene.queries.BoostingQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import java.io.IOException; import java.io.IOException;
@ -103,4 +104,17 @@ public class BoostingQueryBuilderTests extends AbstractQueryTestCase<BoostingQue
assertEquals(query, 8, queryBuilder.negativeQuery().boost(), 0.00001); assertEquals(query, 8, queryBuilder.negativeQuery().boost(), 0.00001);
assertEquals(query, 5, queryBuilder.positiveQuery().boost(), 0.00001); assertEquals(query, 5, queryBuilder.positiveQuery().boost(), 0.00001);
} }
public void testRewrite() throws IOException {
QueryBuilder positive = randomBoolean() ? new MatchAllQueryBuilder() : new WrapperQueryBuilder(new TermQueryBuilder("pos", "bar").toString());
QueryBuilder negative = randomBoolean() ? new MatchAllQueryBuilder() : new WrapperQueryBuilder(new TermQueryBuilder("neg", "bar").toString());
BoostingQueryBuilder qb = new BoostingQueryBuilder(positive, negative);
QueryBuilder<?> rewrite = qb.rewrite(queryShardContext());
if (positive instanceof MatchAllQueryBuilder && negative instanceof MatchAllQueryBuilder) {
assertSame(rewrite, qb);
} else {
assertNotSame(rewrite, qb);
assertEquals(new BoostingQueryBuilder(positive.rewrite(queryShardContext()), negative.rewrite(queryShardContext())), rewrite);
}
}
} }

View File

@ -468,4 +468,10 @@ public class GeoBoundingBoxQueryBuilderTests extends AbstractQueryTestCase<GeoBo
assertEquals(json, 1.0, parsed.boost(), 0.0001); assertEquals(json, 1.0, parsed.boost(), 0.0001);
assertEquals(json, GeoExecType.MEMORY, parsed.type()); assertEquals(json, GeoExecType.MEMORY, parsed.type());
} }
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
} }

View File

@ -411,4 +411,10 @@ public class GeoDistanceQueryBuilderTests extends AbstractQueryTestCase<GeoDista
assertEquals(json, 40.0, parsed.point().getLat(), 0.0001); assertEquals(json, 40.0, parsed.point().getLat(), 0.0001);
assertEquals(json, 12000.0, parsed.distance(), 0.0001); assertEquals(json, 12000.0, parsed.distance(), 0.0001);
} }
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
} }

View File

@ -316,4 +316,10 @@ public class GeoDistanceRangeQueryTests extends AbstractQueryTestCase<GeoDistanc
checkGeneratedJson(json, parsed); checkGeneratedJson(json, parsed);
assertEquals(json, -70.0, parsed.point().lon(), 0.0001); assertEquals(json, -70.0, parsed.point().lon(), 0.0001);
} }
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
} }

View File

@ -343,4 +343,10 @@ public class GeoPolygonQueryBuilderTests extends AbstractQueryTestCase<GeoPolygo
checkGeneratedJson(json, parsed); checkGeneratedJson(json, parsed);
assertEquals(json, 4, parsed.points().size()); assertEquals(json, 4, parsed.points().size());
} }
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
} }

View File

@ -240,4 +240,24 @@ public class GeoShapeQueryBuilderTests extends AbstractQueryTestCase<GeoShapeQue
checkGeneratedJson(json, parsed); checkGeneratedJson(json, parsed);
assertEquals(json, 42.0, parsed.boost(), 0.0001); assertEquals(json, 42.0, parsed.boost(), 0.0001);
} }
@Override
public void testMustRewrite() throws IOException {
GeoShapeQueryBuilder sqb;
do {
sqb = doCreateTestQueryBuilder();
// do this until we get one without a shape
} while (sqb.shape() != null);
try {
sqb.toQuery(queryShardContext());
fail();
} catch (UnsupportedOperationException e) {
assertEquals("query must be rewritten first", e.getMessage());
}
QueryBuilder<?> rewrite = sqb.rewrite(queryShardContext());
GeoShapeQueryBuilder geoShapeQueryBuilder = new GeoShapeQueryBuilder(GEO_SHAPE_FIELD_NAME, indexedShapeToReturn);
geoShapeQueryBuilder.strategy(sqb.strategy());
geoShapeQueryBuilder.relation(sqb.relation());
assertEquals(geoShapeQueryBuilder, rewrite);
}
} }

View File

@ -145,4 +145,10 @@ public class GeohashCellQueryBuilderTests extends AbstractQueryTestCase<Builder>
checkGeneratedJson(json, parsed); checkGeneratedJson(json, parsed);
assertEquals(json, 3, parsed.precision().intValue()); assertEquals(json, 3, parsed.precision().intValue());
} }
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
} }

View File

@ -21,15 +21,12 @@ package org.elasticsearch.index.query;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.core.StringFieldMapper; import org.elasticsearch.index.mapper.core.StringFieldMapper;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import java.util.Collections;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
@ -50,7 +47,7 @@ public class QueryShardContextTests extends ESTestCase {
MapperService mapperService = mock(MapperService.class); MapperService mapperService = mock(MapperService.class);
when(mapperService.getIndexSettings()).thenReturn(indexSettings); when(mapperService.getIndexSettings()).thenReturn(indexSettings);
QueryShardContext context = new QueryShardContext( QueryShardContext context = new QueryShardContext(
indexSettings, null, null, null, mapperService, null, null, null indexSettings, null, null, mapperService, null, null, null
); );
context.setAllowUnmappedFields(false); context.setAllowUnmappedFields(false);

View File

@ -19,9 +19,12 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import org.apache.lucene.index.memory.MemoryIndex;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.script.Script.ScriptParseException; import org.elasticsearch.script.Script.ScriptParseException;
import org.elasticsearch.script.ScriptService.ScriptType; import org.elasticsearch.script.ScriptService.ScriptType;
@ -29,6 +32,7 @@ import org.elasticsearch.script.Template;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -56,7 +60,7 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
@Override @Override
protected void doAssertLuceneQuery(TemplateQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException { protected void doAssertLuceneQuery(TemplateQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
assertEquals(templateBase.toQuery(context), query); assertEquals(rewrite(QueryBuilder.rewriteQuery(templateBase, context).toQuery(context)), rewrite(query));
} }
public void testIllegalArgument() { public void testIllegalArgument() {
@ -118,4 +122,53 @@ public class TemplateQueryBuilderTests extends AbstractQueryTestCase<TemplateQue
XContentType.JSON, params)); XContentType.JSON, params));
assertParsedQuery(query, expectedBuilder); assertParsedQuery(query, expectedBuilder);
} }
@Override
public void testMustRewrite() throws IOException {
String query = "{ \"match_all\" : {}}";
QueryBuilder<?> builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap()));
try {
builder.toQuery(queryShardContext());
fail();
} catch (UnsupportedOperationException ex) {
assertEquals("this query must be rewritten first", ex.getMessage());
}
assertEquals(new MatchAllQueryBuilder(), builder.rewrite(queryShardContext()));
}
public void testRewriteWithInnerName() throws IOException {
final String query = "{ \"match_all\" : {\"_name\" : \"foobar\"}}";
QueryBuilder<?> builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap()));
assertEquals(new MatchAllQueryBuilder().queryName("foobar"), builder.rewrite(queryShardContext()));
builder = new TemplateQueryBuilder(new Template(query, ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap())).queryName("outer");
assertEquals(new BoolQueryBuilder().must(new MatchAllQueryBuilder().queryName("foobar")).queryName("outer"),
builder.rewrite(queryShardContext()));
}
public void testRewriteWithInnerBoost() throws IOException {
final TermQueryBuilder query = new TermQueryBuilder("foo", "bar").boost(2);
QueryBuilder<?> builder = new TemplateQueryBuilder(new Template(query.toString(), ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap()));
assertEquals(query, builder.rewrite(queryShardContext()));
builder = new TemplateQueryBuilder(new Template(query.toString(), ScriptType.INLINE, "mockscript",
XContentType.JSON, Collections.emptyMap())).boost(3);
assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(queryShardContext()));
}
@Override
protected Query rewrite(Query query) throws IOException {
// TemplateQueryBuilder adds some optimization if the template and query builder have boosts / query names that wraps
// the actual QueryBuilder that comes from the template into a BooleanQueryBuilder to give it an outer boost / name
// this causes some queries to be not exactly equal but equivalent such that we need to rewrite them before comparing.
if (query != null) {
MemoryIndex idx = new MemoryIndex();
return idx.createSearcher().rewrite(query);
}
return new MatchAllDocsQuery(); // null == *:*
}
} }

View File

@ -41,6 +41,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ -272,5 +273,18 @@ public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuil
assertEquals(json, 2, parsed.values().size()); assertEquals(json, 2, parsed.values().size());
} }
@Override
public void testMustRewrite() throws IOException {
TermsQueryBuilder termsQueryBuilder = new TermsQueryBuilder(STRING_FIELD_NAME, randomTermsLookup());
try {
termsQueryBuilder.toQuery(queryShardContext());
fail();
} catch (UnsupportedOperationException ex) {
assertEquals("query must be rewritten first", ex.getMessage());
}
assertEquals(termsQueryBuilder.rewrite(queryShardContext()), new TermsQueryBuilder(STRING_FIELD_NAME,
randomTerms.stream().filter(x -> x != null).collect(Collectors.toList()))); // terms lookup removes null values
}
} }

View File

@ -19,19 +19,17 @@
package org.elasticsearch.index.query; package org.elasticsearch.index.query;
import org.apache.lucene.index.memory.MemoryIndex;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.elasticsearch.action.support.ToXContentToBytes; import org.elasticsearch.action.support.ToXContentToBytes;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import static org.hamcrest.Matchers.equalTo;
public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQueryBuilder> { public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQueryBuilder> {
@Override @Override
@ -56,13 +54,9 @@ public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQuery
@Override @Override
protected void doAssertLuceneQuery(WrapperQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException { protected void doAssertLuceneQuery(WrapperQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
try (XContentParser qSourceParser = XContentFactory.xContent(queryBuilder.source()).createParser(queryBuilder.source())) { QueryBuilder<?> innerQuery = queryBuilder.rewrite(queryShardContext());
final QueryShardContext contextCopy = new QueryShardContext(context); Query expected = rewrite(innerQuery.toQuery(context));
contextCopy.reset(qSourceParser); assertEquals(rewrite(query), expected);
QueryBuilder<?> innerQuery = contextCopy.parseContext().parseInnerQueryBuilder();
Query expected = innerQuery.toQuery(context);
assertThat(query, equalTo(expected));
}
} }
public void testIllegalArgument() { public void testIllegalArgument() {
@ -133,4 +127,47 @@ public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQuery
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
@Override
public void testMustRewrite() throws IOException {
TermQueryBuilder tqb = new TermQueryBuilder("foo", "bar");
WrapperQueryBuilder qb = new WrapperQueryBuilder(tqb.toString());
try {
qb.toQuery(queryShardContext());
fail();
} catch (UnsupportedOperationException e) {
assertEquals("this query must be rewritten first", e.getMessage());
}
QueryBuilder<?> rewrite = qb.rewrite(queryShardContext());
assertEquals(tqb, rewrite);
}
public void testRewriteWithInnerName() throws IOException {
QueryBuilder<?> builder = new WrapperQueryBuilder("{ \"match_all\" : {\"_name\" : \"foobar\"}}");
assertEquals(new MatchAllQueryBuilder().queryName("foobar"), builder.rewrite(queryShardContext()));
builder = new WrapperQueryBuilder("{ \"match_all\" : {\"_name\" : \"foobar\"}}").queryName("outer");
assertEquals(new BoolQueryBuilder().must(new MatchAllQueryBuilder().queryName("foobar")).queryName("outer"),
builder.rewrite(queryShardContext()));
}
public void testRewriteWithInnerBoost() throws IOException {
final TermQueryBuilder query = new TermQueryBuilder("foo", "bar").boost(2);
QueryBuilder<?> builder = new WrapperQueryBuilder(query.toString());
assertEquals(query, builder.rewrite(queryShardContext()));
builder = new WrapperQueryBuilder(query.toString()).boost(3);
assertEquals(new BoolQueryBuilder().must(query).boost(3), builder.rewrite(queryShardContext()));
}
@Override
protected Query rewrite(Query query) throws IOException {
// WrapperQueryBuilder adds some optimization if the wrapper and query builder have boosts / query names that wraps
// the actual QueryBuilder that comes from the binary blob into a BooleanQueryBuilder to give it an outer boost / name
// this causes some queries to be not exactly equal but equivalent such that we need to rewrite them before comparing.
if (query != null) {
MemoryIndex idx = new MemoryIndex();
return idx.createSearcher().rewrite(query);
}
return new MatchAllDocsQuery(); // null == *:*
}
} }

View File

@ -39,6 +39,7 @@ import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.RandomQueryBuilder; import org.elasticsearch.index.query.RandomQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.WrapperQueryBuilder;
import org.elasticsearch.index.query.functionscore.exp.ExponentialDecayFunctionBuilder; import org.elasticsearch.index.query.functionscore.exp.ExponentialDecayFunctionBuilder;
import org.elasticsearch.index.query.functionscore.fieldvaluefactor.FieldValueFactorFunctionBuilder; import org.elasticsearch.index.query.functionscore.fieldvaluefactor.FieldValueFactorFunctionBuilder;
import org.elasticsearch.index.query.functionscore.gauss.GaussDecayFunctionBuilder; import org.elasticsearch.index.query.functionscore.gauss.GaussDecayFunctionBuilder;
@ -643,6 +644,35 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
assertEquals(json, 1, parsed.getMinScore(), 0.0001); assertEquals(json, 1, parsed.getMinScore(), 0.0001);
} }
@Override
public void testMustRewrite() throws IOException {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
super.testMustRewrite();
}
public void testRewrite() throws IOException {
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(new WrapperQueryBuilder(new TermQueryBuilder("foo", "bar").toString()));
FunctionScoreQueryBuilder rewrite = (FunctionScoreQueryBuilder) functionScoreQueryBuilder.rewrite(queryShardContext());
assertNotSame(functionScoreQueryBuilder, rewrite);
assertEquals(rewrite.query(), new TermQueryBuilder("foo", "bar"));
}
public void testRewriteWithFunction() throws IOException {
TermQueryBuilder secondFunction = new TermQueryBuilder("tq", "2");
QueryBuilder queryBuilder = randomBoolean() ? new WrapperQueryBuilder(new TermQueryBuilder("foo", "bar").toString()) : new TermQueryBuilder("foo", "bar");
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder,
new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
new FunctionScoreQueryBuilder.FilterFunctionBuilder(new WrapperQueryBuilder(new TermQueryBuilder("tq", "1").toString()), new RandomScoreFunctionBuilder()),
new FunctionScoreQueryBuilder.FilterFunctionBuilder(secondFunction, new RandomScoreFunctionBuilder())
});
FunctionScoreQueryBuilder rewrite = (FunctionScoreQueryBuilder) functionScoreQueryBuilder.rewrite(queryShardContext());
assertNotSame(functionScoreQueryBuilder, rewrite);
assertEquals(rewrite.query(), new TermQueryBuilder("foo", "bar"));
assertEquals(rewrite.filterFunctionBuilders()[0].getFilter(), new TermQueryBuilder("tq", "1"));
assertSame(rewrite.filterFunctionBuilders()[1].getFilter(), secondFunction);
}
public void testQueryMalformedArrayNotSupported() throws IOException { public void testQueryMalformedArrayNotSupported() throws IOException {
String json = String json =
"{\n" + "{\n" +

View File

@ -84,7 +84,7 @@ public class PercolateDocumentParserTests extends ESTestCase {
Map<String, QueryParser<?>> parsers = singletonMap("term", new TermQueryParser()); Map<String, QueryParser<?>> parsers = singletonMap("term", new TermQueryParser());
IndicesQueriesRegistry indicesQueriesRegistry = new IndicesQueriesRegistry(indexSettings.getSettings(), parsers); 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()); HighlightPhase highlightPhase = new HighlightPhase(Settings.EMPTY, new Highlighters());
AggregatorParsers aggregatorParsers = new AggregatorParsers(Collections.emptySet(), Collections.emptySet()); AggregatorParsers aggregatorParsers = new AggregatorParsers(Collections.emptySet(), Collections.emptySet());

View File

@ -41,9 +41,14 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.index.query.AbstractQueryTestCase; import org.elasticsearch.index.query.AbstractQueryTestCase;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.EmptyQueryBuilder; import org.elasticsearch.index.query.EmptyQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryParseContext; 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.index.query.functionscore.ScoreFunctionParser;
import org.elasticsearch.indices.query.IndicesQueriesRegistry; import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.script.Script; import org.elasticsearch.script.Script;
@ -483,4 +488,14 @@ public class SearchSourceBuilderTests extends ESTestCase {
String query = "{ \"post_filter\": {} }"; String query = "{ \"post_filter\": {} }";
assertParseSearchSource(builder, new BytesArray(query)); 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());
}
} }

View File

@ -277,7 +277,7 @@ public class HighlightBuilderTests extends ESTestCase {
Index index = new Index(randomAsciiOfLengthBetween(1, 10), "_na_"); Index index = new Index(randomAsciiOfLengthBetween(1, 10), "_na_");
IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(index, indexSettings); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(index, indexSettings);
// shard context will only need indicesQueriesRegistry for building Query objects nested in highlighter // 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 @Override
public MappedFieldType fieldMapper(String name) { public MappedFieldType fieldMapper(String name) {
StringFieldMapper.Builder builder = new StringFieldMapper.Builder(name); StringFieldMapper.Builder builder = new StringFieldMapper.Builder(name);

View File

@ -159,7 +159,7 @@ public class QueryRescoreBuilderTests extends ESTestCase {
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(); .put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(randomAsciiOfLengthBetween(1, 10), indexSettings); IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(randomAsciiOfLengthBetween(1, 10), indexSettings);
// shard context will only need indicesQueriesRegistry for building Query objects nested in query rescorer // 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 @Override
public MappedFieldType fieldMapper(String name) { public MappedFieldType fieldMapper(String name) {
StringFieldMapper.Builder builder = new StringFieldMapper.Builder(name); StringFieldMapper.Builder builder = new StringFieldMapper.Builder(name);
@ -169,7 +169,7 @@ public class QueryRescoreBuilderTests extends ESTestCase {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
RescoreBuilder<?> rescoreBuilder = randomRescoreBuilder(); RescoreBuilder<?> rescoreBuilder = randomRescoreBuilder();
QueryRescoreContext rescoreContext = (QueryRescoreContext) rescoreBuilder.build(mockShardContext); QueryRescoreContext rescoreContext = rescoreBuilder.build(mockShardContext);
XContentParser parser = createParser(rescoreBuilder); XContentParser parser = createParser(rescoreBuilder);
QueryRescoreContext parsedRescoreContext = (QueryRescoreContext) new RescoreParseElement().parseSingleRescoreContext(parser, mockShardContext); QueryRescoreContext parsedRescoreContext = (QueryRescoreContext) new RescoreParseElement().parseSingleRescoreContext(parser, mockShardContext);

View File

@ -170,7 +170,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 @Override
public MappedFieldType fieldMapper(String name) { public MappedFieldType fieldMapper(String name) {
StringFieldMapper.Builder builder = new StringFieldMapper.Builder(name); StringFieldMapper.Builder builder = new StringFieldMapper.Builder(name);

View File

@ -46,6 +46,7 @@ import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.fielddata.IndexFieldDataCache;
import org.elasticsearch.index.fielddata.IndexFieldDataService; import org.elasticsearch.index.fielddata.IndexFieldDataService;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.TemplateQueryParser; import org.elasticsearch.index.query.TemplateQueryParser;
import org.elasticsearch.index.query.functionscore.ScoreFunctionParser; import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
@ -152,7 +153,7 @@ public class TemplateQueryParserTests extends ESTestCase {
} }
}); });
IndicesQueriesRegistry indicesQueriesRegistry = injector.getInstance(IndicesQueriesRegistry.class); IndicesQueriesRegistry indicesQueriesRegistry = injector.getInstance(IndicesQueriesRegistry.class);
context = new QueryShardContext(idxSettings, proxy, bitsetFilterCache, indexFieldDataService, mapperService, similarityService, scriptService, indicesQueriesRegistry); context = new QueryShardContext(idxSettings, bitsetFilterCache, indexFieldDataService, mapperService, similarityService, scriptService, indicesQueriesRegistry);
} }
@Override @Override
@ -170,7 +171,7 @@ public class TemplateQueryParserTests extends ESTestCase {
templateSourceParser.nextToken(); templateSourceParser.nextToken();
TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class); TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class);
Query query = parser.fromXContent(context.parseContext()).toQuery(context); Query query = QueryBuilder.rewriteQuery(parser.fromXContent(context.parseContext()), context).toQuery(context);
assertTrue("Parsing template query failed.", query instanceof MatchAllDocsQuery); assertTrue("Parsing template query failed.", query instanceof MatchAllDocsQuery);
} }
@ -181,7 +182,7 @@ public class TemplateQueryParserTests extends ESTestCase {
context.reset(templateSourceParser); context.reset(templateSourceParser);
TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class); TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class);
Query query = parser.fromXContent(context.parseContext()).toQuery(context); Query query = QueryBuilder.rewriteQuery(parser.fromXContent(context.parseContext()), context).toQuery(context);
assertTrue("Parsing template query failed.", query instanceof MatchAllDocsQuery); assertTrue("Parsing template query failed.", query instanceof MatchAllDocsQuery);
} }
@ -199,7 +200,7 @@ public class TemplateQueryParserTests extends ESTestCase {
TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class); TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class);
try { try {
parser.fromXContent(context.parseContext()).toQuery(context); parser.fromXContent(context.parseContext()).rewrite(context);
fail("Expected ParsingException"); fail("Expected ParsingException");
} catch (ParsingException e) { } catch (ParsingException e) {
assertThat(e.getMessage(), containsString("query malformed, no field after start_object")); assertThat(e.getMessage(), containsString("query malformed, no field after start_object"));
@ -213,8 +214,24 @@ public class TemplateQueryParserTests extends ESTestCase {
context.reset(templateSourceParser); context.reset(templateSourceParser);
templateSourceParser.nextToken(); templateSourceParser.nextToken();
TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class); TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class);
Query query = parser.fromXContent(context.parseContext()).toQuery(context); Query query = QueryBuilder.rewriteQuery(parser.fromXContent(context.parseContext()), context).toQuery(context);
assertTrue("Parsing template query failed.", query instanceof MatchAllDocsQuery); assertTrue("Parsing template query failed.", query instanceof MatchAllDocsQuery);
} }
public void testMustRewrite() throws Exception {
String templateString = "{ \"file\": \"storedTemplate\" ,\"params\":{\"template\":\"all\" } } ";
XContentParser templateSourceParser = XContentFactory.xContent(templateString).createParser(templateString);
context.reset(templateSourceParser);
templateSourceParser.nextToken();
TemplateQueryParser parser = injector.getInstance(TemplateQueryParser.class);
try {
parser.fromXContent(context.parseContext()).toQuery(context);
fail();
} catch (UnsupportedOperationException ex) {
assertEquals("this query must be rewritten first", ex.getMessage());
}
}
} }