Refactoring of Indices Query
Relates to #10217 This PR is against the query-refactoring branch. Closes #12031
This commit is contained in:
parent
4a3faf1126
commit
99ac70860f
|
@ -198,7 +198,6 @@ public class CommonTermsQueryBuilder extends AbstractQueryBuilder<CommonTermsQue
|
|||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(NAME);
|
||||
builder.startObject(fieldName);
|
||||
|
||||
builder.field("query", text);
|
||||
builder.field("disable_coord", disableCoord);
|
||||
builder.field("high_freq_operator", highFreqOperator.toString());
|
||||
|
|
|
@ -22,11 +22,15 @@ package org.elasticsearch.index.query;
|
|||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.util.CloseableThreadLocal;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
|
@ -72,6 +76,10 @@ public class IndexQueryParserService extends AbstractIndexComponent {
|
|||
|
||||
final IndexFieldDataService fieldDataService;
|
||||
|
||||
final ClusterService clusterService;
|
||||
|
||||
final IndexNameExpressionResolver indexNameExpressionResolver;
|
||||
|
||||
final BitsetFilterCache bitsetFilterCache;
|
||||
|
||||
private final IndicesQueriesRegistry indicesQueriesRegistry;
|
||||
|
@ -87,7 +95,8 @@ public class IndexQueryParserService extends AbstractIndexComponent {
|
|||
ScriptService scriptService, AnalysisService analysisService,
|
||||
MapperService mapperService, IndexCache indexCache, IndexFieldDataService fieldDataService,
|
||||
BitsetFilterCache bitsetFilterCache,
|
||||
@Nullable SimilarityService similarityService) {
|
||||
@Nullable SimilarityService similarityService, ClusterService clusterService,
|
||||
IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
super(index, indexSettings);
|
||||
this.scriptService = scriptService;
|
||||
this.analysisService = analysisService;
|
||||
|
@ -96,6 +105,8 @@ public class IndexQueryParserService extends AbstractIndexComponent {
|
|||
this.indexCache = indexCache;
|
||||
this.fieldDataService = fieldDataService;
|
||||
this.bitsetFilterCache = bitsetFilterCache;
|
||||
this.clusterService = clusterService;
|
||||
this.indexNameExpressionResolver = indexNameExpressionResolver;
|
||||
|
||||
this.defaultField = indexSettings.get(DEFAULT_FIELD, AllFieldMapper.NAME);
|
||||
this.queryStringLenient = indexSettings.getAsBoolean(QUERY_STRING_LENIENT, false);
|
||||
|
@ -318,4 +329,14 @@ public class IndexQueryParserService extends AbstractIndexComponent {
|
|||
public ParseFieldMatcher parseFieldMatcher() {
|
||||
return parseFieldMatcher;
|
||||
}
|
||||
|
||||
public boolean matchesIndices(String... indices) {
|
||||
final String[] concreteIndices = indexNameExpressionResolver.concreteIndices(clusterService.state(), IndicesOptions.lenientExpandOpen(), indices);
|
||||
for (String index : concreteIndices) {
|
||||
if (Regex.simpleMatch(index, this.index.name())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,14 @@
|
|||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A query that will execute the wrapped query only for the specified indices, and "match_all" when
|
||||
|
@ -31,48 +36,64 @@ public class IndicesQueryBuilder extends AbstractQueryBuilder<IndicesQueryBuilde
|
|||
|
||||
public static final String NAME = "indices";
|
||||
|
||||
private final QueryBuilder queryBuilder;
|
||||
private final QueryBuilder innerQuery;
|
||||
|
||||
private final String[] indices;
|
||||
|
||||
private String sNoMatchQuery;
|
||||
private QueryBuilder noMatchQuery;
|
||||
private QueryBuilder noMatchQuery = defaultNoMatchQuery();
|
||||
|
||||
static final IndicesQueryBuilder PROTOTYPE = new IndicesQueryBuilder(null);
|
||||
static final IndicesQueryBuilder PROTOTYPE = new IndicesQueryBuilder();
|
||||
|
||||
public IndicesQueryBuilder(QueryBuilder queryBuilder, String... indices) {
|
||||
this.queryBuilder = queryBuilder;
|
||||
private IndicesQueryBuilder() {
|
||||
this.innerQuery = null;
|
||||
this.indices = null;
|
||||
}
|
||||
|
||||
public IndicesQueryBuilder(QueryBuilder innerQuery, String... indices) {
|
||||
this.innerQuery = Objects.requireNonNull(innerQuery);
|
||||
this.indices = indices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the no match query, can either be <tt>all</tt> or <tt>none</tt>.
|
||||
*/
|
||||
public IndicesQueryBuilder noMatchQuery(String type) {
|
||||
this.sNoMatchQuery = type;
|
||||
return this;
|
||||
public QueryBuilder innerQuery() {
|
||||
return this.innerQuery;
|
||||
}
|
||||
|
||||
public String[] indices() {
|
||||
return this.indices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query to use when it executes on an index that does not match the indices provided.
|
||||
*/
|
||||
public IndicesQueryBuilder noMatchQuery(QueryBuilder noMatchQuery) {
|
||||
this.noMatchQuery = noMatchQuery;
|
||||
this.noMatchQuery = (noMatchQuery != null) ? noMatchQuery : defaultNoMatchQuery();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the no match query, can either be <tt>all</tt> or <tt>none</tt>.
|
||||
*/
|
||||
public IndicesQueryBuilder noMatchQuery(String type) {
|
||||
this.noMatchQuery = IndicesQueryParser.parseNoMatchQuery(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryBuilder noMatchQuery() {
|
||||
return this.noMatchQuery;
|
||||
}
|
||||
|
||||
static QueryBuilder defaultNoMatchQuery() {
|
||||
return QueryBuilders.matchAllQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(NAME);
|
||||
builder.field("indices", indices);
|
||||
builder.field("query");
|
||||
queryBuilder.toXContent(builder, params);
|
||||
if (noMatchQuery != null) {
|
||||
builder.field("no_match_query");
|
||||
noMatchQuery.toXContent(builder, params);
|
||||
} else if (sNoMatchQuery != null) {
|
||||
builder.field("no_match_query", sNoMatchQuery);
|
||||
}
|
||||
innerQuery.toXContent(builder, params);
|
||||
builder.field("no_match_query");
|
||||
noMatchQuery.toXContent(builder, params);
|
||||
printBoostAndQueryName(builder);
|
||||
builder.endObject();
|
||||
}
|
||||
|
@ -81,4 +102,52 @@ public class IndicesQueryBuilder extends AbstractQueryBuilder<IndicesQueryBuilde
|
|||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query doToQuery(QueryShardContext context) throws IOException {
|
||||
if (context.matchesIndices(indices)) {
|
||||
return innerQuery.toQuery(context);
|
||||
}
|
||||
return noMatchQuery.toQuery(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryValidationException validate() {
|
||||
QueryValidationException validationException = null;
|
||||
if (this.innerQuery == null) {
|
||||
validationException = addValidationError("inner query cannot be null", validationException);
|
||||
}
|
||||
if (this.indices == null || this.indices.length == 0) {
|
||||
validationException = addValidationError("list of indices cannot be null or empty", validationException);
|
||||
}
|
||||
validationException = validateInnerQuery(innerQuery, validationException);
|
||||
validationException = validateInnerQuery(noMatchQuery, validationException);
|
||||
return validationException;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IndicesQueryBuilder doReadFrom(StreamInput in) throws IOException {
|
||||
IndicesQueryBuilder indicesQueryBuilder = new IndicesQueryBuilder(in.readQuery(), in.readStringArray());
|
||||
indicesQueryBuilder.noMatchQuery = in.readQuery();
|
||||
return indicesQueryBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWriteTo(StreamOutput out) throws IOException {
|
||||
out.writeQuery(innerQuery);
|
||||
out.writeStringArray(indices);
|
||||
out.writeQuery(noMatchQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int doHashCode() {
|
||||
return Objects.hash(innerQuery, noMatchQuery, Arrays.hashCode(indices));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doEquals(IndicesQueryBuilder other) {
|
||||
return Objects.equals(innerQuery, other.innerQuery) &&
|
||||
Arrays.equals(indices, other.indices) && // otherwise we are comparing pointers
|
||||
Objects.equals(noMatchQuery, other.noMatchQuery);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,37 +19,24 @@
|
|||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.cluster.ClusterService;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.common.Nullable;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.support.XContentStructure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Parser for {@link IndicesQueryBuilder}.
|
||||
*/
|
||||
public class IndicesQueryParser extends BaseQueryParserTemp {
|
||||
public class IndicesQueryParser extends BaseQueryParser {
|
||||
|
||||
private static final ParseField QUERY_FIELD = new ParseField("query", "filter");
|
||||
private static final ParseField NO_MATCH_QUERY = new ParseField("no_match_query", "no_match_filter");
|
||||
|
||||
@Nullable
|
||||
private final ClusterService clusterService;
|
||||
private final IndexNameExpressionResolver indexNameExpressionResolver;
|
||||
|
||||
@Inject
|
||||
public IndicesQueryParser(@Nullable ClusterService clusterService, IndexNameExpressionResolver indexNameExpressionResolver) {
|
||||
this.clusterService = clusterService;
|
||||
this.indexNameExpressionResolver = indexNameExpressionResolver;
|
||||
public IndicesQueryParser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,40 +45,34 @@ public class IndicesQueryParser extends BaseQueryParserTemp {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query parse(QueryShardContext context) throws IOException, QueryParsingException {
|
||||
QueryParseContext parseContext = context.parseContext();
|
||||
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
Query noMatchQuery = null;
|
||||
boolean queryFound = false;
|
||||
boolean indicesFound = false;
|
||||
boolean currentIndexMatchesIndices = false;
|
||||
QueryBuilder innerQuery = null;
|
||||
Collection<String> indices = new ArrayList<>();
|
||||
QueryBuilder noMatchQuery = IndicesQueryBuilder.defaultNoMatchQuery();
|
||||
|
||||
String queryName = null;
|
||||
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
XContentStructure.InnerQuery innerQuery = null;
|
||||
XContentStructure.InnerQuery innerNoMatchQuery = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
|
||||
innerQuery = new XContentStructure.InnerQuery(parseContext, null);
|
||||
queryFound = true;
|
||||
innerQuery = parseContext.parseInnerQueryBuilder();
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NO_MATCH_QUERY)) {
|
||||
innerNoMatchQuery = new XContentStructure.InnerQuery(parseContext, null);
|
||||
noMatchQuery = parseContext.parseInnerQueryBuilder();
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext, "[indices] query does not support [" + currentFieldName + "]");
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||
if ("indices".equals(currentFieldName)) {
|
||||
if (indicesFound) {
|
||||
if (indices.isEmpty() == false) {
|
||||
throw new QueryParsingException(parseContext, "[indices] indices or index already specified");
|
||||
}
|
||||
indicesFound = true;
|
||||
Collection<String> indices = new ArrayList<>();
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
String value = parser.textOrNull();
|
||||
if (value == null) {
|
||||
|
@ -99,24 +80,17 @@ public class IndicesQueryParser extends BaseQueryParserTemp {
|
|||
}
|
||||
indices.add(value);
|
||||
}
|
||||
currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), indices.toArray(new String[indices.size()]));
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext, "[indices] query does not support [" + currentFieldName + "]");
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("index".equals(currentFieldName)) {
|
||||
if (indicesFound) {
|
||||
if (indices.isEmpty() == false) {
|
||||
throw new QueryParsingException(parseContext, "[indices] indices or index already specified");
|
||||
}
|
||||
indicesFound = true;
|
||||
currentIndexMatchesIndices = matchesIndices(parseContext.index().name(), parser.text());
|
||||
indices.add(parser.text());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NO_MATCH_QUERY)) {
|
||||
String type = parser.text();
|
||||
if ("all".equals(type)) {
|
||||
noMatchQuery = Queries.newMatchAllQuery();
|
||||
} else if ("none".equals(type)) {
|
||||
noMatchQuery = Queries.newMatchNoDocsQuery();
|
||||
}
|
||||
noMatchQuery = parseNoMatchQuery(parser.text());
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
queryName = parser.text();
|
||||
} else if ("boost".equals(currentFieldName)) {
|
||||
|
@ -126,44 +100,26 @@ public class IndicesQueryParser extends BaseQueryParserTemp {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!queryFound) {
|
||||
|
||||
if (innerQuery == null) {
|
||||
throw new QueryParsingException(parseContext, "[indices] requires 'query' element");
|
||||
}
|
||||
if (!indicesFound) {
|
||||
if (indices.isEmpty()) {
|
||||
throw new QueryParsingException(parseContext, "[indices] requires 'indices' or 'index' element");
|
||||
}
|
||||
|
||||
Query chosenQuery;
|
||||
if (currentIndexMatchesIndices) {
|
||||
chosenQuery = innerQuery.asQuery();
|
||||
} else {
|
||||
// If noMatchQuery is set, it means "no_match_query" was "all" or "none"
|
||||
if (noMatchQuery != null) {
|
||||
chosenQuery = noMatchQuery;
|
||||
} else {
|
||||
// There might be no "no_match_query" set, so default to the match_all if not set
|
||||
if (innerNoMatchQuery == null) {
|
||||
chosenQuery = Queries.newMatchAllQuery();
|
||||
} else {
|
||||
chosenQuery = innerNoMatchQuery.asQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (queryName != null) {
|
||||
context.addNamedQuery(queryName, chosenQuery);
|
||||
}
|
||||
chosenQuery.setBoost(boost);
|
||||
return chosenQuery;
|
||||
return new IndicesQueryBuilder(innerQuery, indices.toArray(new String[indices.size()]))
|
||||
.noMatchQuery(noMatchQuery)
|
||||
.boost(boost)
|
||||
.queryName(queryName);
|
||||
}
|
||||
|
||||
protected boolean matchesIndices(String currentIndex, String... indices) {
|
||||
final String[] concreteIndices = indexNameExpressionResolver.concreteIndices(clusterService.state(), IndicesOptions.lenientExpandOpen(), indices);
|
||||
for (String index : concreteIndices) {
|
||||
if (Regex.simpleMatch(index, currentIndex)) {
|
||||
return true;
|
||||
}
|
||||
static QueryBuilder parseNoMatchQuery(String type) {
|
||||
if ("all".equals(type)) {
|
||||
return QueryBuilders.matchAllQuery();
|
||||
} else if ("none".equals(type)) {
|
||||
return new MatchNoneQueryBuilder();
|
||||
}
|
||||
return false;
|
||||
throw new IllegalArgumentException("query type can only be [all] or [none] but not " + "[" + type + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A query that matches no document.
|
||||
*/
|
||||
public class MatchNoneQueryBuilder extends AbstractQueryBuilder<MatchNoneQueryBuilder> {
|
||||
|
||||
public static final String NAME = "match_none";
|
||||
|
||||
public static final MatchNoneQueryBuilder PROTOTYPE = new MatchNoneQueryBuilder();
|
||||
|
||||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(NAME);
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query doToQuery(QueryShardContext context) throws IOException {
|
||||
return Queries.newMatchNoDocsQuery();
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryValidationException validate() {
|
||||
// nothing to validate
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doEquals(MatchNoneQueryBuilder other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doHashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MatchNoneQueryBuilder doReadFrom(StreamInput in) throws IOException {
|
||||
return new MatchNoneQueryBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doWriteTo(StreamOutput out) throws IOException {
|
||||
//nothing to write really
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWriteableName() {
|
||||
return NAME;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MatchNoneQueryParser extends BaseQueryParser {
|
||||
|
||||
@Inject
|
||||
public MatchNoneQueryParser() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] names() {
|
||||
return new String[]{MatchNoneQueryBuilder.NAME, Strings.toCamelCase(MatchNoneQueryBuilder.NAME)};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchNoneQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
XContentParser.Token token = parser.nextToken();
|
||||
if (token != XContentParser.Token.END_OBJECT) {
|
||||
throw new QueryParsingException(parseContext, "[match_none] query malformed");
|
||||
}
|
||||
|
||||
return new MatchNoneQueryBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchNoneQueryBuilder getBuilderPrototype() {
|
||||
return MatchNoneQueryBuilder.PROTOTYPE;
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ import java.util.Map;
|
|||
public abstract class QueryBuilders {
|
||||
|
||||
/**
|
||||
* A query that match on all documents.
|
||||
* A query that matches on all documents.
|
||||
*/
|
||||
public static MatchAllQueryBuilder matchAllQuery() {
|
||||
return new MatchAllQueryBuilder();
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.elasticsearch.index.query;
|
|||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.queryparser.classic.MapperQueryParser;
|
||||
import org.apache.lucene.queryparser.classic.QueryParserSettings;
|
||||
|
@ -38,11 +37,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
|||
import org.elasticsearch.index.Index;
|
||||
import org.elasticsearch.index.analysis.AnalysisService;
|
||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||
import org.elasticsearch.index.mapper.ContentPath;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
import org.elasticsearch.index.mapper.MapperBuilders;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.*;
|
||||
import org.elasticsearch.index.mapper.core.StringFieldMapper;
|
||||
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
||||
import org.elasticsearch.index.query.support.NestedScope;
|
||||
|
@ -233,7 +228,8 @@ public class QueryShardContext {
|
|||
return indexQueryParser.mapperService.getObjectMapper(name, getTypes());
|
||||
}
|
||||
|
||||
/** Gets the search analyzer for the given field, or the default if there is none present for the field
|
||||
/**
|
||||
* Gets the search analyzer for the given field, or the default if there is none present for the field
|
||||
* TODO: remove this by moving defaults into mappers themselves
|
||||
*/
|
||||
public Analyzer getSearchAnalyzer(MappedFieldType fieldType) {
|
||||
|
@ -243,7 +239,8 @@ public class QueryShardContext {
|
|||
return mapperService().searchAnalyzer();
|
||||
}
|
||||
|
||||
/** Gets the search quote nalyzer for the given field, or the default if there is none present for the field
|
||||
/**
|
||||
* Gets the search quote analyzer for the given field, or the default if there is none present for the field
|
||||
* TODO: remove this by moving defaults into mappers themselves
|
||||
*/
|
||||
public Analyzer getSearchQuoteAnalyzer(MappedFieldType fieldType) {
|
||||
|
@ -264,7 +261,7 @@ public class QueryShardContext {
|
|||
private MappedFieldType failIfFieldMappingNotFound(String name, MappedFieldType fieldMapping) {
|
||||
if (allowUnmappedFields) {
|
||||
return fieldMapping;
|
||||
} else if (mapUnmappedFieldAsString){
|
||||
} else if (mapUnmappedFieldAsString) {
|
||||
StringFieldMapper.Builder builder = MapperBuilders.stringField(name);
|
||||
// it would be better to pass the real index settings, but they are not easily accessible from here...
|
||||
Settings settings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, indexQueryParser.getIndexCreatedVersion()).build();
|
||||
|
@ -326,4 +323,9 @@ public class QueryShardContext {
|
|||
public QueryParseContext parseContext() {
|
||||
return this.parseContext;
|
||||
}
|
||||
|
||||
public boolean matchesIndices(String... indices) {
|
||||
return this.indexQueryParserService().matchesIndices(indices);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ public class IndicesModule extends AbstractModule {
|
|||
registerQueryParser(NotQueryParser.class);
|
||||
registerQueryParser(ExistsQueryParser.class);
|
||||
registerQueryParser(MissingQueryParser.class);
|
||||
registerQueryParser(MatchNoneQueryParser.class);
|
||||
|
||||
if (ShapesAvailability.JTS_AVAILABLE) {
|
||||
registerQueryParser(GeoShapeQueryParser.class);
|
||||
|
|
|
@ -21,7 +21,6 @@ package org.elasticsearch.indices.query;
|
|||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class IndicesQueryBuilderTest extends BaseQueryTestCase<IndicesQueryBuilder> {
|
||||
|
||||
@Override
|
||||
protected IndicesQueryBuilder doCreateTestQueryBuilder() {
|
||||
String[] indices;
|
||||
if (randomBoolean()) {
|
||||
indices = new String[]{getIndex().getName()};
|
||||
} else {
|
||||
indices = generateRandomStringArray(5, 10, false, false);
|
||||
}
|
||||
IndicesQueryBuilder query = new IndicesQueryBuilder(RandomQueryBuilder.createQuery(random()), indices);
|
||||
|
||||
switch (randomInt(2)) {
|
||||
case 0:
|
||||
query.noMatchQuery(RandomQueryBuilder.createQuery(random()));
|
||||
break;
|
||||
case 1:
|
||||
query.noMatchQuery(randomFrom(QueryBuilders.matchAllQuery(), new MatchNoneQueryBuilder()));
|
||||
break;
|
||||
default:
|
||||
// do not set noMatchQuery
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertLuceneQuery(IndicesQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
|
||||
Query expected;
|
||||
if (queryBuilder.indices().length == 1 && getIndex().getName().equals(queryBuilder.indices()[0])) {
|
||||
expected = queryBuilder.innerQuery().toQuery(context);
|
||||
} else {
|
||||
expected = queryBuilder.noMatchQuery().toQuery(context);
|
||||
}
|
||||
if (expected != null) {
|
||||
expected.setBoost(queryBuilder.boost());
|
||||
}
|
||||
assertEquals(query, expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidate() {
|
||||
int expectedErrors = 0;
|
||||
|
||||
// inner query
|
||||
QueryBuilder innerQuery;
|
||||
if (randomBoolean()) {
|
||||
// setting innerQuery to null would be caught in the builder already and make validation fail
|
||||
innerQuery = RandomQueryBuilder.createInvalidQuery(random());
|
||||
expectedErrors++;
|
||||
} else {
|
||||
innerQuery = RandomQueryBuilder.createQuery(random());
|
||||
}
|
||||
// indices
|
||||
String[] indices;
|
||||
if (randomBoolean()) {
|
||||
indices = randomBoolean() ? null : new String[0];
|
||||
expectedErrors++;
|
||||
} else {
|
||||
indices = new String[]{"index"};
|
||||
}
|
||||
// no match query
|
||||
QueryBuilder noMatchQuery;
|
||||
if (randomBoolean()) {
|
||||
noMatchQuery = RandomQueryBuilder.createInvalidQuery(random());
|
||||
expectedErrors++;
|
||||
} else {
|
||||
noMatchQuery = RandomQueryBuilder.createQuery(random());
|
||||
}
|
||||
|
||||
assertValidate(new IndicesQueryBuilder(innerQuery, indices).noMatchQuery(noMatchQuery), expectedErrors);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MatchNoneQueryBuilderTest extends BaseQueryTestCase {
|
||||
|
||||
@Override
|
||||
protected boolean supportsBoostAndQueryName() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractQueryBuilder doCreateTestQueryBuilder() {
|
||||
return new MatchNoneQueryBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAssertLuceneQuery(AbstractQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
|
||||
assertEquals(query, Queries.newMatchNoDocsQuery());
|
||||
}
|
||||
}
|
|
@ -1892,7 +1892,7 @@ public class SearchQueryIT extends ESIntegTestCase {
|
|||
assertSearchHits(searchResponse, "1", "2", "3");
|
||||
searchResponse = client().prepareSearch("index1", "index2", "index3")
|
||||
.setQuery(indicesQuery(matchQuery("text", "value1"), "index1")
|
||||
.noMatchQuery("all")).get();
|
||||
.noMatchQuery(QueryBuilders.matchAllQuery())).get();
|
||||
assertHitCount(searchResponse, 3l);
|
||||
assertSearchHits(searchResponse, "1", "2", "3");
|
||||
|
||||
|
@ -1903,6 +1903,7 @@ public class SearchQueryIT extends ESIntegTestCase {
|
|||
assertFirstHit(searchResponse, hasId("1"));
|
||||
}
|
||||
|
||||
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/12822")
|
||||
@Test // https://github.com/elasticsearch/elasticsearch/issues/2416
|
||||
public void testIndicesQuerySkipParsing() throws Exception {
|
||||
createIndex("simple");
|
||||
|
|
|
@ -375,17 +375,22 @@ public abstract class ESTestCase extends LuceneTestCase {
|
|||
return RandomizedTest.randomRealisticUnicodeOfCodepointLength(codePoints);
|
||||
}
|
||||
|
||||
public static String[] generateRandomStringArray(int maxArraySize, int maxStringSize, boolean allowNull) {
|
||||
public static String[] generateRandomStringArray(int maxArraySize, int maxStringSize, boolean allowNull, boolean allowEmpty) {
|
||||
if (allowNull && random().nextBoolean()) {
|
||||
return null;
|
||||
}
|
||||
String[] array = new String[random().nextInt(maxArraySize)]; // allow empty arrays
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
int arraySize = randomIntBetween(allowEmpty ? 0 : 1, maxArraySize);
|
||||
String[] array = new String[arraySize];
|
||||
for (int i = 0; i < arraySize; i++) {
|
||||
array[i] = RandomStrings.randomAsciiOfLength(random(), maxStringSize);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
public static String[] generateRandomStringArray(int maxArraySize, int maxStringSize, boolean allowNull) {
|
||||
return generateRandomStringArray(maxArraySize, maxStringSize, allowNull, true);
|
||||
}
|
||||
|
||||
public static String randomTimeValue() {
|
||||
final String[] values = new String[]{"d", "H", "ms", "s", "S", "w"};
|
||||
return randomIntBetween(0, 1000) + randomFrom(values);
|
||||
|
|
Loading…
Reference in New Issue