Refactors BoolQueryBuilder and Parser. Splits the parse(QueryParseContext ctx) method into a parsing

and a query building part, adding NamedWriteable implementation for serialization and hashCode(),
equals() for testing.

This change also adds tests using fixed set of leaf queries (Terms, Ids, MatchAll) as nested Queries in test query setup.
Also QueryParseContext is adapted to return QueryBuilder instances for inner filter parses instead of
previously returning Query instances, keeping old methods in place but deprecating them.

Relates to #10217
Closes #11427
This commit is contained in:
Christoph Büscher 2015-06-11 12:42:00 +02:00 committed by Christoph Büscher
parent 22c5205e67
commit 458d62e4a0
7 changed files with 496 additions and 66 deletions

View File

@ -509,6 +509,20 @@ public abstract class StreamInput extends InputStream {
return namedWriteable.readFrom(this);
}
/**
* Reads a list of {@link NamedWriteable} from the current stream, by first reading its size and then
* reading the individual objects using {@link #readNamedWriteable()}.
*/
public <C> List<C> readNamedWritableList() throws IOException {
List<C> list = new ArrayList<>();
int size = readInt();
for (int i = 0; i < size; i++) {
C obj = readNamedWriteable();
list.add(obj);
}
return list;
}
public static StreamInput wrap(BytesReference reference) {
if (reference.hasArray() == false) {
reference = reference.toBytesArray();

View File

@ -457,4 +457,15 @@ public abstract class StreamOutput extends OutputStream {
writeString(namedWriteable.getName());
namedWriteable.writeTo(this);
}
/**
* Writes a list of {@link NamedWriteable} to the current stream, by first writing its size and then iterating over the objects
* in the list
*/
public void writeNamedWritableList(List<? extends NamedWriteable> list) throws IOException {
writeInt(list.size());
for (NamedWriteable obj : list) {
writeNamedWriteable(obj);
}
}
}

View File

@ -19,20 +19,36 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
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;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
/**
* A Query that matches documents matching boolean combinations of other queries.
*/
public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuilder<BoolQueryBuilder> {
public class BoolQueryBuilder extends QueryBuilder<BoolQueryBuilder> implements BoostableQueryBuilder<BoolQueryBuilder> {
public static final String NAME = "bool";
static final boolean ADJUST_PURE_NEGATIVE_DEFAULT = true;
static final boolean DISABLE_COORD_DEFAULT = false;
static final BoolQueryBuilder PROTOTYPE = new BoolQueryBuilder();
private final List<QueryBuilder> mustClauses = new ArrayList<>();
private final List<QueryBuilder> mustNotClauses = new ArrayList<>();
@ -41,18 +57,16 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
private final List<QueryBuilder> shouldClauses = new ArrayList<>();
private float boost = -1;
private float boost = 1.0f;
private Boolean disableCoord;
private boolean disableCoord = DISABLE_COORD_DEFAULT;
private boolean adjustPureNegative = ADJUST_PURE_NEGATIVE_DEFAULT;
private String minimumShouldMatch;
private Boolean adjustPureNegative;
private String queryName;
static final BoolQueryBuilder PROTOTYPE = new BoolQueryBuilder();
/**
* Adds a query that <b>must</b> appear in the matching documents and will
* contribute to scoring.
@ -62,6 +76,22 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
return this;
}
/**
* Adds a list of queries that <b>must</b> appear in the matching documents and will
* contribute to scoring.
*/
public BoolQueryBuilder must(List<QueryBuilder> queryBuilders) {
mustClauses.addAll(queryBuilders);
return this;
}
/**
* Gets the queries that <b>must</b> appear in the matching documents.
*/
public List<QueryBuilder> must() {
return this.mustClauses;
}
/**
* Adds a query that <b>must</b> appear in the matching documents but will
* not contribute to scoring.
@ -72,8 +102,23 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
}
/**
* Adds a query that <b>must not</b> appear in the matching documents and
* will not contribute to scoring.
* Adds a list of queries that <b>must</b> appear in the matching documents but will
* not contribute to scoring.
*/
public BoolQueryBuilder filter(List<QueryBuilder> queryBuilders) {
filterClauses.addAll(queryBuilders);
return this;
}
/**
* Gets the queries that <b>must</b> appear in the matching documents but don't conntribute to scoring
*/
public List<QueryBuilder> filter() {
return this.filterClauses;
}
/**
* Adds a query that <b>must not</b> appear in the matching documents.
*/
public BoolQueryBuilder mustNot(QueryBuilder queryBuilder) {
mustNotClauses.add(queryBuilder);
@ -81,7 +126,22 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
}
/**
* Adds a query that <i>should</i> appear in the matching documents. For a boolean query with no
* Adds a list of queries that <b>must not</b> appear in the matching documents.
*/
public BoolQueryBuilder mustNot(List<QueryBuilder> queryBuilders) {
mustNotClauses.addAll(queryBuilders);
return this;
}
/**
* Gets the queries that <b>must not</b> appear in the matching documents.
*/
public List<QueryBuilder> mustNot() {
return this.mustNotClauses;
}
/**
* Adds a clause that <i>should</i> be matched by the returned documents. For a boolean query with no
* <tt>MUST</tt> clauses one or more <code>SHOULD</code> clauses must match a document
* for the BooleanQuery to match.
*
@ -92,6 +152,28 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
return this;
}
/**
* Adds a list of clauses that <i>should</i> be matched by the returned documents. For a boolean query with no
* <tt>MUST</tt> clauses one or more <code>SHOULD</code> clauses must match a document
* for the BooleanQuery to match.
*
* @see #minimumNumberShouldMatch(int)
*/
public BoolQueryBuilder should(List<QueryBuilder> queryBuilders) {
shouldClauses.addAll(queryBuilders);
return this;
}
/**
* Gets the list of clauses that <b>should</b> be matched by the returned documents.
*
* @see #should(QueryBuilder)
* @see #minimumNumberShouldMatch(int)
*/
public List<QueryBuilder> should() {
return this.shouldClauses;
}
/**
* Sets the boost for this query. Documents matching this query will (in addition to the normal
* weightings) have their score multiplied by the boost provided.
@ -102,6 +184,13 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
return this;
}
/**
* Get the boost for this query.
*/
public float boost() {
return this.boost;
}
/**
* Disables <tt>Similarity#coord(int,int)</tt> in scoring. Defaults to <tt>false</tt>.
*/
@ -110,6 +199,13 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
return this;
}
/**
* @return whether the <tt>Similarity#coord(int,int)</tt> in scoring are disabled. Defaults to <tt>false</tt>.
*/
public boolean disableCoord() {
return this.disableCoord;
}
/**
* Specifies a minimum number of the optional (should) boolean clauses which must be satisfied.
* <p/>
@ -128,6 +224,23 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
return this;
}
/**
* Specifies a minimum number of the optional (should) boolean clauses which must be satisfied.
* @see BoolQueryBuilder#minimumNumberShouldMatch(int)
*/
public BoolQueryBuilder minimumNumberShouldMatch(String minimumNumberShouldMatch) {
this.minimumShouldMatch = minimumNumberShouldMatch;
return this;
}
/**
* @return the string representation of the minimumShouldMatch settings for this query
*/
public String minimumNumberShouldMatch() {
return this.minimumShouldMatch;
}
/**
* Sets the minimum should match using the special syntax (for example, supporting percentage).
*/
@ -154,6 +267,13 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
return this;
}
/**
* @return the setting for the adjust_pure_negative setting in this query
*/
public boolean adjustPureNegative() {
return this.adjustPureNegative;
}
/**
* Sets the query name for the filter that can be used when searching for matched_filters per hit.
*/
@ -162,6 +282,13 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
return this;
}
/**
* Gets the query name for the filter that can be used when searching for matched_filters per hit.
*/
public String queryName() {
return this.queryName;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
@ -169,25 +296,19 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
doXArrayContent("filter", filterClauses, builder, params);
doXArrayContent("must_not", mustNotClauses, builder, params);
doXArrayContent("should", shouldClauses, builder, params);
if (boost != -1) {
builder.field("boost", boost);
}
if (disableCoord != null) {
builder.field("disable_coord", disableCoord);
}
builder.field("boost", boost);
builder.field("disable_coord", disableCoord);
builder.field("adjust_pure_negative", adjustPureNegative);
if (minimumShouldMatch != null) {
builder.field("minimum_should_match", minimumShouldMatch);
}
if (adjustPureNegative != null) {
builder.field("adjust_pure_negative", adjustPureNegative);
}
if (queryName != null) {
builder.field("_name", queryName);
}
builder.endObject();
}
private void doXArrayContent(String field, List<QueryBuilder> clauses, XContentBuilder builder, Params params) throws IOException {
private static void doXArrayContent(String field, List<QueryBuilder> clauses, XContentBuilder builder, Params params) throws IOException {
if (clauses.isEmpty()) {
return;
}
@ -207,4 +328,100 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
public String queryId() {
return NAME;
}
@Override
public Query toQuery(QueryParseContext parseContext) throws QueryParsingException, IOException {
if (!hasClauses()) {
return new MatchAllDocsQuery();
}
BooleanQuery booleanQuery = new BooleanQuery(this.disableCoord);
addBooleanClauses(parseContext, booleanQuery, this.mustClauses, BooleanClause.Occur.MUST);
addBooleanClauses(parseContext, booleanQuery, this.mustNotClauses, BooleanClause.Occur.MUST_NOT);
addBooleanClauses(parseContext, booleanQuery, this.shouldClauses, BooleanClause.Occur.SHOULD);
addBooleanClauses(parseContext, booleanQuery, this.filterClauses, BooleanClause.Occur.FILTER);
booleanQuery.setBoost(this.boost);
Queries.applyMinimumShouldMatch(booleanQuery, this.minimumShouldMatch);
Query query = this.adjustPureNegative ? fixNegativeQueryIfNeeded(booleanQuery) : booleanQuery;
if (this.queryName != null) {
parseContext.addNamedQuery(this.queryName, query);
}
return query;
}
@Override
public QueryValidationException validate() {
// nothing to validate, clauses are optional, see hasClauses(), other parameters have defaults
return null;
}
private static void addBooleanClauses(QueryParseContext parseContext, BooleanQuery booleanQuery, List<QueryBuilder> clauses, Occur occurs)
throws IOException {
for (QueryBuilder query : clauses) {
Query luceneQuery = query.toQuery(parseContext);
if (luceneQuery != null) {
booleanQuery.add(new BooleanClause(luceneQuery, occurs));
}
}
}
@Override
public int hashCode() {
return Objects.hash(boost, adjustPureNegative, disableCoord,
minimumShouldMatch, queryName, mustClauses, shouldClauses, mustNotClauses, filterClauses);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
BoolQueryBuilder other = (BoolQueryBuilder) obj;
return Objects.equals(boost, other.boost) &&
Objects.equals(adjustPureNegative, other.adjustPureNegative) &&
Objects.equals(disableCoord, other.disableCoord) &&
Objects.equals(minimumShouldMatch, other.minimumShouldMatch) &&
Objects.equals(queryName, other.queryName) &&
Objects.equals(mustClauses, other.mustClauses) &&
Objects.equals(shouldClauses, other.shouldClauses) &&
Objects.equals(mustNotClauses, other.mustNotClauses) &&
Objects.equals(filterClauses, other.filterClauses);
}
@Override
public BoolQueryBuilder readFrom(StreamInput in) throws IOException {
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
List<QueryBuilder> queryBuilders = in.readNamedWritableList();
boolQueryBuilder.must(queryBuilders);
queryBuilders = in.readNamedWritableList();
boolQueryBuilder.mustNot(queryBuilders);
queryBuilders = in.readNamedWritableList();
boolQueryBuilder.should(queryBuilders);
queryBuilders = in.readNamedWritableList();
boolQueryBuilder.filter(queryBuilders);
boolQueryBuilder.boost = in.readFloat();
boolQueryBuilder.adjustPureNegative = in.readBoolean();
boolQueryBuilder.disableCoord = in.readBoolean();
boolQueryBuilder.queryName = in.readOptionalString();
boolQueryBuilder.minimumShouldMatch = in.readOptionalString();
return boolQueryBuilder;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeNamedWritableList(this.mustClauses);
out.writeNamedWritableList(this.mustNotClauses);
out.writeNamedWritableList(this.shouldClauses);
out.writeNamedWritableList(this.filterClauses);
out.writeFloat(this.boost);
out.writeBoolean(this.adjustPureNegative);
out.writeBoolean(this.disableCoord);
out.writeOptionalString(queryName);
out.writeOptionalString(this.minimumShouldMatch);
}
}

View File

@ -19,12 +19,8 @@
package org.elasticsearch.index.query;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser;
@ -32,12 +28,11 @@ import java.io.IOException;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
/**
*
* Parser for the {@link BoolQueryBuilder}
*/
public class BoolQueryParser extends BaseQueryParserTemp {
public class BoolQueryParser extends BaseQueryParser {
@Inject
public BoolQueryParser(Settings settings) {
@ -50,19 +45,23 @@ public class BoolQueryParser extends BaseQueryParserTemp {
}
@Override
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
XContentParser parser = parseContext.parser();
boolean disableCoord = false;
boolean disableCoord = BoolQueryBuilder.DISABLE_COORD_DEFAULT;
boolean adjustPureNegative = BoolQueryBuilder.ADJUST_PURE_NEGATIVE_DEFAULT;
float boost = 1.0f;
String minimumShouldMatch = null;
List<BooleanClause> clauses = newArrayList();
boolean adjustPureNegative = true;
List<QueryBuilder> mustClauses = newArrayList();
List<QueryBuilder> mustNotClauses = newArrayList();
List<QueryBuilder> shouldClauses = newArrayList();
List<QueryBuilder> filterClauses = newArrayList();
String queryName = null;
String currentFieldName = null;
XContentParser.Token token;
QueryBuilder query;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
@ -71,31 +70,31 @@ public class BoolQueryParser extends BaseQueryParserTemp {
} else if (token == XContentParser.Token.START_OBJECT) {
switch (currentFieldName) {
case "must":
Query query = parseContext.parseInnerQuery();
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST));
mustClauses.add(query);
}
break;
case "should":
query = parseContext.parseInnerQuery();
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.SHOULD));
shouldClauses.add(query);
if (parseContext.isFilter() && minimumShouldMatch == null) {
minimumShouldMatch = "1";
}
}
break;
case "filter":
query = parseContext.parseInnerFilter();
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.FILTER));
filterClauses.add(query);
}
break;
case "must_not":
case "mustNot":
query = parseContext.parseInnerFilter();
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST_NOT));
mustNotClauses.add(query);
}
break;
default:
@ -105,31 +104,31 @@ public class BoolQueryParser extends BaseQueryParserTemp {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
switch (currentFieldName) {
case "must":
Query query = parseContext.parseInnerQuery();
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST));
mustClauses.add(query);
}
break;
case "should":
query = parseContext.parseInnerQuery();
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.SHOULD));
shouldClauses.add(query);
if (parseContext.isFilter() && minimumShouldMatch == null) {
minimumShouldMatch = "1";
}
}
break;
case "filter":
query = parseContext.parseInnerFilter();
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.FILTER));
filterClauses.add(query);
}
break;
case "must_not":
case "mustNot":
query = parseContext.parseInnerFilter();
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST_NOT));
mustNotClauses.add(query);
}
break;
default:
@ -154,22 +153,18 @@ public class BoolQueryParser extends BaseQueryParserTemp {
}
}
}
if (clauses.isEmpty()) {
return new MatchAllDocsQuery();
}
BooleanQuery booleanQuery = new BooleanQuery(disableCoord);
for (BooleanClause clause : clauses) {
booleanQuery.add(clause);
}
booleanQuery.setBoost(boost);
Queries.applyMinimumShouldMatch(booleanQuery, minimumShouldMatch);
Query query = adjustPureNegative ? fixNegativeQueryIfNeeded(booleanQuery) : booleanQuery;
if (queryName != null) {
parseContext.addNamedQuery(queryName, query);
}
return query;
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
boolQuery.must(mustClauses);
boolQuery.mustNot(mustNotClauses);
boolQuery.should(shouldClauses);
boolQuery.filter(filterClauses);
boolQuery.boost(boost);
boolQuery.disableCoord(disableCoord);
boolQuery.adjustPureNegative(adjustPureNegative);
boolQuery.minimumNumberShouldMatch(minimumShouldMatch);
boolQuery.queryName(queryName);
boolQuery.validate();
return boolQuery;
}
@Override

View File

@ -267,18 +267,37 @@ public class QueryParseContext {
return result;
}
/**
* @deprecated replaced by calls to parseInnerFilterToQueryBuilder() for the resulting queries
*/
@Nullable
@Deprecated
public Query parseInnerFilter() throws QueryParsingException, IOException {
QueryBuilder builder = parseInnerFilterToQueryBuilder();
Query result = null;
if (builder != null) {
result = builder.toQuery(this);
}
return result;
}
/**
* @return
* @throws QueryParsingException
* @throws IOException
*/
@Nullable
public QueryBuilder parseInnerFilterToQueryBuilder() throws QueryParsingException, IOException {
final boolean originalIsFilter = isFilter;
try {
isFilter = true;
return parseInnerQuery();
return parseInnerQueryBuilder();
} finally {
isFilter = originalIsFilter;
}
}
public Query parseInnerFilter(String queryName) throws IOException, QueryParsingException {
public QueryBuilder parseInnerFilterToQueryBuilder(String queryName) throws IOException, QueryParsingException {
final boolean originalIsFilter = isFilter;
try {
isFilter = true;
@ -286,12 +305,22 @@ public class QueryParseContext {
if (queryParser == null) {
throw new QueryParsingException(this, "No query registered for [" + queryName + "]");
}
return queryParser.parse(this);
return queryParser.fromXContent(this);
} finally {
isFilter = originalIsFilter;
}
}
/**
* @deprecated replaced by calls to parseInnerFilterToQueryBuilder(String queryName) for the resulting queries
*/
@Nullable
@Deprecated
public Query parseInnerFilter(String queryName) throws IOException, QueryParsingException {
QueryBuilder builder = parseInnerFilterToQueryBuilder(queryName);
return (builder != null) ? builder.toQuery(this) : null;
}
public Collection<String> simpleMatchToIndexNames(String pattern) {
return indexQueryParser.mapperService.simpleMatchToIndexNames(pattern, getTypes());
}

View File

@ -0,0 +1,115 @@
/*
* 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.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.BooleanClause.Occur;
import org.elasticsearch.common.lucene.search.Queries;
import java.io.IOException;
import java.util.List;
import static org.hamcrest.Matchers.equalTo;
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
public class BoolQueryBuilderTest extends BaseQueryTestCase<BoolQueryBuilder> {
@Override
protected void assertLuceneQuery(BoolQueryBuilder queryBuilder, Query query, QueryParseContext context) {
if (queryBuilder.queryName() != null) {
Query namedQuery = context.copyNamedFilters().get(queryBuilder.queryName());
if (queryBuilder.hasClauses()) {
assertThat(namedQuery, equalTo(query));
} else {
assertNull(namedQuery);
}
}
}
@Override
protected BoolQueryBuilder createTestQueryBuilder() {
BoolQueryBuilder query = new BoolQueryBuilder();
if (randomBoolean()) {
query.boost(2.0f / randomIntBetween(1, 20));
}
if (randomBoolean()) {
query.adjustPureNegative(randomBoolean());
}
if (randomBoolean()) {
query.disableCoord(randomBoolean());
}
if (randomBoolean()) {
query.minimumNumberShouldMatch(randomIntBetween(1, 10));
}
int mustClauses = randomIntBetween(0, 3);
for (int i = 0; i < mustClauses; i++) {
query.must(RandomQueryBuilder.create(random()));
}
int mustNotClauses = randomIntBetween(0, 3);
for (int i = 0; i < mustNotClauses; i++) {
query.mustNot(RandomQueryBuilder.create(random()));
}
int shouldClauses = randomIntBetween(0, 3);
for (int i = 0; i < shouldClauses; i++) {
query.should(RandomQueryBuilder.create(random()));
}
int filterClauses = randomIntBetween(0, 3);
for (int i = 0; i < filterClauses; i++) {
query.filter(RandomQueryBuilder.create(random()));
}
if (randomBoolean()) {
query.queryName(randomUnicodeOfLengthBetween(3, 15));
}
return query;
}
@Override
protected BoolQueryBuilder createEmptyQueryBuilder() {
return new BoolQueryBuilder();
}
@Override
protected Query createExpectedQuery(BoolQueryBuilder queryBuilder, QueryParseContext context) throws IOException {
if (!queryBuilder.hasClauses()) {
return new MatchAllDocsQuery();
}
BooleanQuery boolQuery = new BooleanQuery(queryBuilder.disableCoord());
boolQuery.setBoost(queryBuilder.boost());
addBooleanClauses(context, boolQuery, queryBuilder.must(), BooleanClause.Occur.MUST);
addBooleanClauses(context, boolQuery, queryBuilder.mustNot(), BooleanClause.Occur.MUST_NOT);
addBooleanClauses(context, boolQuery, queryBuilder.should(), BooleanClause.Occur.SHOULD);
addBooleanClauses(context, boolQuery, queryBuilder.filter(), BooleanClause.Occur.FILTER);
Queries.applyMinimumShouldMatch(boolQuery, queryBuilder.minimumNumberShouldMatch());
Query returnedQuery = queryBuilder.adjustPureNegative() ? fixNegativeQueryIfNeeded(boolQuery) : boolQuery;
return returnedQuery;
}
private static void addBooleanClauses(QueryParseContext parseContext, BooleanQuery booleanQuery, List<QueryBuilder> clauses, Occur occurs)
throws IOException {
for (QueryBuilder query : clauses) {
booleanQuery.add(new BooleanClause(query.toQuery(parseContext), occurs));
}
}
}

View File

@ -0,0 +1,49 @@
/*
* 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 com.carrotsearch.randomizedtesting.generators.RandomInts;
import java.util.Random;
/**
* Utility class for creating random QueryBuilders.
* So far only leaf queries like {@link MatchAllQueryBuilder}, {@link TermQueryBuilder} or
* {@link IdsQueryBuilder} are returned.
*/
public class RandomQueryBuilder {
/**
* @param r random seed
* @return a random {@link QueryBuilder}
*/
public static QueryBuilder create(Random r) {
QueryBuilder query = null;
switch (RandomInts.randomIntBetween(r, 0, 2)) {
case 0:
return new MatchAllQueryBuilderTest().createTestQueryBuilder();
case 1:
return new TermQueryBuilderTest().createTestQueryBuilder();
case 2:
return new IdsQueryBuilderTest().createTestQueryBuilder();
}
return query;
}
}