Generalize how queries on `_index` are handled at rewrite time (#52815)

Generalize how queries on `_index` are handled at rewrite time (#52486)

Since this change refactors rewrites, I also took it as an opportunity to adrress #49254: instead of returning the same queries you would get on a keyword field when a field is unmapped, queries get rewritten to a MatchNoDocsQueryBuilder.

This change exposed a couple bugs, like the fact that the percolator doesn't rewrite queries at query time, or that the significant_terms aggregation doesn't rewrite its inner filter, which I fixed.

Closes #49254
This commit is contained in:
Adrien Grand 2020-02-26 15:37:43 +01:00 committed by GitHub
parent ad3a3b1af9
commit 1807f86751
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 623 additions and 348 deletions

View File

@ -24,6 +24,7 @@ import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.DelegatingAnalyzerWrapper;
import org.apache.lucene.index.BinaryDocValues;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
@ -78,6 +79,7 @@ import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryRewriteContext;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.QueryShardException;
import org.elasticsearch.index.query.Rewriteable;
import org.elasticsearch.indices.breaker.CircuitBreakerService;
import org.elasticsearch.indices.breaker.NoneCircuitBreakerService;
@ -91,7 +93,6 @@ import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import static org.elasticsearch.percolator.PercolatorFieldMapper.parseQuery;
import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES;
public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBuilder> {
@ -646,9 +647,9 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBu
PercolatorFieldMapper.FieldType pft = (PercolatorFieldMapper.FieldType) fieldType;
String name = this.name != null ? this.name : pft.name();
QueryShardContext percolateShardContext = wrap(context);
PercolatorFieldMapper.configureContext(percolateShardContext, pft.mapUnmappedFieldsAsText);;
PercolateQuery.QueryStore queryStore = createStore(pft.queryBuilderField,
percolateShardContext,
pft.mapUnmappedFieldsAsText);
percolateShardContext);
return pft.percolateQuery(name, queryStore, documents, docSearcher, excludeNestedDocuments, context.indexVersionCreated());
}
@ -695,8 +696,7 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBu
}
static PercolateQuery.QueryStore createStore(MappedFieldType queryBuilderFieldType,
QueryShardContext context,
boolean mapUnmappedFieldsAsString) {
QueryShardContext context) {
Version indexVersion = context.indexVersionCreated();
NamedWriteableRegistry registry = context.getWriteableRegistry();
return ctx -> {
@ -723,7 +723,8 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBu
assert valueLength > 0;
QueryBuilder queryBuilder = input.readNamedWriteable(QueryBuilder.class);
assert in.read() == -1;
return PercolatorFieldMapper.toQuery(context, mapUnmappedFieldsAsString, queryBuilder);
queryBuilder = Rewriteable.rewrite(queryBuilder, context);
return queryBuilder.toQuery(context);
}
}
} else {
@ -739,7 +740,10 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBu
try (XContentParser sourceParser = xContent
.createParser(context.getXContentRegistry(), LoggingDeprecationHandler.INSTANCE,
qbSource.bytes, qbSource.offset, qbSource.length)) {
return parseQuery(context, mapUnmappedFieldsAsString, sourceParser);
QueryBuilder queryBuilder = PercolatorFieldMapper.parseQueryBuilder(sourceParser,
sourceParser.getTokenLocation());
queryBuilder = Rewriteable.rewrite(queryBuilder, context);
return queryBuilder.toQuery(context);
}
} else {
return null;
@ -755,6 +759,13 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBu
static QueryShardContext wrap(QueryShardContext shardContext) {
return new QueryShardContext(shardContext) {
@Override
public IndexReader getIndexReader() {
// The reader that matters in this context is not the reader of the shard but
// the reader of the MemoryIndex. We just use `null` for simplicity.
return null;
}
@Override
public BitSetProducer bitsetFilter(Query query) {
return context -> {

View File

@ -397,6 +397,8 @@ public class PercolatorFieldMapper extends FieldMapper {
throw new IllegalArgumentException("a document can only contain one percolator query");
}
configureContext(queryShardContext, isMapUnmappedFieldAsText());
XContentParser parser = context.parser();
QueryBuilder queryBuilder = parseQueryBuilder(
parser, parser.getTokenLocation()
@ -410,7 +412,8 @@ public class PercolatorFieldMapper extends FieldMapper {
Version indexVersion = context.mapperService().getIndexSettings().getIndexVersionCreated();
createQueryBuilderField(indexVersion, queryBuilderField, queryBuilder, context);
Query query = toQuery(queryShardContext, isMapUnmappedFieldAsText(), queryBuilder);
QueryBuilder queryBuilderForProcessing = queryBuilder.rewrite(new QueryShardContext(queryShardContext));
Query query = queryBuilderForProcessing.toQuery(queryShardContext);
processQuery(query, context);
}
@ -480,11 +483,7 @@ public class PercolatorFieldMapper extends FieldMapper {
}
}
static Query parseQuery(QueryShardContext context, boolean mapUnmappedFieldsAsString, XContentParser parser) throws IOException {
return toQuery(context, mapUnmappedFieldsAsString, parseQueryBuilder(parser, parser.getTokenLocation()));
}
static Query toQuery(QueryShardContext context, boolean mapUnmappedFieldsAsString, QueryBuilder queryBuilder) throws IOException {
static void configureContext(QueryShardContext context, boolean mapUnmappedFieldsAsString) {
// This means that fields in the query need to exist in the mapping prior to registering this query
// The reason that this is required, is that if a field doesn't exist then the query assumes defaults, which may be undesired.
//
@ -499,10 +498,9 @@ public class PercolatorFieldMapper extends FieldMapper {
// as an analyzed string.
context.setAllowUnmappedFields(false);
context.setMapUnmappedFieldAsString(mapUnmappedFieldsAsString);
return queryBuilder.toQuery(context);
}
private static QueryBuilder parseQueryBuilder(XContentParser parser, XContentLocation location) {
static QueryBuilder parseQueryBuilder(XContentParser parser, XContentLocation location) {
try {
return parseInnerQueryBuilder(parser);
} catch (IOException e) {

View File

@ -38,10 +38,12 @@ import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.plain.BytesBinaryDVIndexFieldData;
import org.elasticsearch.index.mapper.BinaryFieldMapper;
import org.elasticsearch.index.mapper.ContentPath;
import org.elasticsearch.index.mapper.KeywordFieldMapper;
import org.elasticsearch.index.mapper.Mapper;
import org.elasticsearch.index.mapper.ParseContext;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.mock.orig.Mockito;
import org.elasticsearch.search.SearchModule;
import org.elasticsearch.test.ESTestCase;
@ -93,7 +95,14 @@ public class QueryBuilderStoreTests extends ESTestCase {
when(queryShardContext.getXContentRegistry()).thenReturn(xContentRegistry());
when(queryShardContext.getForField(fieldMapper.fieldType()))
.thenReturn(new BytesBinaryDVIndexFieldData(new Index("index", "uuid"), fieldMapper.name()));
PercolateQuery.QueryStore queryStore = PercolateQueryBuilder.createStore(fieldMapper.fieldType(), queryShardContext, false);
when(queryShardContext.fieldMapper(Mockito.anyString())).thenAnswer(invocation -> {
final String fieldName = (String) invocation.getArguments()[0];
KeywordFieldMapper.KeywordFieldType ft = new KeywordFieldMapper.KeywordFieldType();
ft.setName(fieldName);
ft.freeze();
return ft;
});
PercolateQuery.QueryStore queryStore = PercolateQueryBuilder.createStore(fieldMapper.fieldType(), queryShardContext);
try (IndexReader indexReader = DirectoryReader.open(directory)) {
LeafReaderContext leafContext = indexReader.leaves().get(0);

View File

@ -0,0 +1,123 @@
/*
* 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.mapper;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.index.query.QueryShardContext;
import java.util.List;
/**
* A {@link MappedFieldType} that has the same value for all documents.
* Factory methods for queries are called at rewrite time so they should be
* cheap. In particular they should not read data from disk or perform a
* network call. Furthermore they may only return a {@link MatchAllDocsQuery}
* or a {@link MatchNoDocsQuery}.
*/
public abstract class ConstantFieldType extends MappedFieldType {
public ConstantFieldType() {
super();
}
public ConstantFieldType(ConstantFieldType other) {
super(other);
}
@Override
public final boolean isSearchable() {
return true;
}
@Override
public final boolean isAggregatable() {
return true;
}
@Override
public final Query existsQuery(QueryShardContext context) {
return new MatchAllDocsQuery();
}
/**
* Return whether the constant value of this field matches the provided {@code pattern}
* as documented in {@link Regex#simpleMatch}.
*/
protected abstract boolean matches(String pattern, QueryShardContext context);
private static String valueToString(Object value) {
return value instanceof BytesRef
? ((BytesRef) value).utf8ToString()
: value.toString();
}
@Override
public final Query termQuery(Object value, QueryShardContext context) {
String pattern = valueToString(value);
if (matches(pattern, context)) {
return Queries.newMatchAllQuery();
} else {
return new MatchNoDocsQuery();
}
}
@Override
public final Query termsQuery(List<?> values, QueryShardContext context) {
for (Object value : values) {
String pattern = valueToString(value);
if (matches(pattern, context)) {
// `terms` queries are a disjunction, so one matching term is enough
return Queries.newMatchAllQuery();
}
}
return new MatchNoDocsQuery();
}
@Override
public final Query prefixQuery(String prefix,
@Nullable MultiTermQuery.RewriteMethod method,
QueryShardContext context) {
String pattern = prefix + "*";
if (matches(pattern, context)) {
return Queries.newMatchAllQuery();
} else {
return new MatchNoDocsQuery();
}
}
@Override
public final Query wildcardQuery(String value,
@Nullable MultiTermQuery.RewriteMethod method,
QueryShardContext context) {
if (matches(value, context)) {
return Queries.newMatchAllQuery();
} else {
return new MatchNoDocsQuery();
}
}
}

View File

@ -21,13 +21,7 @@ package org.elasticsearch.index.mapper;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.fielddata.IndexFieldData;
@ -89,7 +83,7 @@ public class IndexFieldMapper extends MetadataFieldMapper {
}
}
static final class IndexFieldType extends MappedFieldType {
static final class IndexFieldType extends ConstantFieldType {
IndexFieldType() {}
@ -108,81 +102,8 @@ public class IndexFieldMapper extends MetadataFieldMapper {
}
@Override
public boolean isSearchable() {
// The _index field is always searchable.
return true;
}
@Override
public Query existsQuery(QueryShardContext context) {
return new MatchAllDocsQuery();
}
/**
* This termQuery impl looks at the context to determine the index that
* is being queried and then returns a MATCH_ALL_QUERY or MATCH_NO_QUERY
* if the value matches this index. This can be useful if aliases or
* wildcards are used but the aim is to restrict the query to specific
* indices
*/
@Override
public Query termQuery(Object value, @Nullable QueryShardContext context) {
String pattern = value instanceof BytesRef
? ((BytesRef) value).utf8ToString()
: value.toString();
if (context.indexMatches(pattern)) {
// No need to OR these clauses - we can only logically be
// running in the context of just one of these index names.
return Queries.newMatchAllQuery();
} else {
return Queries.newMatchNoDocsQuery("The index [" + context.getFullyQualifiedIndex().getName() +
"] doesn't match the provided value [" + value + "].");
}
}
@Override
public Query termsQuery(List values, QueryShardContext context) {
if (context == null) {
return super.termsQuery(values, context);
}
for (Object value : values) {
String pattern = value instanceof BytesRef
? ((BytesRef) value).utf8ToString()
: value.toString();
if (context.indexMatches(pattern)) {
// No need to OR these clauses - we can only logically be
// running in the context of just one of these index names.
return Queries.newMatchAllQuery();
}
}
// None of the listed index names are this one
return Queries.newMatchNoDocsQuery("The index [" + context.getFullyQualifiedIndex().getName() +
"] doesn't match the provided values [" + values + "].");
}
@Override
public Query prefixQuery(String value,
@Nullable MultiTermQuery.RewriteMethod method,
QueryShardContext context) {
String pattern = value + "*";
if (context.indexMatches(pattern)) {
return Queries.newMatchAllQuery();
} else {
return Queries.newMatchNoDocsQuery("The index [" + context.getFullyQualifiedIndex().getName() +
"] doesn't match the provided prefix [" + value + "].");
}
}
@Override
public Query wildcardQuery(String value,
@Nullable MultiTermQuery.RewriteMethod method,
QueryShardContext context) {
if (context.indexMatches(value)) {
return Queries.newMatchAllQuery();
} else {
return Queries.newMatchNoDocsQuery("The index [" + context.getFullyQualifiedIndex().getName()
+ "] doesn't match the provided pattern [" + value + "].");
}
protected boolean matches(String pattern, QueryShardContext context) {
return context.indexMatches(pattern);
}
@Override

View File

@ -20,6 +20,7 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.spans.SpanBoostQuery;
import org.apache.lucene.search.spans.SpanQuery;
@ -103,7 +104,7 @@ public abstract class AbstractQueryBuilder<QB extends AbstractQueryBuilder<QB>>
if (boost != DEFAULT_BOOST) {
if (query instanceof SpanQuery) {
query = new SpanBoostQuery((SpanQuery) query, boost);
} else {
} else if (query instanceof MatchNoDocsQuery == false) {
query = new BoostQuery(query, boost);
}
}
@ -232,7 +233,7 @@ public abstract class AbstractQueryBuilder<QB extends AbstractQueryBuilder<QB>>
IOException {
List<Query> queries = new ArrayList<>(queryBuilders.size());
for (QueryBuilder queryBuilder : queryBuilders) {
Query query = queryBuilder.toQuery(context);
Query query = queryBuilder.rewrite(context).toQuery(context);
if (query != null) {
queries.add(query);
}

View File

@ -19,7 +19,6 @@
package org.elasticsearch.index.query;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.Query;
@ -28,7 +27,6 @@ import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -322,18 +320,26 @@ public class FuzzyQueryBuilder extends AbstractQueryBuilder<FuzzyQueryBuilder> i
return NAME;
}
@Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryShardContext context = queryRewriteContext.convertToShardContext();
if (context != null) {
MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType == null) {
return new MatchNoneQueryBuilder();
}
}
return super.doRewrite(context);
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
Query query = null;
String rewrite = this.rewrite;
MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType != null) {
query = fieldType.fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context);
}
if (query == null) {
int maxEdits = fuzziness.asDistance(BytesRefs.toString(value));
query = new FuzzyQuery(new Term(fieldName, BytesRefs.toBytesRef(value)), maxEdits, prefixLength, maxExpansions, transpositions);
if (fieldType == null) {
throw new IllegalStateException("Rewrite first");
}
String rewrite = this.rewrite;
Query query = fieldType.fuzzyQuery(value, fuzziness, prefixLength, maxExpansions, transpositions, context);
if (query instanceof MultiTermQuery) {
MultiTermQuery.RewriteMethod rewriteMethod = QueryParsers.parseRewriteMethod(rewrite, null,
LoggingDeprecationHandler.INSTANCE);

View File

@ -29,7 +29,6 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
@ -169,31 +168,39 @@ public class IdsQueryBuilder extends AbstractQueryBuilder<IdsQueryBuilder> {
return NAME;
}
@Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
if (ids.isEmpty()) {
return new MatchNoneQueryBuilder();
}
QueryShardContext context = queryRewriteContext.convertToShardContext();
if (context != null && context.fieldMapper(IdFieldMapper.NAME) == null) {
// no mappings yet
return new MatchNoneQueryBuilder();
}
return super.doRewrite(queryRewriteContext);
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
MappedFieldType idField = context.fieldMapper(IdFieldMapper.NAME);
if (idField == null) {
return new MatchNoDocsQuery("No mappings");
if (idField == null || ids.isEmpty()) {
throw new IllegalStateException("Rewrite first");
}
if (this.ids.isEmpty()) {
return Queries.newMatchNoDocsQuery("Missing ids in \"" + this.getName() + "\" query.");
final DocumentMapper mapper = context.getMapperService().documentMapper();
Collection<String> typesForQuery;
if (types.length == 0) {
typesForQuery = context.queryTypes();
} else if (types.length == 1 && MetaData.ALL.equals(types[0])) {
typesForQuery = Collections.singleton(mapper.type());
} else {
final DocumentMapper mapper = context.getMapperService().documentMapper();
Collection<String> typesForQuery;
if (types.length == 0) {
typesForQuery = context.queryTypes();
} else if (types.length == 1 && MetaData.ALL.equals(types[0])) {
typesForQuery = Collections.singleton(mapper.type());
} else {
typesForQuery = new HashSet<>(Arrays.asList(types));
}
typesForQuery = new HashSet<>(Arrays.asList(types));
}
if (typesForQuery.contains(mapper.type())) {
return idField.termsQuery(new ArrayList<>(ids), context);
} else {
return new MatchNoDocsQuery("Type mismatch");
}
if (typesForQuery.contains(mapper.type())) {
return idField.termsQuery(new ArrayList<>(ids), context);
} else {
return new MatchNoDocsQuery("Type mismatch");
}
}

View File

@ -19,20 +19,20 @@
package org.elasticsearch.index.query;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.ConstantFieldType;
import org.elasticsearch.index.query.support.QueryParsers;
import java.io.IOException;
@ -171,14 +171,26 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
@Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
if ("_index".equals(fieldName)) {
// Special-case optimisation for canMatch phase:
// We can skip querying this shard if the index name doesn't match the value of this query on the "_index" field.
QueryShardContext shardContext = queryRewriteContext.convertToShardContext();
if (shardContext != null && shardContext.indexMatches(value + "*") == false) {
QueryShardContext context = queryRewriteContext.convertToShardContext();
if (context != null) {
MappedFieldType fieldType = context.fieldMapper(this.fieldName);
if (fieldType == null) {
return new MatchNoneQueryBuilder();
}
} else if (fieldType instanceof ConstantFieldType) {
// This logic is correct for all field types, but by only applying it to constant
// fields we also have the guarantee that it doesn't perform I/O, which is important
// since rewrites might happen on a network thread.
Query query = fieldType.prefixQuery(value, null, context); // the rewrite method doesn't matter
if (query instanceof MatchAllDocsQuery) {
return new MatchAllQueryBuilder();
} else if (query instanceof MatchNoDocsQuery) {
return new MatchNoneQueryBuilder();
} else {
assert false : "Constant fields must produce match-all or match-none queries, got " + query ;
}
}
}
return super.doRewrite(queryRewriteContext);
}
@ -186,20 +198,11 @@ public class PrefixQueryBuilder extends AbstractQueryBuilder<PrefixQueryBuilder>
protected Query doToQuery(QueryShardContext context) throws IOException {
MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod(rewrite, null, LoggingDeprecationHandler.INSTANCE);
Query query = null;
MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType != null) {
query = fieldType.prefixQuery(value, method, context);
if (fieldType == null) {
throw new IllegalStateException("Rewrite first");
}
if (query == null) {
PrefixQuery prefixQuery = new PrefixQuery(new Term(fieldName, BytesRefs.toBytesRef(value)));
if (method != null) {
prefixQuery.setRewriteMethod(method);
}
query = prefixQuery;
}
return query;
return fieldType.prefixQuery(value, method, context);
}
@Override

View File

@ -21,7 +21,6 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
@ -29,14 +28,12 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.common.time.DateMathParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.FieldNamesFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MapperService;
import java.io.IOException;
import java.time.DateTimeException;
@ -432,21 +429,23 @@ public class RangeQueryBuilder extends AbstractQueryBuilder<RangeQueryBuilder> i
// Overridable for testing only
protected MappedFieldType.Relation getRelation(QueryRewriteContext queryRewriteContext) throws IOException {
QueryShardContext shardContext = queryRewriteContext.convertToShardContext();
// If the context is null we are not on the shard and cannot
// rewrite so just pretend there is an intersection so that the rewrite is a noop
if (shardContext == null || shardContext.getIndexReader() == null) {
return MappedFieldType.Relation.INTERSECTS;
}
final MapperService mapperService = shardContext.getMapperService();
final MappedFieldType fieldType = mapperService.fieldType(fieldName);
if (fieldType == null) {
// no field means we have no values
return MappedFieldType.Relation.DISJOINT;
} else {
if (shardContext != null) {
final MappedFieldType fieldType = shardContext.fieldMapper(fieldName);
if (fieldType == null) {
return MappedFieldType.Relation.DISJOINT;
}
if (shardContext.getIndexReader() == null) {
// No reader, this may happen e.g. for percolator queries.
return MappedFieldType.Relation.INTERSECTS;
}
DateMathParser dateMathParser = getForceDateParser();
return fieldType.isFieldWithinQuery(shardContext.getIndexReader(), from, to, includeLower,
includeUpper, timeZone, dateMathParser, queryRewriteContext);
}
// Not on the shard, we have no way to know what the relation is.
return MappedFieldType.Relation.INTERSECTS;
}
@Override
@ -490,26 +489,14 @@ public class RangeQueryBuilder extends AbstractQueryBuilder<RangeQueryBuilder> i
return ExistsQueryBuilder.newFilter(context, fieldName);
}
}
Query query = null;
MappedFieldType mapper = context.fieldMapper(this.fieldName);
if (mapper != null) {
DateMathParser forcedDateParser = getForceDateParser();
query = mapper.rangeQuery(
from, to, includeLower, includeUpper,
relation, timeZone, forcedDateParser, context);
} else {
if (timeZone != null) {
throw new QueryShardException(context, "[range] time_zone can not be applied to non unmapped field ["
+ fieldName + "]");
}
if (mapper == null) {
throw new IllegalStateException("Rewrite first");
}
if (query == null) {
query = new TermRangeQuery(this.fieldName,
BytesRefs.toBytesRef(from), BytesRefs.toBytesRef(to),
includeLower, includeUpper);
}
return query;
DateMathParser forcedDateParser = getForceDateParser();
return mapper.rangeQuery(
from, to, includeLower, includeUpper,
relation, timeZone, forcedDateParser, context);
}
@Override

View File

@ -126,11 +126,16 @@ public class SpanMultiTermQueryBuilder extends AbstractQueryBuilder<SpanMultiTer
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
if (multiTermQueryBuilder instanceof PrefixQueryBuilder) {
// We do the rewrite in toQuery to not have to deal with the case when a multi-term builder rewrites to a non-multi-term
// builder.
QueryBuilder multiTermQueryBuilder = Rewriteable.rewrite(this.multiTermQueryBuilder, context);
if (multiTermQueryBuilder instanceof MatchNoneQueryBuilder) {
return new SpanMatchNoDocsQuery(this.multiTermQueryBuilder.fieldName(), "Inner query rewrote to match_none");
} else if (multiTermQueryBuilder instanceof PrefixQueryBuilder) {
PrefixQueryBuilder prefixBuilder = (PrefixQueryBuilder) multiTermQueryBuilder;
MappedFieldType fieldType = context.fieldMapper(multiTermQueryBuilder.fieldName());
MappedFieldType fieldType = context.fieldMapper(prefixBuilder.fieldName());
if (fieldType == null) {
return new SpanMatchNoDocsQuery(multiTermQueryBuilder.fieldName(), "unknown field");
throw new IllegalStateException("Rewrite first");
}
final SpanMultiTermQueryWrapper.SpanRewriteMethod spanRewriteMethod;
if (prefixBuilder.rewrite() != null) {
@ -159,7 +164,7 @@ public class SpanMultiTermQueryBuilder extends AbstractQueryBuilder<SpanMultiTer
}
}
if (subQuery instanceof MatchNoDocsQuery) {
return new SpanMatchNoDocsQuery(multiTermQueryBuilder.fieldName(), subQuery.toString());
return new SpanMatchNoDocsQuery(this.multiTermQueryBuilder.fieldName(), subQuery.toString());
} else if (subQuery instanceof MultiTermQuery == false) {
throw new UnsupportedOperationException("unsupported inner query, should be "
+ MultiTermQuery.class.getName() + " but was " + subQuery.getClass().getName());

View File

@ -19,15 +19,15 @@
package org.elasticsearch.index.query;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.ConstantFieldType;
import java.io.IOException;
@ -132,28 +132,35 @@ public class TermQueryBuilder extends BaseTermQueryBuilder<TermQueryBuilder> {
@Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
if ("_index".equals(fieldName)) {
// Special-case optimisation for canMatch phase:
// We can skip querying this shard if the index name doesn't match the value of this query on the "_index" field.
QueryShardContext shardContext = queryRewriteContext.convertToShardContext();
if (shardContext != null && shardContext.indexMatches(BytesRefs.toString(value)) == false) {
QueryShardContext context = queryRewriteContext.convertToShardContext();
if (context != null) {
MappedFieldType fieldType = context.fieldMapper(this.fieldName);
if (fieldType == null) {
return new MatchNoneQueryBuilder();
}
} else if (fieldType instanceof ConstantFieldType) {
// This logic is correct for all field types, but by only applying it to constant
// fields we also have the guarantee that it doesn't perform I/O, which is important
// since rewrites might happen on a network thread.
Query query = fieldType.termQuery(value, context);
if (query instanceof MatchAllDocsQuery) {
return new MatchAllQueryBuilder();
} else if (query instanceof MatchNoDocsQuery) {
return new MatchNoneQueryBuilder();
} else {
assert false : "Constant fields must produce match-all or match-none queries, got " + query ;
}
}
}
return super.doRewrite(queryRewriteContext);
}
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
Query query = null;
MappedFieldType mapper = context.fieldMapper(this.fieldName);
if (mapper != null) {
query = mapper.termQuery(this.value, context);
if (mapper == null) {
throw new IllegalStateException("Rewrite first");
}
if (query == null) {
query = new TermQuery(new Term(this.fieldName, BytesRefs.toBytesRef(this.value)));
}
return query;
return mapper.termQuery(this.value, context);
}
@Override

View File

@ -20,8 +20,9 @@
package org.elasticsearch.index.query;
import org.apache.logging.log4j.LogManager;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermInSetQuery;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.SetOnce;
@ -35,13 +36,12 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.XContentMapValues;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.ConstantFieldType;
import org.elasticsearch.indices.TermsLookup;
import java.io.IOException;
@ -432,12 +432,9 @@ public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> {
@Override
protected Query doToQuery(QueryShardContext context) throws IOException {
if (termsLookup != null || supplier != null) {
if (termsLookup != null || supplier != null || values == null || values.isEmpty()) {
throw new UnsupportedOperationException("query must be rewritten first");
}
if (values == null || values.isEmpty()) {
return Queries.newMatchNoDocsQuery("No terms supplied for \"" + getName() + "\" query.");
}
int maxTermsCount = context.getIndexSettings().getMaxTermsCount();
if (values.size() > maxTermsCount){
throw new IllegalArgumentException(
@ -446,16 +443,10 @@ public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> {
IndexSettings.MAX_TERMS_COUNT_SETTING.getKey() + "] index level setting.");
}
MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType != null) {
return fieldType.termsQuery(values, context);
} else {
BytesRef[] filterValues = new BytesRef[values.size()];
for (int i = 0; i < filterValues.length; i++) {
filterValues[i] = BytesRefs.toBytesRef(values.get(i));
}
return new TermInSetQuery(fieldName, filterValues);
if (fieldType == null) {
throw new IllegalStateException("Rewrite first");
}
return fieldType.termsQuery(values, context);
}
private void fetch(TermsLookup termsLookup, Client client, ActionListener<List<Object>> actionListener) {
@ -499,21 +490,31 @@ public class TermsQueryBuilder extends AbstractQueryBuilder<TermsQueryBuilder> {
})));
return new TermsQueryBuilder(this.fieldName, supplier::get);
}
if ("_index".equals(this.fieldName) && values != null) {
// Special-case optimisation for canMatch phase:
// We can skip querying this shard if the index name doesn't match any of the search terms.
QueryShardContext shardContext = queryRewriteContext.convertToShardContext();
if (shardContext != null) {
for (Object localValue : values) {
if (shardContext.indexMatches(BytesRefs.toString(localValue))) {
// We can match - at least one index name matches
return this;
}
}
// all index names are invalid - no possibility of a match on this shard.
if (values == null || values.isEmpty()) {
return new MatchNoneQueryBuilder();
}
QueryShardContext context = queryRewriteContext.convertToShardContext();
if (context != null) {
MappedFieldType fieldType = context.fieldMapper(this.fieldName);
if (fieldType == null) {
return new MatchNoneQueryBuilder();
} else if (fieldType instanceof ConstantFieldType) {
// This logic is correct for all field types, but by only applying it to constant
// fields we also have the guarantee that it doesn't perform I/O, which is important
// since rewrites might happen on a network thread.
Query query = fieldType.termsQuery(values, context);
if (query instanceof MatchAllDocsQuery) {
return new MatchAllQueryBuilder();
} else if (query instanceof MatchNoDocsQuery) {
return new MatchNoneQueryBuilder();
} else {
assert false : "Constant fields must produce match-all or match-none queries, got " + query ;
}
}
}
return this;
}
}

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.Query;
@ -27,11 +28,11 @@ import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.ConstantFieldType;
import org.elasticsearch.index.query.support.QueryParsers;
import java.io.IOException;
@ -182,14 +183,26 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
@Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
if ("_index".equals(fieldName)) {
// Special-case optimisation for canMatch phase:
// We can skip querying this shard if the index name doesn't match the value of this query on the "_index" field.
QueryShardContext shardContext = queryRewriteContext.convertToShardContext();
if (shardContext != null && shardContext.indexMatches(BytesRefs.toString(value)) == false) {
QueryShardContext context = queryRewriteContext.convertToShardContext();
if (context != null) {
MappedFieldType fieldType = context.fieldMapper(this.fieldName);
if (fieldType == null) {
return new MatchNoneQueryBuilder();
}
} else if (fieldType instanceof ConstantFieldType) {
// This logic is correct for all field types, but by only applying it to constant
// fields we also have the guarantee that it doesn't perform I/O, which is important
// since rewrites might happen on a network thread.
Query query = fieldType.wildcardQuery(value, null, context); // the rewrite method doesn't matter
if (query instanceof MatchAllDocsQuery) {
return new MatchAllQueryBuilder();
} else if (query instanceof MatchNoDocsQuery) {
return new MatchNoneQueryBuilder();
} else {
assert false : "Constant fields must produce match-all or match-none queries, got " + query ;
}
}
}
return super.doRewrite(queryRewriteContext);
}
@ -198,7 +211,7 @@ public class WildcardQueryBuilder extends AbstractQueryBuilder<WildcardQueryBuil
MappedFieldType fieldType = context.fieldMapper(fieldName);
if (fieldType == null) {
return new MatchNoDocsQuery("unknown field [" + fieldName + "]");
throw new IllegalStateException("Rewrite first");
}
MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod(

View File

@ -26,6 +26,7 @@ import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryRewriteContext;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregatorFactories.Builder;
@ -127,10 +128,23 @@ public class SignificantTermsAggregationBuilder extends ValuesSourceAggregationB
}
@Override
protected AggregationBuilder shallowCopy(Builder factoriesBuilder, Map<String, Object> metaData) {
protected SignificantTermsAggregationBuilder shallowCopy(Builder factoriesBuilder, Map<String, Object> metaData) {
return new SignificantTermsAggregationBuilder(this, factoriesBuilder, metaData);
}
@Override
protected AggregationBuilder doRewrite(QueryRewriteContext queryShardContext) throws IOException {
if (filterBuilder != null) {
QueryBuilder rewrittenFilter = filterBuilder.rewrite(queryShardContext);
if (rewrittenFilter != filterBuilder) {
SignificantTermsAggregationBuilder rewritten = shallowCopy(factoriesBuilder, metaData);
rewritten.backgroundFilter(rewrittenFilter);
return rewritten;
}
}
return super.doRewrite(queryShardContext);
}
@Override
protected void innerWriteTo(StreamOutput out) throws IOException {
bucketCountThresholds.writeTo(out);

View File

@ -22,6 +22,7 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -85,7 +86,7 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
if (clauses.isEmpty()) {
assertThat(query, instanceOf(MatchAllDocsQuery.class));
} else {
} else if (query instanceof MatchNoDocsQuery == false) {
assertThat(query, instanceOf(BooleanQuery.class));
BooleanQuery booleanQuery = (BooleanQuery) query;
if (queryBuilder.adjustPureNegative()) {
@ -113,7 +114,7 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
BooleanClause.Occur occur, QueryShardContext context) throws IOException {
List<BooleanClause> clauses = new ArrayList<>();
for (QueryBuilder query : queryBuilders) {
Query innerQuery = query.toQuery(context);
Query innerQuery = query.rewrite(context).toQuery(context);
if (innerQuery != null) {
clauses.add(new BooleanClause(innerQuery, occur));
}
@ -195,15 +196,15 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
public void testMinShouldMatchBiggerThanNumberOfShouldClauses() throws Exception {
BooleanQuery bq = (BooleanQuery) parseQuery(
boolQuery()
.should(termQuery("foo", "bar"))
.should(termQuery("foo2", "bar2"))
.should(termQuery(STRING_FIELD_NAME, "bar"))
.should(termQuery(STRING_FIELD_NAME_2, "bar2"))
.minimumShouldMatch("3")).toQuery(createShardContext());
assertEquals(3, bq.getMinimumNumberShouldMatch());
bq = (BooleanQuery) parseQuery(
boolQuery()
.should(termQuery("foo", "bar"))
.should(termQuery("foo2", "bar2"))
.should(termQuery(STRING_FIELD_NAME, "bar"))
.should(termQuery(STRING_FIELD_NAME_2, "bar2"))
.minimumShouldMatch(3)).toQuery(createShardContext());
assertEquals(3, bq.getMinimumNumberShouldMatch());
}
@ -211,8 +212,8 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
public void testMinShouldMatchDisableCoord() throws Exception {
BooleanQuery bq = (BooleanQuery) parseQuery(
boolQuery()
.should(termQuery("foo", "bar"))
.should(termQuery("foo2", "bar2"))
.should(termQuery(STRING_FIELD_NAME, "bar"))
.should(termQuery(STRING_FIELD_NAME, "bar2"))
.minimumShouldMatch("3")).toQuery(createShardContext());
assertEquals(3, bq.getMinimumNumberShouldMatch());
}
@ -292,22 +293,22 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
boolean mustRewrite = false;
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.must(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "must").toString()));
boolQueryBuilder.must(new WrapperQueryBuilder(new TermsQueryBuilder(STRING_FIELD_NAME, "must").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.should(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "should").toString()));
boolQueryBuilder.should(new WrapperQueryBuilder(new TermsQueryBuilder(STRING_FIELD_NAME, "should").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.filter(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "filter").toString()));
boolQueryBuilder.filter(new WrapperQueryBuilder(new TermsQueryBuilder(STRING_FIELD_NAME, "filter").toString()));
}
if (randomBoolean()) {
mustRewrite = true;
boolQueryBuilder.mustNot(new WrapperQueryBuilder(new TermsQueryBuilder("foo", "must_not").toString()));
boolQueryBuilder.mustNot(new WrapperQueryBuilder(new TermsQueryBuilder(STRING_FIELD_NAME, "must_not").toString()));
}
if (mustRewrite == false && randomBoolean()) {
boolQueryBuilder.must(new TermsQueryBuilder("foo", "no_rewrite"));
boolQueryBuilder.must(new TermsQueryBuilder(STRING_FIELD_NAME, "no_rewrite"));
}
QueryBuilder rewritten = boolQueryBuilder.rewrite(createShardContext());
if (mustRewrite == false && boolQueryBuilder.must().isEmpty()) {
@ -318,16 +319,16 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
if (mustRewrite) {
assertNotSame(rewrite, boolQueryBuilder);
if (boolQueryBuilder.must().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "must"), rewrite.must().get(0));
assertEquals(new TermsQueryBuilder(STRING_FIELD_NAME, "must"), rewrite.must().get(0));
}
if (boolQueryBuilder.should().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "should"), rewrite.should().get(0));
assertEquals(new TermsQueryBuilder(STRING_FIELD_NAME, "should"), rewrite.should().get(0));
}
if (boolQueryBuilder.mustNot().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "must_not"), rewrite.mustNot().get(0));
assertEquals(new TermsQueryBuilder(STRING_FIELD_NAME, "must_not"), rewrite.mustNot().get(0));
}
if (boolQueryBuilder.filter().isEmpty() == false) {
assertEquals(new TermsQueryBuilder("foo", "filter"), rewrite.filter().get(0));
assertEquals(new TermsQueryBuilder(STRING_FIELD_NAME, "filter"), rewrite.filter().get(0));
}
} else {
assertSame(rewrite, boolQueryBuilder);
@ -360,14 +361,14 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
assertEquals(new MatchNoneQueryBuilder(), rewritten);
boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new TermQueryBuilder("foo","bar"));
boolQueryBuilder.must(new TermQueryBuilder(STRING_FIELD_NAME,"bar"));
boolQueryBuilder.filter(new WrapperQueryBuilder(new WrapperQueryBuilder(new MatchNoneQueryBuilder().toString()).toString()));
rewritten = boolQueryBuilder.rewrite(createShardContext());
assertEquals(new MatchNoneQueryBuilder(), rewritten);
boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(new TermQueryBuilder("foo","bar"));
boolQueryBuilder.filter(new BoolQueryBuilder().should(new TermQueryBuilder("foo","bar"))
boolQueryBuilder.must(new TermQueryBuilder(STRING_FIELD_NAME,"bar"));
boolQueryBuilder.filter(new BoolQueryBuilder().should(new TermQueryBuilder(STRING_FIELD_NAME,"bar"))
.filter(new MatchNoneQueryBuilder()));
rewritten = Rewriteable.rewrite(boolQueryBuilder, createShardContext());
assertEquals(new MatchNoneQueryBuilder(), rewritten);
@ -378,7 +379,7 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
assertEquals(new MatchNoneQueryBuilder(), rewritten);
boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.should(new TermQueryBuilder("foo", "bar"));
boolQueryBuilder.should(new TermQueryBuilder(STRING_FIELD_NAME, "bar"));
boolQueryBuilder.should(new WrapperQueryBuilder(new MatchNoneQueryBuilder().toString()));
rewritten = Rewriteable.rewrite(boolQueryBuilder, createShardContext());
assertNotEquals(new MatchNoneQueryBuilder(), rewritten);
@ -387,4 +388,16 @@ public class BoolQueryBuilderTests extends AbstractQueryTestCase<BoolQueryBuilde
rewritten = Rewriteable.rewrite(boolQueryBuilder, createShardContext());
assertNotEquals(new MatchNoneQueryBuilder(), rewritten);
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
TermQueryBuilder termQuery = new TermQueryBuilder("unmapped_field", 42);
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
boolQuery.must(termQuery);
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> boolQuery.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}

View File

@ -40,8 +40,8 @@ public class BoostingQueryBuilderTests extends AbstractQueryTestCase<BoostingQue
@Override
protected void doAssertLuceneQuery(BoostingQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
Query positive = queryBuilder.positiveQuery().toQuery(context);
Query negative = queryBuilder.negativeQuery().toQuery(context);
Query positive = queryBuilder.positiveQuery().rewrite(context).toQuery(context);
Query negative = queryBuilder.negativeQuery().rewrite(context).toQuery(context);
if (positive == null || negative == null) {
assertThat(query, nullValue());
} else {
@ -103,4 +103,22 @@ public class BoostingQueryBuilderTests extends AbstractQueryTestCase<BoostingQue
assertEquals(new BoostingQueryBuilder(positive.rewrite(createShardContext()), negative.rewrite(createShardContext())), rewrite);
}
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
BoostingQueryBuilder queryBuilder1 = new BoostingQueryBuilder(
new TermQueryBuilder("unmapped_field", "foo"), new MatchNoneQueryBuilder());
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> queryBuilder1.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
BoostingQueryBuilder queryBuilder2 = new BoostingQueryBuilder(
new MatchAllQueryBuilder(), new TermQueryBuilder("unmapped_field", "foo"));
e = expectThrows(IllegalStateException.class,
() -> queryBuilder2.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.test.AbstractQueryTestCase;
@ -41,9 +42,11 @@ public class ConstantScoreQueryBuilderTests extends AbstractQueryTestCase<Consta
@Override
protected void doAssertLuceneQuery(ConstantScoreQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
Query innerQuery = queryBuilder.innerQuery().toQuery(context);
Query innerQuery = queryBuilder.innerQuery().rewrite(context).toQuery(context);
if (innerQuery == null) {
assertThat(query, nullValue());
} else if (innerQuery instanceof MatchNoDocsQuery) {
assertThat(query, instanceOf(MatchNoDocsQuery.class));
} else {
assertThat(query, instanceOf(ConstantScoreQuery.class));
ConstantScoreQuery constantScoreQuery = (ConstantScoreQuery) query;
@ -107,4 +110,14 @@ public class ConstantScoreQueryBuilderTests extends AbstractQueryTestCase<Consta
QueryBuilder rewrite = constantScoreQueryBuilder.rewrite(createShardContext());
assertEquals(rewrite, new MatchNoneQueryBuilder());
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
ConstantScoreQueryBuilder queryBuilder = new ConstantScoreQueryBuilder(new TermQueryBuilder("unmapped_field", "foo"));
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> queryBuilder.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}

View File

@ -164,4 +164,14 @@ public class IdsQueryBuilderTests extends AbstractQueryTestCase<IdsQueryBuilder>
}
return query;
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContextWithNoType();
context.setAllowUnmappedFields(true);
IdsQueryBuilder queryBuilder = createTestQueryBuilder();
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> queryBuilder.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}

View File

@ -57,8 +57,6 @@ import static org.mockito.Mockito.when;
public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBuilder> {
boolean requiresRewrite = false;
@Override
protected void initializeAdditionalMappings(MapperService mapperService) throws IOException {
mapperService.merge("_doc", new CompressedXContent(Strings.toString(PutMappingRequest.buildFromSimplifiedDef("_doc",
@ -79,10 +77,6 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
@Override
protected NestedQueryBuilder doCreateTestQueryBuilder() {
QueryBuilder innerQueryBuilder = RandomQueryBuilder.createQuery(random());
if (randomBoolean()) {
requiresRewrite = true;
innerQueryBuilder = new WrapperQueryBuilder(innerQueryBuilder.toString());
}
NestedQueryBuilder nqb = new NestedQueryBuilder("nested1", innerQueryBuilder,
RandomPicks.randomFrom(random(), ScoreMode.values()));
nqb.ignoreUnmapped(randomBoolean());
@ -186,13 +180,14 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
@Override
public void testMustRewrite() throws IOException {
try {
super.testMustRewrite();
} catch (UnsupportedOperationException e) {
if (requiresRewrite == false) {
throw e;
}
}
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
TermQueryBuilder innerQueryBuilder = new TermQueryBuilder("nested1.unmapped_field", "foo");
NestedQueryBuilder nestedQueryBuilder = new NestedQueryBuilder("nested1", innerQueryBuilder,
RandomPicks.randomFrom(random(), ScoreMode.values()));
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> nestedQueryBuilder.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
public void testIgnoreUnmapped() throws IOException {

View File

@ -20,11 +20,13 @@
package org.elasticsearch.index.query;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.test.AbstractQueryTestCase;
import org.hamcrest.Matchers;
import java.io.IOException;
import java.util.HashMap;
@ -68,12 +70,14 @@ public class PrefixQueryBuilderTests extends AbstractQueryTestCase<PrefixQueryBu
@Override
protected void doAssertLuceneQuery(PrefixQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
assertThat(query, instanceOf(PrefixQuery.class));
PrefixQuery prefixQuery = (PrefixQuery) query;
assertThat(query, Matchers.anyOf(instanceOf(PrefixQuery.class), instanceOf(MatchNoDocsQuery.class)));
if (context.fieldMapper(queryBuilder.fieldName()) != null) { // The field is mapped
PrefixQuery prefixQuery = (PrefixQuery) query;
String expectedFieldName = expectedFieldName(queryBuilder.fieldName());
assertThat(prefixQuery.getPrefix().field(), equalTo(expectedFieldName));
assertThat(prefixQuery.getPrefix().text(), equalTo(queryBuilder.value()));
String expectedFieldName = expectedFieldName(queryBuilder.fieldName());
assertThat(prefixQuery.getPrefix().field(), equalTo(expectedFieldName));
assertThat(prefixQuery.getPrefix().text(), equalTo(queryBuilder.value()));
}
}
public void testIllegalArguments() {
@ -88,10 +92,10 @@ public class PrefixQueryBuilderTests extends AbstractQueryTestCase<PrefixQueryBu
public void testBlendedRewriteMethod() throws IOException {
String rewrite = "top_terms_blended_freqs_10";
Query parsedQuery = parseQuery(prefixQuery("field", "val").rewrite(rewrite)).toQuery(createShardContext());
Query parsedQuery = parseQuery(prefixQuery(STRING_FIELD_NAME, "val").rewrite(rewrite)).toQuery(createShardContext());
assertThat(parsedQuery, instanceOf(PrefixQuery.class));
PrefixQuery prefixQuery = (PrefixQuery) parsedQuery;
assertThat(prefixQuery.getPrefix(), equalTo(new Term("field", "val")));
assertThat(prefixQuery.getPrefix(), equalTo(new Term(STRING_FIELD_NAME, "val")));
assertThat(prefixQuery.getRewriteMethod(), instanceOf(MultiTermQuery.TopTermsBlendedFreqScoringRewrite.class));
}
@ -153,7 +157,16 @@ public class PrefixQueryBuilderTests extends AbstractQueryTestCase<PrefixQueryBu
PrefixQueryBuilder query = prefixQuery("_index", getIndex().getName());
QueryShardContext queryShardContext = createShardContext();
QueryBuilder rewritten = query.rewrite(queryShardContext);
assertThat(rewritten, instanceOf(PrefixQueryBuilder.class));
assertThat(rewritten, instanceOf(MatchAllQueryBuilder.class));
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
PrefixQueryBuilder queryBuilder = new PrefixQueryBuilder("unmapped_field", "foo");
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> queryBuilder.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}

View File

@ -53,7 +53,6 @@ import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.sameInstance;
@ -245,16 +244,6 @@ public class RangeQueryBuilderTests extends AbstractQueryTestCase<RangeQueryBuil
expectThrows(IllegalArgumentException.class, () -> rangeQueryBuilder.format("badFormat"));
}
/**
* Specifying a timezone together with an unmapped field should throw an exception.
*/
public void testToQueryUnmappedWithTimezone() throws QueryShardException {
RangeQueryBuilder query = new RangeQueryBuilder("bogus_field");
query.from(1).to(10).timeZone("UTC");
QueryShardException e = expectThrows(QueryShardException.class, () -> query.toQuery(createShardContext()));
assertThat(e.getMessage(), containsString("[range] time_zone can not be applied"));
}
public void testToQueryNumericField() throws IOException {
Query parsedQuery = rangeQuery(INT_FIELD_NAME).from(23).to(54).includeLower(true).includeUpper(false).toQuery(createShardContext());
// since age is automatically registered in data, we encode it as numeric

View File

@ -99,6 +99,19 @@ public class ScriptScoreQueryBuilderTests extends AbstractQueryTestCase<ScriptSc
assertFalse("query should not be cacheable: " + queryBuilder.toString(), context.isCacheable());
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
TermQueryBuilder termQueryBuilder = new TermQueryBuilder("unmapped_field", "foo");
String scriptStr = "1";
Script script = new Script(ScriptType.INLINE, MockScriptEngine.NAME, scriptStr, Collections.emptyMap());
ScriptScoreQueryBuilder scriptScoreQueryBuilder = new ScriptScoreQueryBuilder(termQueryBuilder, script);
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> scriptScoreQueryBuilder.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
public void testDisallowExpensiveQueries() {
QueryShardContext queryShardContext = mock(QueryShardContext.class);
when(queryShardContext.allowExpensiveQueries()).thenReturn(false);

View File

@ -90,7 +90,8 @@ public class SpanMultiTermQueryBuilderTests extends AbstractQueryTestCase<SpanMu
if (query instanceof SpanMatchNoDocsQuery) {
return;
}
assertThat(query, either(instanceOf(SpanMultiTermQueryWrapper.class)).or(instanceOf(FieldMaskingSpanQuery.class)));
assertThat(query, either(instanceOf(SpanMultiTermQueryWrapper.class))
.or(instanceOf(FieldMaskingSpanQuery.class)));
if (query instanceof SpanMultiTermQueryWrapper) {
SpanMultiTermQueryWrapper wrapper = (SpanMultiTermQueryWrapper) query;
Query innerQuery = queryBuilder.innerQuery().toQuery(context);

View File

@ -21,11 +21,11 @@ package org.elasticsearch.index.query;
import com.fasterxml.jackson.core.io.JsonStringEncoder;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.PointRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.index.mapper.MappedFieldType;
import java.io.IOException;
@ -91,7 +91,7 @@ public class TermQueryBuilderTests extends AbstractTermQueryTestCase<TermQueryBu
@Override
protected void doAssertLuceneQuery(TermQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
assertThat(query, either(instanceOf(TermQuery.class)).or(instanceOf(PointRangeQuery.class)));
assertThat(query, either(instanceOf(TermQuery.class)).or(instanceOf(PointRangeQuery.class)).or(instanceOf(MatchNoDocsQuery.class)));
MappedFieldType mapper = context.fieldMapper(queryBuilder.fieldName());
if (query instanceof TermQuery) {
TermQuery termQuery = (TermQuery) query;
@ -99,14 +99,12 @@ public class TermQueryBuilderTests extends AbstractTermQueryTestCase<TermQueryBu
String expectedFieldName = expectedFieldName(queryBuilder.fieldName());
assertThat(termQuery.getTerm().field(), equalTo(expectedFieldName));
if (mapper != null) {
Term term = ((TermQuery) mapper.termQuery(queryBuilder.value(), null)).getTerm();
assertThat(termQuery.getTerm(), equalTo(term));
} else {
assertThat(termQuery.getTerm().bytes(), equalTo(BytesRefs.toBytesRef(queryBuilder.value())));
}
} else {
Term term = ((TermQuery) mapper.termQuery(queryBuilder.value(), null)).getTerm();
assertThat(termQuery.getTerm(), equalTo(term));
} else if (mapper != null) {
assertEquals(query, mapper.termQuery(queryBuilder.value(), null));
} else {
assertThat(query, instanceOf(MatchNoDocsQuery.class));
}
}
@ -185,6 +183,16 @@ public class TermQueryBuilderTests extends AbstractTermQueryTestCase<TermQueryBu
TermQueryBuilder query = QueryBuilders.termQuery("_index", getIndex().getName());
QueryShardContext queryShardContext = createShardContext();
QueryBuilder rewritten = query.rewrite(queryShardContext);
assertThat(rewritten, instanceOf(TermQueryBuilder.class));
}
assertThat(rewritten, instanceOf(MatchAllQueryBuilder.class));
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
TermQueryBuilder queryBuilder = new TermQueryBuilder("unmapped_field", "foo");
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> queryBuilder.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}

View File

@ -112,16 +112,13 @@ public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuil
protected void doAssertLuceneQuery(TermsQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
if (queryBuilder.termsLookup() == null && (queryBuilder.values() == null || queryBuilder.values().isEmpty())) {
assertThat(query, instanceOf(MatchNoDocsQuery.class));
MatchNoDocsQuery matchNoDocsQuery = (MatchNoDocsQuery) query;
assertThat(matchNoDocsQuery.toString(), containsString("No terms supplied for \"terms\" query."));
} else if (queryBuilder.termsLookup() != null && randomTerms.size() == 0){
assertThat(query, instanceOf(MatchNoDocsQuery.class));
MatchNoDocsQuery matchNoDocsQuery = (MatchNoDocsQuery) query;
assertThat(matchNoDocsQuery.toString(), containsString("No terms supplied for \"terms\" query."));
} else {
assertThat(query, either(instanceOf(TermInSetQuery.class))
.or(instanceOf(PointInSetQuery.class))
.or(instanceOf(ConstantScoreQuery.class)));
.or(instanceOf(ConstantScoreQuery.class))
.or(instanceOf(MatchNoDocsQuery.class)));
if (query instanceof ConstantScoreQuery) {
assertThat(((ConstantScoreQuery) query).getQuery(), instanceOf(BooleanQuery.class));
}
@ -141,8 +138,13 @@ public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuil
}
String fieldName = expectedFieldName(queryBuilder.fieldName());
TermInSetQuery expected = new TermInSetQuery(fieldName,
terms.stream().filter(Objects::nonNull).map(Object::toString).map(BytesRef::new).collect(Collectors.toList()));
Query expected;
if (context.fieldMapper(fieldName) != null) {
expected = new TermInSetQuery(fieldName,
terms.stream().filter(Objects::nonNull).map(Object::toString).map(BytesRef::new).collect(Collectors.toList()));
} else {
expected = new MatchNoDocsQuery();
}
assertEquals(expected, query);
}
}
@ -267,8 +269,16 @@ public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuil
UnsupportedOperationException e = expectThrows(UnsupportedOperationException.class,
() -> termsQueryBuilder.toQuery(createShardContext()));
assertEquals("query must be rewritten first", e.getMessage());
assertEquals(rewriteAndFetch(termsQueryBuilder, createShardContext()), new TermsQueryBuilder(STRING_FIELD_NAME,
randomTerms.stream().filter(x -> x != null).collect(Collectors.toList()))); // terms lookup removes null values
// terms lookup removes null values
List<Object> nonNullTerms = randomTerms.stream().filter(x -> x != null).collect(Collectors.toList());
QueryBuilder expected;
if (nonNullTerms.isEmpty()) {
expected = new MatchNoneQueryBuilder();
} else {
expected = new TermsQueryBuilder(STRING_FIELD_NAME, nonNullTerms);
}
assertEquals(expected, rewriteAndFetch(termsQueryBuilder, createShardContext()));
}
public void testGeo() throws Exception {
@ -329,7 +339,7 @@ public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuil
TermsQueryBuilder query = new TermsQueryBuilder("_index", "does_not_exist", getIndex().getName());
QueryShardContext queryShardContext = createShardContext();
QueryBuilder rewritten = query.rewrite(queryShardContext);
assertThat(rewritten, instanceOf(TermsQueryBuilder.class));
assertThat(rewritten, instanceOf(MatchAllQueryBuilder.class));
}
@Override
@ -343,4 +353,5 @@ public class TermsQueryBuilderTests extends AbstractQueryTestCase<TermsQueryBuil
}
return query;
}
}

View File

@ -152,6 +152,16 @@ public class WildcardQueryBuilderTests extends AbstractQueryTestCase<WildcardQue
WildcardQueryBuilder query = new WildcardQueryBuilder("_index", firstHalfOfIndexName +"*");
QueryShardContext queryShardContext = createShardContext();
QueryBuilder rewritten = query.rewrite(queryShardContext);
assertThat(rewritten, instanceOf(WildcardQueryBuilder.class));
}
assertThat(rewritten, instanceOf(MatchAllQueryBuilder.class));
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
WildcardQueryBuilder queryBuilder = new WildcardQueryBuilder("unmapped_field", "foo");
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> queryBuilder.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}

View File

@ -119,7 +119,7 @@ public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQuery
@Override
public void testMustRewrite() throws IOException {
TermQueryBuilder tqb = new TermQueryBuilder("foo", "bar");
TermQueryBuilder tqb = new TermQueryBuilder(STRING_FIELD_NAME, "bar");
WrapperQueryBuilder qb = new WrapperQueryBuilder(tqb.toString());
UnsupportedOperationException e = expectThrows(UnsupportedOperationException.class, () -> qb.toQuery(createShardContext()));
assertEquals("this query must be rewritten first", e.getMessage());
@ -137,7 +137,7 @@ public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQuery
}
public void testRewriteWithInnerBoost() throws IOException {
final TermQueryBuilder query = new TermQueryBuilder("foo", "bar").boost(2);
final TermQueryBuilder query = new TermQueryBuilder(STRING_FIELD_NAME, "bar").boost(2);
QueryBuilder builder = new WrapperQueryBuilder(query.toString());
QueryShardContext shardContext = createShardContext();
assertEquals(query, builder.rewrite(shardContext));
@ -149,15 +149,15 @@ public class WrapperQueryBuilderTests extends AbstractQueryTestCase<WrapperQuery
QueryShardContext shardContext = createShardContext();
QueryBuilder qb = new WrapperQueryBuilder(
new WrapperQueryBuilder(new TermQueryBuilder("foo", "bar").toString()).toString()
new WrapperQueryBuilder(new TermQueryBuilder(STRING_FIELD_NAME, "bar").toString()).toString()
);
assertEquals(new TermQuery(new Term("foo", "bar")), qb.rewrite(shardContext).toQuery(shardContext));
assertEquals(new TermQuery(new Term(STRING_FIELD_NAME, "bar")), qb.rewrite(shardContext).toQuery(shardContext));
qb = new WrapperQueryBuilder(
new WrapperQueryBuilder(
new WrapperQueryBuilder(new TermQueryBuilder("foo", "bar").toString()).toString()
new WrapperQueryBuilder(new TermQueryBuilder(STRING_FIELD_NAME, "bar").toString()).toString()
).toString()
);
assertEquals(new TermQuery(new Term("foo", "bar")), qb.rewrite(shardContext).toQuery(shardContext));
assertEquals(new TermQuery(new Term(STRING_FIELD_NAME, "bar")), qb.rewrite(shardContext).toQuery(shardContext));
qb = new WrapperQueryBuilder(new BoolQueryBuilder().toString());
assertEquals(new MatchAllDocsQuery(), qb.rewrite(shardContext).toQuery(shardContext));

View File

@ -552,11 +552,12 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
}
public void testCustomWeightFactorQueryBuilderWithFunctionScore() throws IOException {
Query parsedQuery = parseQuery(functionScoreQuery(termQuery("name.last", "banon"), weightFactorFunction(1.3f)))
.toQuery(createShardContext());
QueryShardContext context = createShardContext();
Query parsedQuery = parseQuery(functionScoreQuery(termQuery(STRING_FIELD_NAME_2, "banon"), weightFactorFunction(1.3f)))
.rewrite(context).toQuery(context);
assertThat(parsedQuery, instanceOf(FunctionScoreQuery.class));
FunctionScoreQuery functionScoreQuery = (FunctionScoreQuery) parsedQuery;
assertThat(((TermQuery) functionScoreQuery.getSubQuery()).getTerm(), equalTo(new Term("name.last", "banon")));
assertThat(((TermQuery) functionScoreQuery.getSubQuery()).getTerm(), equalTo(new Term(STRING_FIELD_NAME_2, "banon")));
assertThat((double) (functionScoreQuery.getFunctions()[0]).getWeight(), closeTo(1.3, 0.001));
}
@ -642,14 +643,14 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
public void testRewrite() throws IOException {
FunctionScoreQueryBuilder functionScoreQueryBuilder =
new FunctionScoreQueryBuilder(new WrapperQueryBuilder(new TermQueryBuilder("foo", "bar").toString()))
new FunctionScoreQueryBuilder(new WrapperQueryBuilder(new TermQueryBuilder(STRING_FIELD_NAME, "bar").toString()))
.boostMode(CombineFunction.REPLACE)
.scoreMode(FunctionScoreQuery.ScoreMode.SUM)
.setMinScore(1)
.maxBoost(100);
FunctionScoreQueryBuilder rewrite = (FunctionScoreQueryBuilder) functionScoreQueryBuilder.rewrite(createShardContext());
assertNotSame(functionScoreQueryBuilder, rewrite);
assertEquals(rewrite.query(), new TermQueryBuilder("foo", "bar"));
assertEquals(rewrite.query(), new TermQueryBuilder(STRING_FIELD_NAME, "bar"));
assertEquals(rewrite.boostMode(), CombineFunction.REPLACE);
assertEquals(rewrite.scoreMode(), FunctionScoreQuery.ScoreMode.SUM);
assertEquals(rewrite.getMinScore(), 1f, 0.0001);
@ -657,18 +658,18 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
}
public void testRewriteWithFunction() throws IOException {
QueryBuilder firstFunction = new WrapperQueryBuilder(new TermQueryBuilder("tq", "1").toString());
TermQueryBuilder secondFunction = new TermQueryBuilder("tq", "2");
QueryBuilder queryBuilder = randomBoolean() ? new WrapperQueryBuilder(new TermQueryBuilder("foo", "bar").toString())
: new TermQueryBuilder("foo", "bar");
QueryBuilder firstFunction = new WrapperQueryBuilder(new TermQueryBuilder(STRING_FIELD_NAME_2, "1").toString());
TermQueryBuilder secondFunction = new TermQueryBuilder(STRING_FIELD_NAME_2, "2");
QueryBuilder queryBuilder = randomBoolean() ? new WrapperQueryBuilder(new TermQueryBuilder(STRING_FIELD_NAME, "bar").toString())
: new TermQueryBuilder(STRING_FIELD_NAME, "bar");
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(queryBuilder,
new FunctionScoreQueryBuilder.FilterFunctionBuilder[] {
new FunctionScoreQueryBuilder.FilterFunctionBuilder(firstFunction, new RandomScoreFunctionBuilder()),
new FunctionScoreQueryBuilder.FilterFunctionBuilder(secondFunction, new RandomScoreFunctionBuilder()) });
FunctionScoreQueryBuilder rewrite = (FunctionScoreQueryBuilder) functionScoreQueryBuilder.rewrite(createShardContext());
assertNotSame(functionScoreQueryBuilder, rewrite);
assertEquals(rewrite.query(), new TermQueryBuilder("foo", "bar"));
assertEquals(rewrite.filterFunctionBuilders()[0].getFilter(), new TermQueryBuilder("tq", "1"));
assertEquals(rewrite.query(), new TermQueryBuilder(STRING_FIELD_NAME, "bar"));
assertEquals(rewrite.filterFunctionBuilders()[0].getFilter(), new TermQueryBuilder(STRING_FIELD_NAME_2, "1"));
assertSame(rewrite.filterFunctionBuilders()[1].getFilter(), secondFunction);
}
@ -685,7 +686,8 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
builder.boostMode(randomFrom(CombineFunction.values()));
}
Query query = builder.toQuery(createShardContext());
QueryShardContext shardContext = createShardContext();
Query query = builder.rewrite(shardContext).toQuery(shardContext);
assertThat(query, instanceOf(FunctionScoreQuery.class));
CombineFunction expectedBoostMode = builder.boostMode() != null
@ -840,4 +842,27 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
}
return true;
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
TermQueryBuilder termQueryBuilder = new TermQueryBuilder("unmapped_field", "foo");
// main query needs rewriting
FunctionScoreQueryBuilder functionQueryBuilder1 = new FunctionScoreQueryBuilder(termQueryBuilder);
functionQueryBuilder1.setMinScore(1);
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> functionQueryBuilder1.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
// filter needs rewriting
FunctionScoreQueryBuilder functionQueryBuilder2 = new FunctionScoreQueryBuilder(new MatchAllQueryBuilder(),
new FilterFunctionBuilder[] {
new FilterFunctionBuilder(termQueryBuilder, new RandomScoreFunctionBuilder())
});
e = expectThrows(IllegalStateException.class,
() -> functionQueryBuilder2.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}

View File

@ -683,13 +683,15 @@ public class TermsAggregatorTests extends AggregatorTestCase {
}
if (multiValued == false) {
MappedFieldType filterFieldType = new KeywordFieldMapper.KeywordFieldType();
filterFieldType.setName("include");
aggregationBuilder = new FilterAggregationBuilder("_name1", QueryBuilders.termQuery("include", "yes"));
aggregationBuilder.subAggregation(new TermsAggregationBuilder("_name2", valueType)
.executionHint(executionHint)
.size(numTerms)
.collectMode(randomFrom(Aggregator.SubAggCollectionMode.values()))
.field("field"));
aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType);
aggregator = createAggregator(aggregationBuilder, indexSearcher, fieldType, filterFieldType);
aggregator.preCollection();
indexSearcher.search(new MatchAllDocsQuery(), aggregator);
aggregator.postCollection();

View File

@ -291,6 +291,7 @@ public class HighlightBuilderTests extends ESTestCase {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
HighlightBuilder highlightBuilder = randomHighlighterBuilder();
highlightBuilder = Rewriteable.rewrite(highlightBuilder, mockShardContext);
SearchContextHighlight highlight = highlightBuilder.build(mockShardContext);
for (SearchContextHighlight.Field field : highlight.fields()) {
String encoder = highlightBuilder.encoder() != null ? highlightBuilder.encoder() : HighlightBuilder.DEFAULT_ENCODER;

View File

@ -48,6 +48,7 @@ import org.elasticsearch.index.query.IdsQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.Rewriteable;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.script.MockScriptEngine;
import org.elasticsearch.script.ScriptEngine;
@ -154,7 +155,8 @@ public abstract class AbstractSortTestCase<T extends SortBuilder<T>> extends EST
QueryShardContext mockShardContext = createMockShardContext();
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
T sortBuilder = createTestItem();
SortFieldAndFormat sortField = sortBuilder.build(mockShardContext);
SortFieldAndFormat sortField = Rewriteable.rewrite(sortBuilder, mockShardContext)
.build(mockShardContext);
sortFieldAssertions(sortBuilder, sortField.field, sortField.format);
}
}

View File

@ -166,7 +166,9 @@ public abstract class AggregatorTestCase extends ESTestCase {
MappedFieldType... fieldTypes) throws IOException {
SearchContext searchContext = createSearchContext(indexSearcher, indexSettings, query, bucketConsumer, fieldTypes);
@SuppressWarnings("unchecked")
A aggregator = (A) aggregationBuilder.build(searchContext.getQueryShardContext(), null)
A aggregator = (A) aggregationBuilder
.rewrite(searchContext.getQueryShardContext())
.build(searchContext.getQueryShardContext(), null)
.create(searchContext, null, true);
return aggregator;
}

View File

@ -22,6 +22,7 @@ package org.elasticsearch.test;
import com.fasterxml.jackson.core.io.JsonStringEncoder;
import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.spans.SpanBoostQuery;
@ -453,7 +454,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
rewrite(secondLuceneQuery), rewrite(firstLuceneQuery));
}
if (supportsBoost()) {
if (supportsBoost() && firstLuceneQuery instanceof MatchNoDocsQuery == false) {
secondQuery.boost(firstQuery.boost() + 1f + randomFloat());
Query thirdLuceneQuery = rewriteQuery(secondQuery, context).toQuery(context);
assertNotEquals("modifying the boost doesn't affect the corresponding lucene query", rewrite(firstLuceneQuery),
@ -495,20 +496,23 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
* {@link #doAssertLuceneQuery(AbstractQueryBuilder, Query, QueryShardContext)} for query specific checks.
*/
private void assertLuceneQuery(QB queryBuilder, Query query, QueryShardContext context) throws IOException {
if (queryBuilder.queryName() != null) {
if (queryBuilder.queryName() != null && query instanceof MatchNoDocsQuery == false) {
Query namedQuery = context.copyNamedQueries().get(queryBuilder.queryName());
assertThat(namedQuery, equalTo(query));
}
if (query != null) {
if (queryBuilder.boost() != AbstractQueryBuilder.DEFAULT_BOOST) {
assertThat(query, either(instanceOf(BoostQuery.class)).or(instanceOf(SpanBoostQuery.class)));
assertThat(query, either(instanceOf(BoostQuery.class)).or(instanceOf(SpanBoostQuery.class))
.or(instanceOf(MatchNoDocsQuery.class)));
if (query instanceof SpanBoostQuery) {
SpanBoostQuery spanBoostQuery = (SpanBoostQuery) query;
assertThat(spanBoostQuery.getBoost(), equalTo(queryBuilder.boost()));
query = spanBoostQuery.getQuery();
} else {
} else if (query instanceof BoostQuery) {
BoostQuery boostQuery = (BoostQuery) query;
assertThat(boostQuery.getBoost(), equalTo(queryBuilder.boost()));
if (boostQuery.getQuery() instanceof MatchNoDocsQuery == false) {
assertThat(boostQuery.getBoost(), equalTo(queryBuilder.boost()));
}
query = boostQuery.getQuery();
}
}

View File

@ -29,6 +29,7 @@ import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.KeywordFieldMapper;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryShardContext;
@ -68,6 +69,7 @@ import static org.mockito.Mockito.when;
public class DocumentSubsetBitsetCacheTests extends ESTestCase {
private static final String MISSING_FIELD_NAME = "does-not-exist";
private static final int FIELD_COUNT = 10;
private ExecutorService singleThreadExecutor;
@ -99,7 +101,7 @@ public class DocumentSubsetBitsetCacheTests extends ESTestCase {
public void testNullBitSetIsReturnedForNonMatchingQuery() throws Exception {
final DocumentSubsetBitsetCache cache = newCache(Settings.EMPTY);
runTestOnIndex((shardContext, leafContext) -> {
final Query query = QueryBuilders.termQuery("does-not-exist", "any-value").toQuery(shardContext);
final Query query = QueryBuilders.termQuery(MISSING_FIELD_NAME, "any-value").rewrite(shardContext).toQuery(shardContext);
final BitSet bitSet = cache.getBitSet(query, leafContext);
assertThat(bitSet, nullValue());
});
@ -543,6 +545,17 @@ public class DocumentSubsetBitsetCacheTests extends ESTestCase {
private void runTestOnIndices(int numberIndices, CheckedConsumer<List<TestIndexContext>, Exception> body) throws Exception {
final MapperService mapperService = mock(MapperService.class);
when(mapperService.fieldType(Mockito.anyString())).thenAnswer(invocation -> {
final String fieldName = (String) invocation.getArguments()[0];
if (fieldName.equals(MISSING_FIELD_NAME)) {
return null;
} else {
KeywordFieldMapper.KeywordFieldType ft = new KeywordFieldMapper.KeywordFieldType();
ft.setName(fieldName);
ft.freeze();
return ft;
}
});
final Client client = mock(Client.class);
when(client.settings()).thenReturn(Settings.EMPTY);

View File

@ -29,12 +29,14 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.BigArrays;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.KeywordFieldMapper;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.mock.orig.Mockito;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.internal.ContextIndexSearcher;
import org.elasticsearch.test.AbstractBuilderTestCase;
@ -69,6 +71,13 @@ public class SecurityIndexReaderWrapperIntegrationTests extends AbstractBuilderT
when(mapperService.documentMapper()).thenReturn(null);
when(mapperService.simpleMatchToFullName(anyString()))
.then(invocationOnMock -> Collections.singletonList((String) invocationOnMock.getArguments()[0]));
when(mapperService.fieldType(Mockito.anyString())).then(invocation -> {
final String fieldName = (String) invocation.getArguments()[0];
KeywordFieldMapper.KeywordFieldType ft = new KeywordFieldMapper.KeywordFieldType();
ft.setName(fieldName);
ft.freeze();
return ft;
});
final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
@ -177,6 +186,13 @@ public class SecurityIndexReaderWrapperIntegrationTests extends AbstractBuilderT
when(mapperService.documentMapper()).thenReturn(null);
when(mapperService.simpleMatchToFullName(anyString()))
.then(invocationOnMock -> Collections.singletonList((String) invocationOnMock.getArguments()[0]));
when(mapperService.fieldType(Mockito.anyString())).then(invocation -> {
final String fieldName = (String) invocation.getArguments()[0];
KeywordFieldMapper.KeywordFieldType ft = new KeywordFieldMapper.KeywordFieldType();
ft.setName(fieldName);
ft.freeze();
return ft;
});
final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
@ -203,7 +219,8 @@ public class SecurityIndexReaderWrapperIntegrationTests extends AbstractBuilderT
IndicesAccessControl.IndexAccessControl limitedIndexAccessControl = new IndicesAccessControl.IndexAccessControl(true, new
FieldPermissions(),
DocumentPermissions.filteredBy(queries));
IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(shardId.getIndex(), Settings.EMPTY);
IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(shardId.getIndex(),
Settings.builder().put(IndexSettings.ALLOW_UNMAPPED.getKey(), false).build());
Client client = mock(Client.class);
when(client.settings()).thenReturn(Settings.EMPTY);
final long nowInMillis = randomNonNegativeLong();

View File

@ -170,4 +170,13 @@ public class PinnedQueryBuilderTests extends AbstractQueryTestCase<PinnedQueryBu
assertThat(rewritten, instanceOf(PinnedQueryBuilder.class));
}
@Override
public void testMustRewrite() throws IOException {
QueryShardContext context = createShardContext();
context.setAllowUnmappedFields(true);
PinnedQueryBuilder queryBuilder = new PinnedQueryBuilder(new TermQueryBuilder("unmapped_field", "42"));
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> queryBuilder.toQuery(context));
assertEquals("Rewrite first", e.getMessage());
}
}