diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java index adaf5ca6323a9..6eaec39d8fe03 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolateQueryBuilder.java @@ -24,6 +24,7 @@ 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.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.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 @@ protected Analyzer getWrappedAnalyzer(String fieldName) { 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 @@ static IndexSearcher createMultiDocumentSearcher(Analyzer analyzer, Collection<P } 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 @@ static PercolateQuery.QueryStore createStore(MappedFieldType queryBuilderFieldTy 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 @@ static PercolateQuery.QueryStore createStore(MappedFieldType queryBuilderFieldTy 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 @@ static PercolateQuery.QueryStore createStore(MappedFieldType queryBuilderFieldTy 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 -> { diff --git a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java index 4f1f6d04d302c..7464e17aeb022 100644 --- a/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java +++ b/modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorFieldMapper.java @@ -397,6 +397,8 @@ public void parse(ParseContext context) throws IOException { 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 void parse(ParseContext context) throws IOException { 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 @@ void processQuery(Query query, ParseContext context) { } } - 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 @@ static Query toQuery(QueryShardContext context, boolean mapUnmappedFieldsAsStrin // 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) { diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/QueryBuilderStoreTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/QueryBuilderStoreTests.java index 1c7ae3681ac63..ff9688487d32d 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/QueryBuilderStoreTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/QueryBuilderStoreTests.java @@ -38,10 +38,12 @@ 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 void testStoringQueryBuilders() throws IOException { 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); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ConstantFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/ConstantFieldType.java new file mode 100644 index 0000000000000..779e8f91350dc --- /dev/null +++ b/server/src/main/java/org/elasticsearch/index/mapper/ConstantFieldType.java @@ -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(); + } + } + +} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java index c3693f4ded9f3..603b8b22216aa 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java @@ -21,13 +21,7 @@ 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 MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext c } } - static final class IndexFieldType extends MappedFieldType { + static final class IndexFieldType extends ConstantFieldType { IndexFieldType() {} @@ -108,81 +102,8 @@ public String typeName() { } @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 diff --git a/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java index d5cc101ede8ad..2e027e21b8f5f 100644 --- a/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/AbstractQueryBuilder.java @@ -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 final Query toQuery(QueryShardContext context) throws IOException { 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 @@ static Collection<Query> toQueries(Collection<QueryBuilder> queryBuilders, Query 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); } diff --git a/server/src/main/java/org/elasticsearch/index/query/FuzzyQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/FuzzyQueryBuilder.java index 8df0fec044124..bd3048da29bb4 100644 --- a/server/src/main/java/org/elasticsearch/index/query/FuzzyQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/FuzzyQueryBuilder.java @@ -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.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 String getWriteableName() { 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); diff --git a/server/src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java index 304f17e88cb55..1ea6adb14d5b4 100644 --- a/server/src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/IdsQueryBuilder.java @@ -29,7 +29,6 @@ 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 String getWriteableName() { 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"); } } diff --git a/server/src/main/java/org/elasticsearch/index/query/PrefixQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/PrefixQueryBuilder.java index db596e2ecfc7b..44c8dd44b49c8 100644 --- a/server/src/main/java/org/elasticsearch/index/query/PrefixQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/PrefixQueryBuilder.java @@ -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 String getWriteableName() { @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 @@ protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws 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 diff --git a/server/src/main/java/org/elasticsearch/index/query/RangeQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/RangeQueryBuilder.java index 156fa27264e1f..6cb1704611ba0 100644 --- a/server/src/main/java/org/elasticsearch/index/query/RangeQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/RangeQueryBuilder.java @@ -21,7 +21,6 @@ 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.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 String getWriteableName() { // 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 @@ protected Query doToQuery(QueryShardContext context) throws IOException { 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 (query == null) { - query = new TermRangeQuery(this.fieldName, - BytesRefs.toBytesRef(from), BytesRefs.toBytesRef(to), - includeLower, includeUpper); + if (mapper == null) { + throw new IllegalStateException("Rewrite first"); } - return query; + DateMathParser forcedDateParser = getForceDateParser(); + return mapper.rangeQuery( + from, to, includeLower, includeUpper, + relation, timeZone, forcedDateParser, context); } @Override diff --git a/server/src/main/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilder.java index 49e5e53e1ed91..d55f9bceaa9bd 100644 --- a/server/src/main/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilder.java @@ -126,11 +126,16 @@ public static SpanMultiTermQueryBuilder fromXContent(XContentParser parser) thro @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 @@ protected Query doToQuery(QueryShardContext context) throws IOException { } } 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()); diff --git a/server/src/main/java/org/elasticsearch/index/query/TermQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/TermQueryBuilder.java index 262bfb2c6b5b3..8a0118d26e8db 100644 --- a/server/src/main/java/org/elasticsearch/index/query/TermQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/TermQueryBuilder.java @@ -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 static TermQueryBuilder fromXContent(XContentParser parser) throws IOExce @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 (query == null) { - query = new TermQuery(new Term(this.fieldName, BytesRefs.toBytesRef(this.value))); + if (mapper == null) { + throw new IllegalStateException("Rewrite first"); } - return query; + return mapper.termQuery(this.value, context); } @Override diff --git a/server/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java index 4cf205ea5b240..48aaa4cb8249c 100644 --- a/server/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/TermsQueryBuilder.java @@ -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.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 String getWriteableName() { @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 @@ protected Query doToQuery(QueryShardContext context) throws IOException { 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 @@ protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) { }))); 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; } } diff --git a/server/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java b/server/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java index 115fa8d476dfd..39ca3b0a45b6f 100644 --- a/server/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java +++ b/server/src/main/java/org/elasticsearch/index/query/WildcardQueryBuilder.java @@ -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.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 static WildcardQueryBuilder fromXContent(XContentParser parser) throws IO @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 @@ protected Query doToQuery(QueryShardContext context) throws IOException { MappedFieldType fieldType = context.fieldMapper(fieldName); if (fieldType == null) { - return new MatchNoDocsQuery("unknown field [" + fieldName + "]"); + throw new IllegalStateException("Rewrite first"); } MultiTermQuery.RewriteMethod method = QueryParsers.parseRewriteMethod( diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregationBuilder.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregationBuilder.java index 81d1b0445b3e2..45a755d07b286 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregationBuilder.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/significant/SignificantTermsAggregationBuilder.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryBuilder; +import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregatorFactories.Builder; @@ -127,10 +128,23 @@ protected SignificantTermsAggregationBuilder(SignificantTermsAggregationBuilder } @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); diff --git a/server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java index 97aa6f0d40589..9fc49ab0f5db5 100644 --- a/server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/BoolQueryBuilderTests.java @@ -22,6 +22,7 @@ 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 @@ protected void doAssertLuceneQuery(BoolQueryBuilder queryBuilder, Query query, Q 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 @@ private static List<BooleanClause> getBooleanClauses(List<QueryBuilder> queryBui 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 void testMinShouldMatchFilterWithoutShouldClauses() throws Exception { 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 void testMinShouldMatchBiggerThanNumberOfShouldClauses() throws Exception 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 void testRewrite() throws IOException { 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 void testRewrite() throws IOException { 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 void testRewriteWithMatchNone() throws IOException { 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 void testRewriteWithMatchNone() throws IOException { 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 void testRewriteWithMatchNone() throws IOException { 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()); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/BoostingQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/BoostingQueryBuilderTests.java index 534126ee5f35a..22d4db861be82 100644 --- a/server/src/test/java/org/elasticsearch/index/query/BoostingQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/BoostingQueryBuilderTests.java @@ -40,8 +40,8 @@ protected BoostingQueryBuilder doCreateTestQueryBuilder() { @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 void testRewrite() throws IOException { 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()); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java index fd2ad04af5f95..5281788a8307d 100644 --- a/server/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/ConstantScoreQueryBuilderTests.java @@ -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 @@ protected ConstantScoreQueryBuilder doCreateTestQueryBuilder() { @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 void testRewriteToMatchNone() throws IOException { 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()); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java index 5f3ab8b108faf..95042d1deae24 100644 --- a/server/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java @@ -164,4 +164,14 @@ protected QueryBuilder parseQuery(XContentParser parser) throws IOException { } 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()); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java index c6fd863a35ba4..9edd91fd812e0 100644 --- a/server/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/NestedQueryBuilderTests.java @@ -57,8 +57,6 @@ 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 @@ protected void initializeAdditionalMappings(MapperService mapperService) throws @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 void testFromJson() throws IOException { @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 { diff --git a/server/src/test/java/org/elasticsearch/index/query/PrefixQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/PrefixQueryBuilderTests.java index dba92d712c107..94596ffd6c58d 100644 --- a/server/src/test/java/org/elasticsearch/index/query/PrefixQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/PrefixQueryBuilderTests.java @@ -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 @@ private static PrefixQueryBuilder randomPrefixQuery() { @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 void testIllegalArguments() { 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 void testRewriteIndexQueryToNotMatchNone() throws Exception { 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()); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/RangeQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/RangeQueryBuilderTests.java index fea0356aa4cd0..a282bbe987d14 100644 --- a/server/src/test/java/org/elasticsearch/index/query/RangeQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/RangeQueryBuilderTests.java @@ -53,7 +53,6 @@ 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 void testIllegalArguments() { 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 diff --git a/server/src/test/java/org/elasticsearch/index/query/ScriptScoreQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/ScriptScoreQueryBuilderTests.java index 04322a01d0f68..d3ff1434c4d2c 100644 --- a/server/src/test/java/org/elasticsearch/index/query/ScriptScoreQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/ScriptScoreQueryBuilderTests.java @@ -99,6 +99,19 @@ public void testCacheability() throws IOException { 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); diff --git a/server/src/test/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilderTests.java index f3c80c2759c0f..69d096cf11ac2 100644 --- a/server/src/test/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/SpanMultiTermQueryBuilderTests.java @@ -90,7 +90,8 @@ protected void doAssertLuceneQuery(SpanMultiTermQueryBuilder queryBuilder, Query 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); diff --git a/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java index 0bf6ddbc57438..376aa54ef15b3 100644 --- a/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/TermQueryBuilderTests.java @@ -21,11 +21,11 @@ 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 @@ protected TermQueryBuilder createQueryBuilder(String fieldName, Object value) { @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 @@ protected void doAssertLuceneQuery(TermQueryBuilder queryBuilder, Query query, Q 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 void testRewriteIndexQueryToNotMatchNone() throws IOException { 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()); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java index 4725961deceed..913934a766268 100644 --- a/server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/TermsQueryBuilderTests.java @@ -112,16 +112,13 @@ private TermsLookup randomTermsLookup() { 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 @@ protected void doAssertLuceneQuery(TermsQueryBuilder queryBuilder, Query query, } 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 void testMustRewrite() throws IOException { 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 void testRewriteIndexQueryToNotMatchNone() throws IOException { 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 @@ protected QueryBuilder parseQuery(XContentParser parser) throws IOException { } return query; } + } diff --git a/server/src/test/java/org/elasticsearch/index/query/WildcardQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/WildcardQueryBuilderTests.java index bf88ab9ee2da6..9482e0bb87d49 100644 --- a/server/src/test/java/org/elasticsearch/index/query/WildcardQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/WildcardQueryBuilderTests.java @@ -152,6 +152,16 @@ public void testRewriteIndexQueryNotMatchNone() throws IOException { 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()); + } } diff --git a/server/src/test/java/org/elasticsearch/index/query/WrapperQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/WrapperQueryBuilderTests.java index d5ccff8402420..ad607a561fbce 100644 --- a/server/src/test/java/org/elasticsearch/index/query/WrapperQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/WrapperQueryBuilderTests.java @@ -119,7 +119,7 @@ public void testFromJson() throws IOException { @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 void testRewriteWithInnerName() throws IOException { } 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 void testRewriteInnerQueryToo() throws IOException { 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)); diff --git a/server/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java index fbaf93c0bd6f0..3f3e557e2ed4b 100644 --- a/server/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/functionscore/FunctionScoreQueryBuilderTests.java @@ -552,11 +552,12 @@ public void testMalformedThrowsException() throws IOException { } 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 void testFromJson() throws IOException { 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 void testRewrite() throws IOException { } 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 void testSingleScriptFunction() throws IOException { 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 @@ private boolean isCacheable(FunctionScoreQueryBuilder queryBuilder) { } 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()); + } } diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java index 7ef271fbe5055..abaad56eedb5b 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java @@ -683,13 +683,15 @@ private <T> void termsAggregator(ValueType valueType, MappedFieldType fieldType, } 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(); diff --git a/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilderTests.java b/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilderTests.java index d432573f38a9e..9daf41bf48df1 100644 --- a/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightBuilderTests.java @@ -291,6 +291,7 @@ public MappedFieldType fieldMapper(String name) { 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; diff --git a/server/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java b/server/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java index 931d06f07931f..332b3621eb392 100644 --- a/server/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java +++ b/server/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java @@ -48,6 +48,7 @@ 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 void testBuildSortField() throws IOException { 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); } } diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java index c8fe29b09b65c..ff5408a2ab808 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java @@ -166,7 +166,9 @@ protected <A extends Aggregator> A createAggregator(Query query, 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; } diff --git a/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java index b27628e699d32..207709f6cc13a 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/AbstractQueryTestCase.java @@ -22,6 +22,7 @@ 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 void testToQuery() throws IOException { 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 @@ protected boolean supportsQueryName() { * {@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(); } } diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCacheTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCacheTests.java index d859731b29ada..48ac7bea6a910 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCacheTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/DocumentSubsetBitsetCacheTests.java @@ -29,6 +29,7 @@ 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 @@ 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 void testSameBitSetIsReturnedForIdenticalQuery() throws Exception { 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 @@ null, null, mapperService, null, null, xContentRegistry(), writableRegistry(), 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); diff --git a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapperIntegrationTests.java b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapperIntegrationTests.java index 117fa4b2f49c9..a66be9616600c 100644 --- a/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapperIntegrationTests.java +++ b/x-pack/plugin/core/src/test/java/org/elasticsearch/xpack/core/security/authz/accesscontrol/SecurityIndexReaderWrapperIntegrationTests.java @@ -29,12 +29,14 @@ 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 void testDLS() throws Exception { 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 void testDLSWithLimitedPermissions() throws Exception { 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 void testDLSWithLimitedPermissions() throws Exception { 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(); diff --git a/x-pack/plugin/search-business-rules/src/test/java/org/elasticsearch/xpack/searchbusinessrules/PinnedQueryBuilderTests.java b/x-pack/plugin/search-business-rules/src/test/java/org/elasticsearch/xpack/searchbusinessrules/PinnedQueryBuilderTests.java index db3d46fc1a7dd..e91ef3a5d76f9 100644 --- a/x-pack/plugin/search-business-rules/src/test/java/org/elasticsearch/xpack/searchbusinessrules/PinnedQueryBuilderTests.java +++ b/x-pack/plugin/search-business-rules/src/test/java/org/elasticsearch/xpack/searchbusinessrules/PinnedQueryBuilderTests.java @@ -170,4 +170,13 @@ public void testRewrite() throws IOException { 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()); + } }