Refactoring of Indices Query

Relates to #10217

This PR is against the query-refactoring branch.

Closes #12031
This commit is contained in:
Alex Ksikes 2015-07-02 21:38:22 -05:00
parent 4a3faf1126
commit 99ac70860f
14 changed files with 438 additions and 109 deletions

View File

@ -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());

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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");

View File

@ -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);