Merge pull request #11915 from cbuescher/feature/query-refactoring-emptyquerybuilder

Query Refactoring: Make parsing nested queries always return query builder
This commit is contained in:
Christoph Büscher 2015-07-03 09:55:29 +02:00
commit faa4e98aa5
34 changed files with 338 additions and 289 deletions

View File

@ -233,22 +233,6 @@ public abstract class AbstractQueryBuilder<QB extends AbstractQueryBuilder> exte
return queries;
}
/**
* Utility method that converts inner query builders to xContent and
* checks for null values, rendering out empty object in this case.
*/
protected static void doXContentInnerBuilder(XContentBuilder xContentBuilder, String fieldName,
QueryBuilder queryBuilder, Params params) throws IOException {
xContentBuilder.field(fieldName);
if (queryBuilder != null) {
queryBuilder.toXContent(xContentBuilder, params);
} else {
// we output an empty object, QueryParseContext#parseInnerQueryBuilder will parse this back to `null` value
xContentBuilder.startObject();
xContentBuilder.endObject();
}
}
protected static QueryValidationException validateInnerQueries(List<QueryBuilder> queryBuilders, QueryValidationException initialValidationException) {
QueryValidationException validationException = initialValidationException;
for (QueryBuilder queryBuilder : queryBuilders) {

View File

@ -98,6 +98,10 @@ public class AndQueryBuilder extends AbstractQueryBuilder<AndQueryBuilder> {
query.add(innerQuery, Occur.MUST);
}
}
if (query.clauses().isEmpty()) {
// no inner lucene query exists, ignore upstream
return null;
}
return query;
}

View File

@ -54,10 +54,8 @@ public class AndQueryParser extends BaseQueryParser {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
queriesFound = true;
QueryBuilder filter = parseContext.parseInnerFilterToQueryBuilder();
if (filter != null) {
queries.add(filter);
}
}
} else {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
@ -69,19 +67,15 @@ public class AndQueryParser extends BaseQueryParser {
queriesFound = true;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
QueryBuilder filter = parseContext.parseInnerFilterToQueryBuilder();
if (filter != null) {
queries.add(filter);
}
}
} else {
queriesFound = true;
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
QueryBuilder filter = parseContext.parseInnerFilterToQueryBuilder();
if (filter != null) {
queries.add(filter);
}
}
}
} else if (token.isValue()) {
if ("_name".equals(currentFieldName)) {
queryName = parser.text();

View File

@ -255,16 +255,16 @@ public class BoolQueryBuilder extends AbstractQueryBuilder<BoolQueryBuilder> {
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
if (!hasClauses()) {
return new MatchAllDocsQuery();
}
BooleanQuery booleanQuery = new BooleanQuery(disableCoord);
addBooleanClauses(parseContext, booleanQuery, mustClauses, BooleanClause.Occur.MUST);
addBooleanClauses(parseContext, booleanQuery, mustNotClauses, BooleanClause.Occur.MUST_NOT);
addBooleanClauses(parseContext, booleanQuery, shouldClauses, BooleanClause.Occur.SHOULD);
addBooleanClauses(parseContext, booleanQuery, filterClauses, BooleanClause.Occur.FILTER);
if (booleanQuery.clauses().isEmpty()) {
return new MatchAllDocsQuery();
}
Queries.applyMinimumShouldMatch(booleanQuery, minimumShouldMatch);
return adjustPureNegative ? fixNegativeQueryIfNeeded(booleanQuery) : booleanQuery;
}

View File

@ -71,14 +71,13 @@ public class BoolQueryParser extends BaseQueryParser {
switch (currentFieldName) {
case "must":
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
mustClauses.add(query);
}
break;
case "should":
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
shouldClauses.add(query);
// EmptyQueryBuilder does not add lucene query later, skip setting minuminShouldMatch
if (query != EmptyQueryBuilder.PROTOTYPE) {
if (parseContext.isFilter() && minimumShouldMatch == null) {
minimumShouldMatch = "1";
}
@ -86,16 +85,12 @@ public class BoolQueryParser extends BaseQueryParser {
break;
case "filter":
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
filterClauses.add(query);
}
break;
case "must_not":
case "mustNot":
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
mustNotClauses.add(query);
}
break;
default:
throw new QueryParsingException(parseContext, "[bool] query does not support [" + currentFieldName + "]");
@ -105,14 +100,13 @@ public class BoolQueryParser extends BaseQueryParser {
switch (currentFieldName) {
case "must":
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
mustClauses.add(query);
}
break;
case "should":
query = parseContext.parseInnerQueryBuilder();
if (query != null) {
shouldClauses.add(query);
// EmptyQueryBuilder does not add lucene query later, skip setting minuminShouldMatch
if (query != EmptyQueryBuilder.PROTOTYPE) {
if (parseContext.isFilter() && minimumShouldMatch == null) {
minimumShouldMatch = "1";
}
@ -120,16 +114,12 @@ public class BoolQueryParser extends BaseQueryParser {
break;
case "filter":
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
filterClauses.add(query);
}
break;
case "must_not":
case "mustNot":
query = parseContext.parseInnerFilterToQueryBuilder();
if (query != null) {
mustNotClauses.add(query);
}
break;
default:
throw new QueryParsingException(parseContext, "bool query does not support [" + currentFieldName + "]");

View File

@ -44,23 +44,31 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
public static final String NAME = "boosting";
private QueryBuilder positiveQuery;
private final QueryBuilder positiveQuery;
private QueryBuilder negativeQuery;
private final QueryBuilder negativeQuery;
private float negativeBoost = -1;
static final BoostingQueryBuilder PROTOTYPE = new BoostingQueryBuilder();
public BoostingQueryBuilder() {
/**
* this constructor only used for prototype
*/
private BoostingQueryBuilder() {
this.positiveQuery = null;
this.negativeQuery = null;
}
/**
* Add the positive query for this boosting query.
* Create a new {@link BoostingQueryBuilder}
*
* @param positiveQuery the positive query for this boosting query.
* @param negativeQuery the negative query for this boosting query.
*/
public BoostingQueryBuilder positive(QueryBuilder positiveQuery) {
this.positiveQuery = positiveQuery;
return this;
public BoostingQueryBuilder(QueryBuilder positiveQuery, QueryBuilder negativeQuery) {
this.positiveQuery = Objects.requireNonNull(positiveQuery);
this.negativeQuery = Objects.requireNonNull(negativeQuery);
}
/**
@ -70,14 +78,6 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
return this.positiveQuery;
}
/**
* Add the negative query for this boosting query.
*/
public BoostingQueryBuilder negative(QueryBuilder negativeQuery) {
this.negativeQuery = negativeQuery;
return this;
}
/**
* Get the negative query for this boosting query.
*/
@ -103,8 +103,10 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
doXContentInnerBuilder(builder, "positive", positiveQuery, params);
doXContentInnerBuilder(builder, "negative", negativeQuery, params);
builder.field("positive");
positiveQuery.toXContent(builder, params);
builder.field("negative");
negativeQuery.toXContent(builder, params);
builder.field("negative_boost", negativeBoost);
printBoostAndQueryName(builder);
builder.endObject();
@ -128,13 +130,10 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
// make upstream queries ignore this query by returning `null`
// if either inner query builder is null or returns null-Query
if (positiveQuery == null || negativeQuery == null) {
return null;
}
Query positive = positiveQuery.toQuery(parseContext);
Query negative = negativeQuery.toQuery(parseContext);
// make upstream queries ignore this query by returning `null`
// if either inner query builder returns null
if (positive == null || negative == null) {
return null;
}
@ -158,9 +157,7 @@ public class BoostingQueryBuilder extends AbstractQueryBuilder<BoostingQueryBuil
protected BoostingQueryBuilder doReadFrom(StreamInput in) throws IOException {
QueryBuilder positiveQuery = in.readNamedWriteable();
QueryBuilder negativeQuery = in.readNamedWriteable();
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder();
boostingQuery.positive(positiveQuery);
boostingQuery.negative(negativeQuery);
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder(positiveQuery, negativeQuery);
boostingQuery.negativeBoost = in.readFloat();
return boostingQuery;
}

View File

@ -88,9 +88,7 @@ public class BoostingQueryParser extends BaseQueryParser {
throw new QueryParsingException(parseContext, "[boosting] query requires 'negative_boost' to be set to be a positive value'");
}
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder();
boostingQuery.positive(positiveQuery);
boostingQuery.negative(negativeQuery);
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder(positiveQuery, negativeQuery);
boostingQuery.negativeBoost(negativeBoost);
boostingQuery.boost(boost);
boostingQuery.queryName(queryName);

View File

@ -38,7 +38,12 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
private final QueryBuilder filterBuilder;
static final ConstantScoreQueryBuilder PROTOTYPE = new ConstantScoreQueryBuilder(null);
static final ConstantScoreQueryBuilder PROTOTYPE = new ConstantScoreQueryBuilder();
// only used for prototype
private ConstantScoreQueryBuilder() {
this.filterBuilder = null;
}
/**
* A query that wraps another query and simply returns a constant score equal to the
@ -47,7 +52,7 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
* @param filterBuilder The query to wrap in a constant score query
*/
public ConstantScoreQueryBuilder(QueryBuilder filterBuilder) {
this.filterBuilder = filterBuilder;
this.filterBuilder = Objects.requireNonNull(filterBuilder);
}
/**
@ -60,24 +65,19 @@ public class ConstantScoreQueryBuilder extends AbstractQueryBuilder<ConstantScor
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
doXContentInnerBuilder(builder, "filter", filterBuilder, params);
builder.field("filter");
filterBuilder.toXContent(builder, params);
printBoostAndQueryName(builder);
builder.endObject();
}
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
// current DSL allows empty inner filter clauses, we ignore them
if (filterBuilder == null) {
return null;
}
Query innerFilter = filterBuilder.toQuery(parseContext);
if (innerFilter == null ) {
// return null so that parent queries (e.g. bool) also ignore this
return null;
}
return new ConstantScoreQuery(filterBuilder.toQuery(parseContext));
}

View File

@ -62,9 +62,7 @@ public class DisMaxQueryParser extends BaseQueryParser {
if ("queries".equals(currentFieldName)) {
queriesFound = true;
QueryBuilder query = parseContext.parseInnerQueryBuilder();
if (query != null) {
queries.add(query);
}
} else {
throw new QueryParsingException(parseContext, "[dis_max] query does not support [" + currentFieldName + "]");
}
@ -73,9 +71,7 @@ public class DisMaxQueryParser extends BaseQueryParser {
queriesFound = true;
while (token != XContentParser.Token.END_ARRAY) {
QueryBuilder query = parseContext.parseInnerQueryBuilder();
if (query != null) {
queries.add(query);
}
token = parser.nextToken();
}
} else {

View File

@ -0,0 +1,83 @@
/*
* 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.xcontent.XContentBuilder;
import java.io.IOException;
/**
* A {@link QueryBuilder} that is a stand in replacement for an empty query clause in the DSL.
* The current DSL allows parsing inner queries / filters like "{ }", in order to have a
* valid non-null representation of these clauses that actually do nothing we can use this class.
*
* This builder has no corresponding parser and it is not registered under the query name. It is
* intended to be used internally as a stand-in for nested queries that are left empty and should
* be ignored upstream.
*/
public class EmptyQueryBuilder extends AbstractQueryBuilder<EmptyQueryBuilder> {
public static final String NAME = "empty_query";
/** the one and only empty query builder */
public static final EmptyQueryBuilder PROTOTYPE = new EmptyQueryBuilder();
private EmptyQueryBuilder() {
// prevent other constructors
}
@Override
public String getName() {
return NAME;
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
// empty
}
/**
* @returns <tt>null</tt> to signal to caller that this query should be ignored
* in the context of the DSL.
*/
@Override
public Query doToQuery(QueryParseContext parseContext) throws QueryParsingException, IOException {
return null;
}
@Override
public QueryValidationException validate() {
// nothing to validate
return null;
}
@Override
public EmptyQueryBuilder doReadFrom(StreamInput in) throws IOException {
return EmptyQueryBuilder.PROTOTYPE;
}
@Override
public void doWriteTo(StreamOutput out) throws IOException {
// nothing to serialize
}
}

View File

@ -39,7 +39,7 @@ public class FQueryFilterBuilder extends AbstractQueryBuilder<FQueryFilterBuilde
public static final String NAME = "fquery";
static final FQueryFilterBuilder PROTOTYPE = new FQueryFilterBuilder(null);
static final FQueryFilterBuilder PROTOTYPE = new FQueryFilterBuilder();
private final QueryBuilder queryBuilder;
@ -49,7 +49,11 @@ public class FQueryFilterBuilder extends AbstractQueryBuilder<FQueryFilterBuilde
* @param queryBuilder The query to wrap as a filter
*/
public FQueryFilterBuilder(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
this.queryBuilder = Objects.requireNonNull(queryBuilder);
}
private FQueryFilterBuilder() {
this.queryBuilder = null;
}
/**
@ -62,7 +66,8 @@ public class FQueryFilterBuilder extends AbstractQueryBuilder<FQueryFilterBuilde
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(FQueryFilterBuilder.NAME);
doXContentInnerBuilder(builder, "query", queryBuilder, params);
builder.field("query");
queryBuilder.toXContent(builder, params);
printBoostAndQueryName(builder);
builder.endObject();
}
@ -70,9 +75,6 @@ public class FQueryFilterBuilder extends AbstractQueryBuilder<FQueryFilterBuilde
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
// inner query builder can potentially be `null`, in that case we ignore it
if (this.queryBuilder == null) {
return null;
}
Query innerQuery = this.queryBuilder.toQuery(parseContext);
if (innerQuery == null) {
return null;

View File

@ -76,9 +76,6 @@ public class FQueryFilterParser extends BaseQueryParser {
if (!queryFound) {
throw new QueryParsingException(parseContext, "[fquery] requires 'query' element");
}
if (wrappedQuery == null) {
return null;
}
FQueryFilterBuilder queryBuilder = new FQueryFilterBuilder(wrappedQuery);
queryBuilder.queryName(queryName);
queryBuilder.boost(boost);

View File

@ -40,6 +40,7 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
static final FieldMaskingSpanQueryBuilder PROTOTYPE = new FieldMaskingSpanQueryBuilder();
// only used for prototype
private FieldMaskingSpanQueryBuilder() {
this.queryBuilder = null;
this.fieldName = null;
@ -53,7 +54,7 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
*/
public FieldMaskingSpanQueryBuilder(SpanQueryBuilder queryBuilder, String fieldName) {
this.queryBuilder = Objects.requireNonNull(queryBuilder);
this.fieldName = fieldName;
this.fieldName = Objects.requireNonNull(fieldName);
}
/**
@ -73,7 +74,8 @@ public class FieldMaskingSpanQueryBuilder extends AbstractQueryBuilder<FieldMask
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
doXContentInnerBuilder(builder, "query", queryBuilder, params);
builder.field("query");
queryBuilder.toXContent(builder, params);
builder.field("field", fieldName);
printBoostAndQueryName(builder);
builder.endObject();

View File

@ -37,10 +37,14 @@ public class NotQueryBuilder extends AbstractQueryBuilder<NotQueryBuilder> {
private final QueryBuilder filter;
static final NotQueryBuilder PROTOTYPE = new NotQueryBuilder(null);
static final NotQueryBuilder PROTOTYPE = new NotQueryBuilder();
public NotQueryBuilder(QueryBuilder filter) {
this.filter = filter;
this.filter = Objects.requireNonNull(filter);
}
private NotQueryBuilder() {
this.filter = null;
}
/**
@ -53,16 +57,14 @@ public class NotQueryBuilder extends AbstractQueryBuilder<NotQueryBuilder> {
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(NAME);
doXContentInnerBuilder(builder, "query", filter, params);
builder.field("query");
filter.toXContent(builder, params);
printBoostAndQueryName(builder);
builder.endObject();
}
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
if (filter == null) {
return null;
}
Query luceneQuery = filter.toQuery(parseContext);
if (luceneQuery == null) {
return null;

View File

@ -95,6 +95,10 @@ public class OrQueryBuilder extends AbstractQueryBuilder<OrQueryBuilder> {
query.add(innerQuery, Occur.SHOULD);
}
}
if (query.clauses().isEmpty()) {
// no inner lucene query exists, ignore upstream
return null;
}
return query;
}

View File

@ -277,8 +277,8 @@ public abstract class QueryBuilders {
* Unlike the "NOT" clause, this still selects documents that contain undesirable terms,
* but reduces their overall score:
*/
public static BoostingQueryBuilder boostingQuery() {
return new BoostingQueryBuilder();
public static BoostingQueryBuilder boostingQuery(QueryBuilder positiveQuery, QueryBuilder negativeQuery) {
return new BoostingQueryBuilder(positiveQuery, negativeQuery);
}
/**

View File

@ -40,7 +40,7 @@ public class QueryFilterBuilder extends AbstractQueryBuilder<QueryFilterBuilder>
private final QueryBuilder queryBuilder;
static final QueryFilterBuilder PROTOTYPE = new QueryFilterBuilder(null);
static final QueryFilterBuilder PROTOTYPE = new QueryFilterBuilder();
/**
* A filter that simply wraps a query.
@ -48,7 +48,11 @@ public class QueryFilterBuilder extends AbstractQueryBuilder<QueryFilterBuilder>
* @param queryBuilder The query to wrap as a filter
*/
public QueryFilterBuilder(QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
this.queryBuilder = Objects.requireNonNull(queryBuilder);
}
private QueryFilterBuilder() {
this.queryBuilder = null;
}
/**
@ -72,15 +76,13 @@ public class QueryFilterBuilder extends AbstractQueryBuilder<QueryFilterBuilder>
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
doXContentInnerBuilder(builder, NAME, queryBuilder, params);
builder.field(NAME);
queryBuilder.toXContent(builder, params);
}
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
// inner query builder can potentially be `null`, in that case we ignore it
if (this.queryBuilder == null) {
return null;
}
Query innerQuery = this.queryBuilder.toQuery(parseContext);
if (innerQuery == null) {
return null;

View File

@ -232,7 +232,7 @@ public class QueryParseContext {
token = parser.nextToken();
if (token == XContentParser.Token.END_OBJECT) {
// empty query
return null;
return EmptyQueryBuilder.PROTOTYPE;
}
if (token != XContentParser.Token.FIELD_NAME) {
throw new QueryParsingException(this, "[_na] query malformed, no field after start_object");

View File

@ -26,6 +26,7 @@ import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.EmptyQueryBuilder;
import org.elasticsearch.index.query.QueryParser;
import java.util.Map;
@ -45,6 +46,9 @@ public class IndicesQueriesRegistry extends AbstractComponent {
}
namedWriteableRegistry.registerPrototype(queryParser.getBuilderPrototype());
}
// EmptyQueryBuilder is not registered as query parser but used internally.
// We need to register it with the NamedWriteableRegistry in order to serialize it
namedWriteableRegistry.registerPrototype(EmptyQueryBuilder.PROTOTYPE);
this.queryParsers = ImmutableMap.copyOf(queryParsers);
}

View File

@ -43,6 +43,9 @@ public class AndQueryBuilderTest extends BaseQueryTestCase<AndQueryBuilder> {
query.add(innerQuery, Occur.MUST);
}
}
if (query.clauses().isEmpty()) {
return null;
}
return query;
}
@ -78,6 +81,16 @@ public class AndQueryBuilderTest extends BaseQueryTestCase<AndQueryBuilder> {
context.indexQueryParserService().queryParser(AndQueryBuilder.PROTOTYPE.getName()).fromXContent(context);
}
@Test(expected=NullPointerException.class)
public void testNullConstructor() {
new AndQueryBuilder(EmptyQueryBuilder.PROTOTYPE, null);
}
@Test(expected=NullPointerException.class)
public void testAddNull() {
new AndQueryBuilder(EmptyQueryBuilder.PROTOTYPE).add(null);
}
@Test
public void testValidate() {
AndQueryBuilder andQuery = new AndQueryBuilder();

View File

@ -234,14 +234,22 @@ public abstract class BaseQueryTestCase<QB extends AbstractQueryBuilder<QB>> ext
Query expectedQuery = createExpectedQuery(testQuery, context);
Query actualQuery = testQuery.toQuery(context);
// expectedQuery can be null, e.g. in case of BoostingQueryBuilder
// with inner clause that returns null itself
if (expectedQuery == null) {
assertNull("Expected a null query, saw some object.", actualQuery);
} else {
assertThat(actualQuery, instanceOf(expectedQuery.getClass()));
assertThat(actualQuery, equalTo(expectedQuery));
assertLuceneQuery(testQuery, actualQuery, context);
}
}
protected final Query createExpectedQuery(QB queryBuilder, QueryParseContext context) throws IOException {
Query expectedQuery = doCreateExpectedQuery(queryBuilder, context);
if (expectedQuery != null) {
expectedQuery.setBoost(queryBuilder.boost());
}
return expectedQuery;
}

View File

@ -72,12 +72,14 @@ public class BoolQueryBuilderTest extends BaseQueryTestCase<BoolQueryBuilder> {
}
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);
if (boolQuery.clauses().isEmpty()) {
return new MatchAllDocsQuery();
}
Queries.applyMinimumShouldMatch(boolQuery, queryBuilder.minimumNumberShouldMatch());
return queryBuilder.adjustPureNegative() ? fixNegativeQueryIfNeeded(boolQuery) : boolQuery;
}
@ -85,7 +87,10 @@ public class BoolQueryBuilderTest extends BaseQueryTestCase<BoolQueryBuilder> {
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));
Query innerQuery = query.toQuery(parseContext);
if (innerQuery != null) {
booleanQuery.add(new BooleanClause(innerQuery, occurs));
}
}
}
@ -129,4 +134,24 @@ public class BoolQueryBuilderTest extends BaseQueryTestCase<BoolQueryBuilder> {
}
assertValidate(booleanQuery, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testAddNullMust() {
new BoolQueryBuilder().must(null);
}
@Test(expected=NullPointerException.class)
public void testAddNullMustNot() {
new BoolQueryBuilder().mustNot(null);
}
@Test(expected=NullPointerException.class)
public void testAddNullShould() {
new BoolQueryBuilder().should(null);
}
@Test(expected=NullPointerException.class)
public void testAddNullFilter() {
new BoolQueryBuilder().filter(null);
}
}

View File

@ -21,9 +21,6 @@ package org.elasticsearch.index.query;
import org.apache.lucene.queries.BoostingQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.junit.Test;
import java.io.IOException;
@ -32,9 +29,7 @@ public class BoostingQueryBuilderTest extends BaseQueryTestCase<BoostingQueryBui
@Override
protected BoostingQueryBuilder doCreateTestQueryBuilder() {
BoostingQueryBuilder query = new BoostingQueryBuilder();
query.positive(RandomQueryBuilder.createQuery(random()));
query.negative(RandomQueryBuilder.createQuery(random()));
BoostingQueryBuilder query = new BoostingQueryBuilder(RandomQueryBuilder.createQuery(random()), RandomQueryBuilder.createQuery(random()));
query.negativeBoost(2.0f / randomIntBetween(1, 20));
return query;
}
@ -43,89 +38,30 @@ public class BoostingQueryBuilderTest extends BaseQueryTestCase<BoostingQueryBui
protected Query doCreateExpectedQuery(BoostingQueryBuilder queryBuilder, QueryParseContext context) throws IOException {
Query positive = queryBuilder.positive().toQuery(context);
Query negative = queryBuilder.negative().toQuery(context);
return new BoostingQuery(positive, negative, queryBuilder.negativeBoost());
}
/**
* test that setting a null negative/positive clause renders a parseable query
*/
@Test
public void testInnerClauseNull() throws IOException {
BoostingQueryBuilder boostingQueryBuilder = new BoostingQueryBuilder().negativeBoost(0.1f);
if (randomBoolean()) {
boostingQueryBuilder.positive(new MatchAllQueryBuilder());
} else {
boostingQueryBuilder.negative(new MatchAllQueryBuilder());
}
String contentString = boostingQueryBuilder.toString();
XContentParser parser = XContentFactory.xContent(contentString).createParser(contentString);
QueryParseContext context = createContext();
context.reset(parser);
assertQueryHeader(parser, boostingQueryBuilder.getName());
QueryBuilder parsedBuilder = context.indexQueryParserService().queryParser(boostingQueryBuilder.getName()).fromXContent(context);
assertNotNull(parsedBuilder);
assertNotSame(parsedBuilder, boostingQueryBuilder);
assertEquals(parsedBuilder, boostingQueryBuilder);
}
/**
* tests that we signal upstream queries to ignore this query by returning <tt>null</tt>
* if any of the inner query builder is not set
*/
@Test
public void testInnerQueryBuilderNull() throws IOException {
BoostingQueryBuilder boostingQueryBuilder = new BoostingQueryBuilder();
if (randomBoolean()) {
boostingQueryBuilder.positive(new MatchAllQueryBuilder()).negative(null);
} else {
boostingQueryBuilder.positive(null).negative(new MatchAllQueryBuilder());
}
assertNull(boostingQueryBuilder.toQuery(createContext()));
}
@Test
public void testInnerQueryBuilderReturnsNull() throws IOException {
QueryBuilder noOpBuilder = new AbstractQueryBuilder() {
@Override
public String getName() {
return "dummy";
}
@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
}
@Override
protected Query doToQuery(QueryParseContext parseContext) throws IOException {
if (positive == null || negative == null) {
return null;
}
};
BoostingQueryBuilder boostingQueryBuilder;
if (randomBoolean()) {
boostingQueryBuilder = new BoostingQueryBuilder().positive(new MatchAllQueryBuilder()).negative(noOpBuilder);
} else {
boostingQueryBuilder = new BoostingQueryBuilder().positive(noOpBuilder).negative(new MatchAllQueryBuilder());
}
assertNull(boostingQueryBuilder.toQuery(createContext()));
return new BoostingQuery(positive, negative, queryBuilder.negativeBoost());
}
@Test
public void testValidate() {
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder();
int totalExpectedErrors = 0;
if (randomBoolean()) {
boostingQuery.negative(RandomQueryBuilder.createInvalidQuery(random()));
QueryBuilder positive;
QueryBuilder negative;
if (frequently()) {
negative = RandomQueryBuilder.createInvalidQuery(random());
totalExpectedErrors++;
} else if(rarely()) {
boostingQuery.negative(RandomQueryBuilder.createQuery(random()));
} else {
negative = RandomQueryBuilder.createQuery(random());
}
if (randomBoolean()) {
boostingQuery.positive(RandomQueryBuilder.createInvalidQuery(random()));
if (frequently()) {
positive = RandomQueryBuilder.createInvalidQuery(random());
totalExpectedErrors++;
} else if(rarely()) {
boostingQuery.positive(RandomQueryBuilder.createQuery(random()));
} else {
positive = RandomQueryBuilder.createQuery(random());
}
BoostingQueryBuilder boostingQuery = new BoostingQueryBuilder(positive, negative);
if (frequently()) {
boostingQuery.negativeBoost(0.5f);
} else {
@ -134,4 +70,13 @@ public class BoostingQueryBuilderTest extends BaseQueryTestCase<BoostingQueryBui
}
assertValidate(boostingQuery, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullConstructorArgument() {
if (randomBoolean()) {
new BoostingQueryBuilder(null, RandomQueryBuilder.createQuery(random()));
} else {
new BoostingQueryBuilder(RandomQueryBuilder.createQuery(random()), null);
}
}
}

View File

@ -31,7 +31,11 @@ public class ConstantScoreQueryBuilderTest extends BaseQueryTestCase<ConstantSco
@Override
protected Query doCreateExpectedQuery(ConstantScoreQueryBuilder testBuilder, QueryParseContext context) throws QueryParsingException, IOException {
return new ConstantScoreQuery(testBuilder.query().toQuery(context));
Query innerQuery = testBuilder.query().toQuery(context);
if (innerQuery != null) {
return new ConstantScoreQuery(innerQuery);
}
return null;
}
/**
@ -46,7 +50,7 @@ public class ConstantScoreQueryBuilderTest extends BaseQueryTestCase<ConstantSco
* test that missing "filter" element causes {@link QueryParsingException}
*/
@Test(expected=QueryParsingException.class)
public void testNoFilterElement() throws IOException {
public void testFilterElement() throws IOException {
QueryParseContext context = createContext();
String queryId = ConstantScoreQueryBuilder.PROTOTYPE.getName();
String queryString = "{ \""+queryId+"\" : {}";
@ -56,24 +60,6 @@ public class ConstantScoreQueryBuilderTest extends BaseQueryTestCase<ConstantSco
context.indexQueryParserService().queryParser(queryId).fromXContent(context);
}
/**
* Test empty "filter" element.
* Current DSL allows inner filter element to be empty, returning a `null` inner filter builder
*/
@Test
public void testEmptyFilterElement() throws IOException {
QueryParseContext context = createContext();
String queryId = ConstantScoreQueryBuilder.PROTOTYPE.getName();
String queryString = "{ \""+queryId+"\" : { \"filter\" : { } }";
XContentParser parser = XContentFactory.xContent(queryString).createParser(queryString);
context.reset(parser);
assertQueryHeader(parser, queryId);
ConstantScoreQueryBuilder queryBuilder = (ConstantScoreQueryBuilder) context.indexQueryParserService()
.queryParser(queryId).fromXContent(context);
assertNull(queryBuilder.query());
assertNull(queryBuilder.toQuery(createContext()));
}
@Test
public void testValidate() {
QueryBuilder innerQuery;
@ -87,4 +73,9 @@ public class ConstantScoreQueryBuilderTest extends BaseQueryTestCase<ConstantSco
ConstantScoreQueryBuilder constantScoreQuery = new ConstantScoreQueryBuilder(innerQuery);
assertValidate(constantScoreQuery, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullConstructor() {
new ConstantScoreQueryBuilder(null);
}
}

View File

@ -26,12 +26,17 @@ import org.elasticsearch.common.xcontent.XContentParser;
import org.junit.Test;
import java.io.IOException;
import java.util.Collection;
public class DisMaxQueryBuilderTest extends BaseQueryTestCase<DisMaxQueryBuilder> {
@Override
protected Query doCreateExpectedQuery(DisMaxQueryBuilder testBuilder, QueryParseContext context) throws QueryParsingException, IOException {
return new DisjunctionMaxQuery(AbstractQueryBuilder.toQueries(testBuilder.queries(), context), testBuilder.tieBreaker());
Collection<Query> queries = AbstractQueryBuilder.toQueries(testBuilder.queries(), context);
if (queries.isEmpty()) {
return null;
}
return new DisjunctionMaxQuery(queries, testBuilder.tieBreaker());
}
/**
@ -82,6 +87,11 @@ public class DisMaxQueryBuilderTest extends BaseQueryTestCase<DisMaxQueryBuilder
assertNull(disMaxBuilder.toQuery(context));
}
@Test(expected=NullPointerException.class)
public void testAddNull() {
new DisMaxQueryBuilder().add(null);
}
@Test
public void testValidate() {
DisMaxQueryBuilder disMaxQuery = new DisMaxQueryBuilder();

View File

@ -32,11 +32,15 @@ public class FQueryFilterBuilderTest extends BaseQueryTestCase<FQueryFilterBuild
@Override
protected Query doCreateExpectedQuery(FQueryFilterBuilder queryBuilder, QueryParseContext context) throws QueryParsingException, IOException {
return new ConstantScoreQuery(queryBuilder.innerQuery().toQuery(context));
Query query = queryBuilder.innerQuery().toQuery(context);
if (query != null) {
return new ConstantScoreQuery(query);
}
return null;
}
/**
* @return a AndQueryBuilder with random limit between 0 and 20
* @return a FQueryFilterBuilder with random inner query
*/
@Override
protected FQueryFilterBuilder doCreateTestQueryBuilder() {
@ -49,7 +53,7 @@ public class FQueryFilterBuilderTest extends BaseQueryTestCase<FQueryFilterBuild
*/
@Test
public void testNoInnerQuery() throws QueryParsingException, IOException {
FQueryFilterBuilder queryFilterQuery = new FQueryFilterBuilder(null);
FQueryFilterBuilder queryFilterQuery = new FQueryFilterBuilder(EmptyQueryBuilder.PROTOTYPE);
assertNull(queryFilterQuery.toQuery(createContext()));
}
@ -85,4 +89,9 @@ public class FQueryFilterBuilderTest extends BaseQueryTestCase<FQueryFilterBuild
FQueryFilterBuilder fQueryFilter = new FQueryFilterBuilder(innerQuery);
assertValidate(fQueryFilter, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullConstructo() {
new FQueryFilterBuilder(null);
}
}

View File

@ -61,7 +61,7 @@ public class FieldMaskingSpanQueryBuilderTest extends BaseQueryTestCase<FieldMas
if (randomBoolean()) {
fieldName = "fieldName";
} else {
fieldName = randomBoolean() ? "" : null;
fieldName = "";
totalExpectedErrors++;
}
if (randomBoolean()) {
@ -73,4 +73,14 @@ public class FieldMaskingSpanQueryBuilderTest extends BaseQueryTestCase<FieldMas
FieldMaskingSpanQueryBuilder queryBuilder = new FieldMaskingSpanQueryBuilder(spanQueryBuilder, fieldName);
assertValidate(queryBuilder, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullFieldName() {
new FieldMaskingSpanQueryBuilder(new SpanTermQueryBuilder("name", "value"), null);
}
@Test(expected=NullPointerException.class)
public void testNullInnerQuery() {
new FieldMaskingSpanQueryBuilder(null, "");
}
}

View File

@ -31,7 +31,7 @@ public class NotQueryBuilderTest extends BaseQueryTestCase<NotQueryBuilder> {
@Override
protected Query doCreateExpectedQuery(NotQueryBuilder queryBuilder, QueryParseContext context) throws QueryParsingException, IOException {
if (queryBuilder.filter() == null) {
if (queryBuilder.filter().toQuery(context) == null) {
return null;
}
return Queries.not(queryBuilder.filter().toQuery(context));
@ -45,25 +45,9 @@ public class NotQueryBuilderTest extends BaseQueryTestCase<NotQueryBuilder> {
return new NotQueryBuilder(RandomQueryBuilder.createQuery(random()));
}
/**
* test corner case where no inner query exist
*/
@Test
public void testNoInnerQuerry() throws QueryParsingException, IOException {
NotQueryBuilder notQuery = new NotQueryBuilder(null);
assertNull(notQuery.toQuery(createContext()));
}
/**
* Test corner case where inner queries returns null.
* This is legal, e.g. here {@link ConstantScoreQueryBuilder} can wrap a filter
* element that itself parses to <tt>null</tt>, e.g.
* '{ "constant_score" : "filter {} }'
*/
@Test
public void testInnerQuery() throws QueryParsingException, IOException {
NotQueryBuilder notQuery = new NotQueryBuilder(new ConstantScoreQueryBuilder(null));
assertNull(notQuery.toQuery(createContext()));
@Test(expected=NullPointerException.class)
public void testNotQueryBuilderNull() {
new NotQueryBuilder(null);
}
/**

View File

@ -44,6 +44,9 @@ public class OrQueryBuilderTest extends BaseQueryTestCase<OrQueryBuilder> {
query.add(innerQuery, Occur.SHOULD);
}
}
if (query.clauses().isEmpty()) {
return null;
}
return query;
}
@ -94,4 +97,14 @@ public class OrQueryBuilderTest extends BaseQueryTestCase<OrQueryBuilder> {
}
assertValidate(orQuery, totalExpectedErrors);
}
@Test(expected=NullPointerException.class)
public void testNullConstructor() {
new OrQueryBuilder(EmptyQueryBuilder.PROTOTYPE, null);
}
@Test(expected=NullPointerException.class)
public void testAddNull() {
new OrQueryBuilder().add(null);
}
}

View File

@ -21,8 +21,6 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.junit.Test;
import java.io.IOException;
@ -32,11 +30,12 @@ public class QueryFilterBuilderTest extends BaseQueryTestCase<QueryFilterBuilder
@Override
protected Query doCreateExpectedQuery(QueryFilterBuilder queryBuilder, QueryParseContext context) throws QueryParsingException, IOException {
return new ConstantScoreQuery(queryBuilder.innerQuery().toQuery(context));
Query query = queryBuilder.innerQuery().toQuery(context);
return query != null ? new ConstantScoreQuery(query) : query;
}
/**
* @return a AndQueryBuilder with random limit between 0 and 20
* @return a QueryFilterBuilder with random inner query
*/
@Override
protected QueryFilterBuilder doCreateTestQueryBuilder() {
@ -44,32 +43,9 @@ public class QueryFilterBuilderTest extends BaseQueryTestCase<QueryFilterBuilder
return new QueryFilterBuilder(innerQuery);
}
/**
* test corner case where no inner query exist
*/
@Test
public void testNoInnerQuery() throws QueryParsingException, IOException {
QueryFilterBuilder queryFilterQuery = new QueryFilterBuilder(null);
assertNull(queryFilterQuery.toQuery(createContext()));
}
/**
* test wrapping an inner filter that returns null also returns <tt>null</null> to pass on upwards
*/
@Test
public void testInnerQueryReturnsNull() throws IOException {
QueryParseContext context = createContext();
// create inner filter
String queryString = "{ \"constant_score\" : { \"filter\" : {} }";
XContentParser parser = XContentFactory.xContent(queryString).createParser(queryString);
context.reset(parser);
assertQueryHeader(parser, ConstantScoreQueryBuilder.PROTOTYPE.getName());
QueryBuilder innerQuery = context.indexQueryParserService().queryParser(ConstantScoreQueryBuilder.PROTOTYPE.getName()).fromXContent(context);
// check that when wrapping this filter, toQuery() returns null
QueryFilterBuilder queryFilterQuery = new QueryFilterBuilder(innerQuery);
assertNull(queryFilterQuery.toQuery(createContext()));
@Test(expected=NullPointerException.class)
public void testQueryFilterBuilderNull() {
new QueryFilterBuilder(null);
}
@Test

View File

@ -36,13 +36,15 @@ public class RandomQueryBuilder {
* @return a random {@link QueryBuilder}
*/
public static QueryBuilder createQuery(Random r) {
switch (RandomInts.randomIntBetween(r, 0, 2)) {
switch (RandomInts.randomIntBetween(r, 0, 3)) {
case 0:
return new MatchAllQueryBuilderTest().createTestQueryBuilder();
case 1:
return new TermQueryBuilderTest().createTestQueryBuilder();
case 2:
return new IdsQueryBuilderTest().createTestQueryBuilder();
case 3:
return EmptyQueryBuilder.PROTOTYPE;
default:
throw new UnsupportedOperationException();
}
@ -59,7 +61,7 @@ public class RandomQueryBuilder {
case 0:
return new TermQueryBuilder("", "test");
case 1:
return new BoostingQueryBuilder().negativeBoost(-1f);
return new BoostingQueryBuilder(new MatchAllQueryBuilder(), new MatchAllQueryBuilder()).negativeBoost(-1f);
case 2:
return new CommonTermsQueryBuilder("", "text");
case 3:

View File

@ -964,7 +964,7 @@ public class SimpleIndexQueryParserTests extends ElasticsearchSingleNodeTest {
@Test
public void testBoostingQueryBuilder() throws IOException {
IndexQueryParserService queryParser = queryParser();
Query parsedQuery = queryParser.parse(boostingQuery().positive(termQuery("field1", "value1")).negative(termQuery("field1", "value2")).negativeBoost(0.2f)).query();
Query parsedQuery = queryParser.parse(boostingQuery(termQuery("field1", "value1"), termQuery("field1", "value2")).negativeBoost(0.2f)).query();
assertThat(parsedQuery, instanceOf(BoostingQuery.class));
}

View File

@ -50,7 +50,6 @@ import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.search.builder.SearchSourceBuilder.highlight;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.elasticsearch.test.hamcrest.RegexMatcher.matches;
import static org.hamcrest.Matchers.*;
@ -1368,7 +1367,7 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
logger.info("--> highlighting and searching on field1");
SearchSourceBuilder source = searchSource()
.query(boostingQuery().positive(termQuery("field2", "brown")).negative(termQuery("field2", "foobar")).negativeBoost(0.5f))
.query(boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f))
.highlight(highlight().field("field2").order("score").preTags("<x>").postTags("</x>"));
SearchResponse searchResponse = client().prepareSearch("test").setSource(source.buildAsBytes()).get();
@ -1386,7 +1385,7 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
logger.info("--> highlighting and searching on field1");
SearchSourceBuilder source = searchSource()
.query(boostingQuery().positive(termQuery("field2", "brown")).negative(termQuery("field2", "foobar")).negativeBoost(0.5f))
.query(boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f))
.highlight(highlight().field("field2").order("score").preTags("<x>").postTags("</x>"));
SearchResponse searchResponse = client().prepareSearch("test").setSource(source.buildAsBytes()).get();
@ -2300,7 +2299,7 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
logger.info("--> highlighting and searching on field1");
SearchSourceBuilder source = searchSource()
.query(boostingQuery().positive(termQuery("field2", "brown")).negative(termQuery("field2", "foobar")).negativeBoost(0.5f))
.query(boostingQuery(termQuery("field2", "brown"), termQuery("field2", "foobar")).negativeBoost(0.5f))
.highlight(highlight().field("field2").preTags("<x>").postTags("</x>"));
SearchResponse searchResponse = client().search(searchRequest("test").source(source)).actionGet();
@ -2603,10 +2602,10 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher);
phrase.boost(1);
// Try with a boosting query
response = search.setQuery(boostingQuery().positive(phrase).negative(terms).boost(boost).negativeBoost(1)).get();
response = search.setQuery(boostingQuery(phrase, terms).boost(boost).negativeBoost(1)).get();
assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher);
// Try with a boosting query using a negative boost
response = search.setQuery(boostingQuery().positive(phrase).negative(terms).boost(1).negativeBoost(1/boost)).get();
response = search.setQuery(boostingQuery(phrase, terms).boost(1).negativeBoost(1/boost)).get();
assertHighlight(response, 0, "field1", 0, 1, highlightedMatcher);
}
}

View File

@ -6,6 +6,11 @@ on the query-refactoring feature branch.
=== Java-API
==== BoostingQueryBuilder
Removed setters for mandatory positive/negative query. Both arguments now have
to be supplied at construction time already and have to be non-null.
==== QueryFilterBuilder
Removed the setter `queryName(String queryName)` since this field is not supported