Merge pull request #11121 from cbuescher/feature/query-refactoring-boolquery

Query refactoring: BoolQueryBuilder and Parser
This commit is contained in:
Christoph Büscher 2015-06-11 13:04:23 +02:00
commit 4f4b1b74df
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;
}
}