Query Refactoring: Make sure that parsing nested queries always returns an query builder
Currently parsing inner queries can return `null` which leads to unnecessary complicated null checks further up the query hierarchy. By introducing a special EmptyQueryBuilder that can be used to signal that this query clause should be ignored upstream where possible, we can avoid additional null checks in parent query builders and still allow for this query to be ignored when creating the lucene query later. This new builder returns `null` when calling its `toQuery()` method, so at this point we still need to handle it, but having an explicit object for the intermediate query representation makes it easier to differentiate between cases where inner query clauses are empty (legal) or are not set at all (illegal). Also added check for empty inner builder list to BoolQueryBuilder and OrQueryBuilder. Removed setters for positive/negatice query in favour of constructor with mandatory non-null arguments in BoostingQueryBuilder. Closes #11915
This commit is contained in:
parent
63530631f9
commit
c51cfbb6d0
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -54,9 +54,7 @@ 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);
|
||||
}
|
||||
queries.add(filter);
|
||||
}
|
||||
} else {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
|
@ -69,17 +67,13 @@ 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);
|
||||
}
|
||||
queries.add(filter);
|
||||
}
|
||||
} else {
|
||||
queriesFound = true;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
QueryBuilder filter = parseContext.parseInnerFilterToQueryBuilder();
|
||||
if (filter != null) {
|
||||
queries.add(filter);
|
||||
}
|
||||
queries.add(filter);
|
||||
}
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -71,14 +71,13 @@ public class BoolQueryParser extends BaseQueryParser {
|
|||
switch (currentFieldName) {
|
||||
case "must":
|
||||
query = parseContext.parseInnerQueryBuilder();
|
||||
if (query != null) {
|
||||
mustClauses.add(query);
|
||||
}
|
||||
mustClauses.add(query);
|
||||
break;
|
||||
case "should":
|
||||
query = parseContext.parseInnerQueryBuilder();
|
||||
if (query != null) {
|
||||
shouldClauses.add(query);
|
||||
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);
|
||||
}
|
||||
filterClauses.add(query);
|
||||
break;
|
||||
case "must_not":
|
||||
case "mustNot":
|
||||
query = parseContext.parseInnerFilterToQueryBuilder();
|
||||
if (query != null) {
|
||||
mustNotClauses.add(query);
|
||||
}
|
||||
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);
|
||||
}
|
||||
mustClauses.add(query);
|
||||
break;
|
||||
case "should":
|
||||
query = parseContext.parseInnerQueryBuilder();
|
||||
if (query != null) {
|
||||
shouldClauses.add(query);
|
||||
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);
|
||||
}
|
||||
filterClauses.add(query);
|
||||
break;
|
||||
case "must_not":
|
||||
case "mustNot":
|
||||
query = parseContext.parseInnerFilterToQueryBuilder();
|
||||
if (query != null) {
|
||||
mustNotClauses.add(query);
|
||||
}
|
||||
mustNotClauses.add(query);
|
||||
break;
|
||||
default:
|
||||
throw new QueryParsingException(parseContext, "bool query does not support [" + currentFieldName + "]");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
queries.add(query);
|
||||
token = parser.nextToken();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -234,14 +234,22 @@ public abstract class BaseQueryTestCase<QB extends AbstractQueryBuilder<QB>> ext
|
|||
|
||||
Query expectedQuery = createExpectedQuery(testQuery, context);
|
||||
Query actualQuery = testQuery.toQuery(context);
|
||||
assertThat(actualQuery, instanceOf(expectedQuery.getClass()));
|
||||
assertThat(actualQuery, equalTo(expectedQuery));
|
||||
assertLuceneQuery(testQuery, actualQuery, 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);
|
||||
expectedQuery.setBoost(queryBuilder.boost());
|
||||
if (expectedQuery != null) {
|
||||
expectedQuery.setBoost(queryBuilder.boost());
|
||||
}
|
||||
return expectedQuery;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
if (positive == null || negative == null) {
|
||||
return null;
|
||||
}
|
||||
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 {
|
||||
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()));
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue