Merge pull request #11121 from cbuescher/feature/query-refactoring-boolquery
Query refactoring: BoolQueryBuilder and Parser
This commit is contained in:
commit
4f4b1b74df
|
@ -509,6 +509,20 @@ public abstract class StreamInput extends InputStream {
|
|||
return namedWriteable.readFrom(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a list of {@link NamedWriteable} from the current stream, by first reading its size and then
|
||||
* reading the individual objects using {@link #readNamedWriteable()}.
|
||||
*/
|
||||
public <C> List<C> readNamedWritableList() throws IOException {
|
||||
List<C> list = new ArrayList<>();
|
||||
int size = readInt();
|
||||
for (int i = 0; i < size; i++) {
|
||||
C obj = readNamedWriteable();
|
||||
list.add(obj);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static StreamInput wrap(BytesReference reference) {
|
||||
if (reference.hasArray() == false) {
|
||||
reference = reference.toBytesArray();
|
||||
|
|
|
@ -457,4 +457,15 @@ public abstract class StreamOutput extends OutputStream {
|
|||
writeString(namedWriteable.getName());
|
||||
namedWriteable.writeTo(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a list of {@link NamedWriteable} to the current stream, by first writing its size and then iterating over the objects
|
||||
* in the list
|
||||
*/
|
||||
public void writeNamedWritableList(List<? extends NamedWriteable> list) throws IOException {
|
||||
writeInt(list.size());
|
||||
for (NamedWriteable obj : list) {
|
||||
writeNamedWriteable(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,20 +19,36 @@
|
|||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
|
||||
|
||||
/**
|
||||
* A Query that matches documents matching boolean combinations of other queries.
|
||||
*/
|
||||
public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuilder<BoolQueryBuilder> {
|
||||
public class BoolQueryBuilder extends QueryBuilder<BoolQueryBuilder> implements BoostableQueryBuilder<BoolQueryBuilder> {
|
||||
|
||||
public static final String NAME = "bool";
|
||||
|
||||
static final boolean ADJUST_PURE_NEGATIVE_DEFAULT = true;
|
||||
|
||||
static final boolean DISABLE_COORD_DEFAULT = false;
|
||||
|
||||
static final BoolQueryBuilder PROTOTYPE = new BoolQueryBuilder();
|
||||
|
||||
private final List<QueryBuilder> mustClauses = new ArrayList<>();
|
||||
|
||||
private final List<QueryBuilder> mustNotClauses = new ArrayList<>();
|
||||
|
@ -41,18 +57,16 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
|
||||
private final List<QueryBuilder> shouldClauses = new ArrayList<>();
|
||||
|
||||
private float boost = -1;
|
||||
private float boost = 1.0f;
|
||||
|
||||
private Boolean disableCoord;
|
||||
private boolean disableCoord = DISABLE_COORD_DEFAULT;
|
||||
|
||||
private boolean adjustPureNegative = ADJUST_PURE_NEGATIVE_DEFAULT;
|
||||
|
||||
private String minimumShouldMatch;
|
||||
|
||||
private Boolean adjustPureNegative;
|
||||
|
||||
private String queryName;
|
||||
|
||||
static final BoolQueryBuilder PROTOTYPE = new BoolQueryBuilder();
|
||||
|
||||
/**
|
||||
* Adds a query that <b>must</b> appear in the matching documents and will
|
||||
* contribute to scoring.
|
||||
|
@ -62,6 +76,22 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of queries that <b>must</b> appear in the matching documents and will
|
||||
* contribute to scoring.
|
||||
*/
|
||||
public BoolQueryBuilder must(List<QueryBuilder> queryBuilders) {
|
||||
mustClauses.addAll(queryBuilders);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the queries that <b>must</b> appear in the matching documents.
|
||||
*/
|
||||
public List<QueryBuilder> must() {
|
||||
return this.mustClauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a query that <b>must</b> appear in the matching documents but will
|
||||
* not contribute to scoring.
|
||||
|
@ -72,8 +102,23 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a query that <b>must not</b> appear in the matching documents and
|
||||
* will not contribute to scoring.
|
||||
* Adds a list of queries that <b>must</b> appear in the matching documents but will
|
||||
* not contribute to scoring.
|
||||
*/
|
||||
public BoolQueryBuilder filter(List<QueryBuilder> queryBuilders) {
|
||||
filterClauses.addAll(queryBuilders);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the queries that <b>must</b> appear in the matching documents but don't conntribute to scoring
|
||||
*/
|
||||
public List<QueryBuilder> filter() {
|
||||
return this.filterClauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a query that <b>must not</b> appear in the matching documents.
|
||||
*/
|
||||
public BoolQueryBuilder mustNot(QueryBuilder queryBuilder) {
|
||||
mustNotClauses.add(queryBuilder);
|
||||
|
@ -81,7 +126,22 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a query that <i>should</i> appear in the matching documents. For a boolean query with no
|
||||
* Adds a list of queries that <b>must not</b> appear in the matching documents.
|
||||
*/
|
||||
public BoolQueryBuilder mustNot(List<QueryBuilder> queryBuilders) {
|
||||
mustNotClauses.addAll(queryBuilders);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the queries that <b>must not</b> appear in the matching documents.
|
||||
*/
|
||||
public List<QueryBuilder> mustNot() {
|
||||
return this.mustNotClauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a clause that <i>should</i> be matched by the returned documents. For a boolean query with no
|
||||
* <tt>MUST</tt> clauses one or more <code>SHOULD</code> clauses must match a document
|
||||
* for the BooleanQuery to match.
|
||||
*
|
||||
|
@ -92,6 +152,28 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of clauses that <i>should</i> be matched by the returned documents. For a boolean query with no
|
||||
* <tt>MUST</tt> clauses one or more <code>SHOULD</code> clauses must match a document
|
||||
* for the BooleanQuery to match.
|
||||
*
|
||||
* @see #minimumNumberShouldMatch(int)
|
||||
*/
|
||||
public BoolQueryBuilder should(List<QueryBuilder> queryBuilders) {
|
||||
shouldClauses.addAll(queryBuilders);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of clauses that <b>should</b> be matched by the returned documents.
|
||||
*
|
||||
* @see #should(QueryBuilder)
|
||||
* @see #minimumNumberShouldMatch(int)
|
||||
*/
|
||||
public List<QueryBuilder> should() {
|
||||
return this.shouldClauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the boost for this query. Documents matching this query will (in addition to the normal
|
||||
* weightings) have their score multiplied by the boost provided.
|
||||
|
@ -102,6 +184,13 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the boost for this query.
|
||||
*/
|
||||
public float boost() {
|
||||
return this.boost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables <tt>Similarity#coord(int,int)</tt> in scoring. Defaults to <tt>false</tt>.
|
||||
*/
|
||||
|
@ -110,6 +199,13 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the <tt>Similarity#coord(int,int)</tt> in scoring are disabled. Defaults to <tt>false</tt>.
|
||||
*/
|
||||
public boolean disableCoord() {
|
||||
return this.disableCoord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies a minimum number of the optional (should) boolean clauses which must be satisfied.
|
||||
* <p/>
|
||||
|
@ -128,6 +224,23 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies a minimum number of the optional (should) boolean clauses which must be satisfied.
|
||||
* @see BoolQueryBuilder#minimumNumberShouldMatch(int)
|
||||
*/
|
||||
public BoolQueryBuilder minimumNumberShouldMatch(String minimumNumberShouldMatch) {
|
||||
this.minimumShouldMatch = minimumNumberShouldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the string representation of the minimumShouldMatch settings for this query
|
||||
*/
|
||||
public String minimumNumberShouldMatch() {
|
||||
return this.minimumShouldMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum should match using the special syntax (for example, supporting percentage).
|
||||
*/
|
||||
|
@ -154,6 +267,13 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the setting for the adjust_pure_negative setting in this query
|
||||
*/
|
||||
public boolean adjustPureNegative() {
|
||||
return this.adjustPureNegative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query name for the filter that can be used when searching for matched_filters per hit.
|
||||
*/
|
||||
|
@ -162,6 +282,13 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query name for the filter that can be used when searching for matched_filters per hit.
|
||||
*/
|
||||
public String queryName() {
|
||||
return this.queryName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(NAME);
|
||||
|
@ -169,25 +296,19 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
doXArrayContent("filter", filterClauses, builder, params);
|
||||
doXArrayContent("must_not", mustNotClauses, builder, params);
|
||||
doXArrayContent("should", shouldClauses, builder, params);
|
||||
if (boost != -1) {
|
||||
builder.field("boost", boost);
|
||||
}
|
||||
if (disableCoord != null) {
|
||||
builder.field("disable_coord", disableCoord);
|
||||
}
|
||||
builder.field("boost", boost);
|
||||
builder.field("disable_coord", disableCoord);
|
||||
builder.field("adjust_pure_negative", adjustPureNegative);
|
||||
if (minimumShouldMatch != null) {
|
||||
builder.field("minimum_should_match", minimumShouldMatch);
|
||||
}
|
||||
if (adjustPureNegative != null) {
|
||||
builder.field("adjust_pure_negative", adjustPureNegative);
|
||||
}
|
||||
if (queryName != null) {
|
||||
builder.field("_name", queryName);
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
private void doXArrayContent(String field, List<QueryBuilder> clauses, XContentBuilder builder, Params params) throws IOException {
|
||||
private static void doXArrayContent(String field, List<QueryBuilder> clauses, XContentBuilder builder, Params params) throws IOException {
|
||||
if (clauses.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
@ -207,4 +328,100 @@ public class BoolQueryBuilder extends QueryBuilder implements BoostableQueryBuil
|
|||
public String queryId() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query toQuery(QueryParseContext parseContext) throws QueryParsingException, IOException {
|
||||
if (!hasClauses()) {
|
||||
return new MatchAllDocsQuery();
|
||||
}
|
||||
|
||||
BooleanQuery booleanQuery = new BooleanQuery(this.disableCoord);
|
||||
addBooleanClauses(parseContext, booleanQuery, this.mustClauses, BooleanClause.Occur.MUST);
|
||||
addBooleanClauses(parseContext, booleanQuery, this.mustNotClauses, BooleanClause.Occur.MUST_NOT);
|
||||
addBooleanClauses(parseContext, booleanQuery, this.shouldClauses, BooleanClause.Occur.SHOULD);
|
||||
addBooleanClauses(parseContext, booleanQuery, this.filterClauses, BooleanClause.Occur.FILTER);
|
||||
|
||||
booleanQuery.setBoost(this.boost);
|
||||
Queries.applyMinimumShouldMatch(booleanQuery, this.minimumShouldMatch);
|
||||
Query query = this.adjustPureNegative ? fixNegativeQueryIfNeeded(booleanQuery) : booleanQuery;
|
||||
if (this.queryName != null) {
|
||||
parseContext.addNamedQuery(this.queryName, query);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryValidationException validate() {
|
||||
// nothing to validate, clauses are optional, see hasClauses(), other parameters have defaults
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void addBooleanClauses(QueryParseContext parseContext, BooleanQuery booleanQuery, List<QueryBuilder> clauses, Occur occurs)
|
||||
throws IOException {
|
||||
for (QueryBuilder query : clauses) {
|
||||
Query luceneQuery = query.toQuery(parseContext);
|
||||
if (luceneQuery != null) {
|
||||
booleanQuery.add(new BooleanClause(luceneQuery, occurs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(boost, adjustPureNegative, disableCoord,
|
||||
minimumShouldMatch, queryName, mustClauses, shouldClauses, mustNotClauses, filterClauses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
BoolQueryBuilder other = (BoolQueryBuilder) obj;
|
||||
return Objects.equals(boost, other.boost) &&
|
||||
Objects.equals(adjustPureNegative, other.adjustPureNegative) &&
|
||||
Objects.equals(disableCoord, other.disableCoord) &&
|
||||
Objects.equals(minimumShouldMatch, other.minimumShouldMatch) &&
|
||||
Objects.equals(queryName, other.queryName) &&
|
||||
Objects.equals(mustClauses, other.mustClauses) &&
|
||||
Objects.equals(shouldClauses, other.shouldClauses) &&
|
||||
Objects.equals(mustNotClauses, other.mustNotClauses) &&
|
||||
Objects.equals(filterClauses, other.filterClauses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoolQueryBuilder readFrom(StreamInput in) throws IOException {
|
||||
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
|
||||
List<QueryBuilder> queryBuilders = in.readNamedWritableList();
|
||||
boolQueryBuilder.must(queryBuilders);
|
||||
queryBuilders = in.readNamedWritableList();
|
||||
boolQueryBuilder.mustNot(queryBuilders);
|
||||
queryBuilders = in.readNamedWritableList();
|
||||
boolQueryBuilder.should(queryBuilders);
|
||||
queryBuilders = in.readNamedWritableList();
|
||||
boolQueryBuilder.filter(queryBuilders);
|
||||
boolQueryBuilder.boost = in.readFloat();
|
||||
boolQueryBuilder.adjustPureNegative = in.readBoolean();
|
||||
boolQueryBuilder.disableCoord = in.readBoolean();
|
||||
boolQueryBuilder.queryName = in.readOptionalString();
|
||||
boolQueryBuilder.minimumShouldMatch = in.readOptionalString();
|
||||
return boolQueryBuilder;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeNamedWritableList(this.mustClauses);
|
||||
out.writeNamedWritableList(this.mustNotClauses);
|
||||
out.writeNamedWritableList(this.shouldClauses);
|
||||
out.writeNamedWritableList(this.filterClauses);
|
||||
out.writeFloat(this.boost);
|
||||
out.writeBoolean(this.adjustPureNegative);
|
||||
out.writeBoolean(this.disableCoord);
|
||||
out.writeOptionalString(queryName);
|
||||
out.writeOptionalString(this.minimumShouldMatch);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,8 @@
|
|||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
|
@ -32,12 +28,11 @@ import java.io.IOException;
|
|||
import java.util.List;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
|
||||
|
||||
/**
|
||||
*
|
||||
* Parser for the {@link BoolQueryBuilder}
|
||||
*/
|
||||
public class BoolQueryParser extends BaseQueryParserTemp {
|
||||
public class BoolQueryParser extends BaseQueryParser {
|
||||
|
||||
@Inject
|
||||
public BoolQueryParser(Settings settings) {
|
||||
|
@ -50,19 +45,23 @@ public class BoolQueryParser extends BaseQueryParserTemp {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Query parse(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
public QueryBuilder fromXContent(QueryParseContext parseContext) throws IOException, QueryParsingException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
|
||||
boolean disableCoord = false;
|
||||
boolean disableCoord = BoolQueryBuilder.DISABLE_COORD_DEFAULT;
|
||||
boolean adjustPureNegative = BoolQueryBuilder.ADJUST_PURE_NEGATIVE_DEFAULT;
|
||||
float boost = 1.0f;
|
||||
String minimumShouldMatch = null;
|
||||
|
||||
List<BooleanClause> clauses = newArrayList();
|
||||
boolean adjustPureNegative = true;
|
||||
List<QueryBuilder> mustClauses = newArrayList();
|
||||
List<QueryBuilder> mustNotClauses = newArrayList();
|
||||
List<QueryBuilder> shouldClauses = newArrayList();
|
||||
List<QueryBuilder> filterClauses = newArrayList();
|
||||
String queryName = null;
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
QueryBuilder query;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
currentFieldName = parser.currentName();
|
||||
|
@ -71,31 +70,31 @@ public class BoolQueryParser extends BaseQueryParserTemp {
|
|||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
switch (currentFieldName) {
|
||||
case "must":
|
||||
Query query = parseContext.parseInnerQuery();
|
||||
query = parseContext.parseInnerQueryBuilder();
|
||||
if (query != null) {
|
||||
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST));
|
||||
mustClauses.add(query);
|
||||
}
|
||||
break;
|
||||
case "should":
|
||||
query = parseContext.parseInnerQuery();
|
||||
query = parseContext.parseInnerQueryBuilder();
|
||||
if (query != null) {
|
||||
clauses.add(new BooleanClause(query, BooleanClause.Occur.SHOULD));
|
||||
shouldClauses.add(query);
|
||||
if (parseContext.isFilter() && minimumShouldMatch == null) {
|
||||
minimumShouldMatch = "1";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "filter":
|
||||
query = parseContext.parseInnerFilter();
|
||||
query = parseContext.parseInnerFilterToQueryBuilder();
|
||||
if (query != null) {
|
||||
clauses.add(new BooleanClause(query, BooleanClause.Occur.FILTER));
|
||||
filterClauses.add(query);
|
||||
}
|
||||
break;
|
||||
case "must_not":
|
||||
case "mustNot":
|
||||
query = parseContext.parseInnerFilter();
|
||||
query = parseContext.parseInnerFilterToQueryBuilder();
|
||||
if (query != null) {
|
||||
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST_NOT));
|
||||
mustNotClauses.add(query);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -105,31 +104,31 @@ public class BoolQueryParser extends BaseQueryParserTemp {
|
|||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
switch (currentFieldName) {
|
||||
case "must":
|
||||
Query query = parseContext.parseInnerQuery();
|
||||
query = parseContext.parseInnerQueryBuilder();
|
||||
if (query != null) {
|
||||
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST));
|
||||
mustClauses.add(query);
|
||||
}
|
||||
break;
|
||||
case "should":
|
||||
query = parseContext.parseInnerQuery();
|
||||
query = parseContext.parseInnerQueryBuilder();
|
||||
if (query != null) {
|
||||
clauses.add(new BooleanClause(query, BooleanClause.Occur.SHOULD));
|
||||
shouldClauses.add(query);
|
||||
if (parseContext.isFilter() && minimumShouldMatch == null) {
|
||||
minimumShouldMatch = "1";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "filter":
|
||||
query = parseContext.parseInnerFilter();
|
||||
query = parseContext.parseInnerFilterToQueryBuilder();
|
||||
if (query != null) {
|
||||
clauses.add(new BooleanClause(query, BooleanClause.Occur.FILTER));
|
||||
filterClauses.add(query);
|
||||
}
|
||||
break;
|
||||
case "must_not":
|
||||
case "mustNot":
|
||||
query = parseContext.parseInnerFilter();
|
||||
query = parseContext.parseInnerFilterToQueryBuilder();
|
||||
if (query != null) {
|
||||
clauses.add(new BooleanClause(query, BooleanClause.Occur.MUST_NOT));
|
||||
mustNotClauses.add(query);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -154,22 +153,18 @@ public class BoolQueryParser extends BaseQueryParserTemp {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clauses.isEmpty()) {
|
||||
return new MatchAllDocsQuery();
|
||||
}
|
||||
|
||||
BooleanQuery booleanQuery = new BooleanQuery(disableCoord);
|
||||
for (BooleanClause clause : clauses) {
|
||||
booleanQuery.add(clause);
|
||||
}
|
||||
booleanQuery.setBoost(boost);
|
||||
Queries.applyMinimumShouldMatch(booleanQuery, minimumShouldMatch);
|
||||
Query query = adjustPureNegative ? fixNegativeQueryIfNeeded(booleanQuery) : booleanQuery;
|
||||
if (queryName != null) {
|
||||
parseContext.addNamedQuery(queryName, query);
|
||||
}
|
||||
return query;
|
||||
BoolQueryBuilder boolQuery = new BoolQueryBuilder();
|
||||
boolQuery.must(mustClauses);
|
||||
boolQuery.mustNot(mustNotClauses);
|
||||
boolQuery.should(shouldClauses);
|
||||
boolQuery.filter(filterClauses);
|
||||
boolQuery.boost(boost);
|
||||
boolQuery.disableCoord(disableCoord);
|
||||
boolQuery.adjustPureNegative(adjustPureNegative);
|
||||
boolQuery.minimumNumberShouldMatch(minimumShouldMatch);
|
||||
boolQuery.queryName(queryName);
|
||||
boolQuery.validate();
|
||||
return boolQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -267,18 +267,37 @@ public class QueryParseContext {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated replaced by calls to parseInnerFilterToQueryBuilder() for the resulting queries
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public Query parseInnerFilter() throws QueryParsingException, IOException {
|
||||
QueryBuilder builder = parseInnerFilterToQueryBuilder();
|
||||
Query result = null;
|
||||
if (builder != null) {
|
||||
result = builder.toQuery(this);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @throws QueryParsingException
|
||||
* @throws IOException
|
||||
*/
|
||||
@Nullable
|
||||
public QueryBuilder parseInnerFilterToQueryBuilder() throws QueryParsingException, IOException {
|
||||
final boolean originalIsFilter = isFilter;
|
||||
try {
|
||||
isFilter = true;
|
||||
return parseInnerQuery();
|
||||
return parseInnerQueryBuilder();
|
||||
} finally {
|
||||
isFilter = originalIsFilter;
|
||||
}
|
||||
}
|
||||
|
||||
public Query parseInnerFilter(String queryName) throws IOException, QueryParsingException {
|
||||
public QueryBuilder parseInnerFilterToQueryBuilder(String queryName) throws IOException, QueryParsingException {
|
||||
final boolean originalIsFilter = isFilter;
|
||||
try {
|
||||
isFilter = true;
|
||||
|
@ -286,12 +305,22 @@ public class QueryParseContext {
|
|||
if (queryParser == null) {
|
||||
throw new QueryParsingException(this, "No query registered for [" + queryName + "]");
|
||||
}
|
||||
return queryParser.parse(this);
|
||||
return queryParser.fromXContent(this);
|
||||
} finally {
|
||||
isFilter = originalIsFilter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated replaced by calls to parseInnerFilterToQueryBuilder(String queryName) for the resulting queries
|
||||
*/
|
||||
@Nullable
|
||||
@Deprecated
|
||||
public Query parseInnerFilter(String queryName) throws IOException, QueryParsingException {
|
||||
QueryBuilder builder = parseInnerFilterToQueryBuilder(queryName);
|
||||
return (builder != null) ? builder.toQuery(this) : null;
|
||||
}
|
||||
|
||||
public Collection<String> simpleMatchToIndexNames(String pattern) {
|
||||
return indexQueryParser.mapperService.simpleMatchToIndexNames(pattern, getTypes());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import org.apache.lucene.search.BooleanClause;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.BooleanClause.Occur;
|
||||
import org.elasticsearch.common.lucene.search.Queries;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded;
|
||||
|
||||
public class BoolQueryBuilderTest extends BaseQueryTestCase<BoolQueryBuilder> {
|
||||
|
||||
@Override
|
||||
protected void assertLuceneQuery(BoolQueryBuilder queryBuilder, Query query, QueryParseContext context) {
|
||||
if (queryBuilder.queryName() != null) {
|
||||
Query namedQuery = context.copyNamedFilters().get(queryBuilder.queryName());
|
||||
if (queryBuilder.hasClauses()) {
|
||||
assertThat(namedQuery, equalTo(query));
|
||||
} else {
|
||||
assertNull(namedQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BoolQueryBuilder createTestQueryBuilder() {
|
||||
BoolQueryBuilder query = new BoolQueryBuilder();
|
||||
if (randomBoolean()) {
|
||||
query.boost(2.0f / randomIntBetween(1, 20));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
query.adjustPureNegative(randomBoolean());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
query.disableCoord(randomBoolean());
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
query.minimumNumberShouldMatch(randomIntBetween(1, 10));
|
||||
}
|
||||
int mustClauses = randomIntBetween(0, 3);
|
||||
for (int i = 0; i < mustClauses; i++) {
|
||||
query.must(RandomQueryBuilder.create(random()));
|
||||
}
|
||||
int mustNotClauses = randomIntBetween(0, 3);
|
||||
for (int i = 0; i < mustNotClauses; i++) {
|
||||
query.mustNot(RandomQueryBuilder.create(random()));
|
||||
}
|
||||
int shouldClauses = randomIntBetween(0, 3);
|
||||
for (int i = 0; i < shouldClauses; i++) {
|
||||
query.should(RandomQueryBuilder.create(random()));
|
||||
}
|
||||
int filterClauses = randomIntBetween(0, 3);
|
||||
for (int i = 0; i < filterClauses; i++) {
|
||||
query.filter(RandomQueryBuilder.create(random()));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
query.queryName(randomUnicodeOfLengthBetween(3, 15));
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BoolQueryBuilder createEmptyQueryBuilder() {
|
||||
return new BoolQueryBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query createExpectedQuery(BoolQueryBuilder queryBuilder, QueryParseContext context) throws IOException {
|
||||
if (!queryBuilder.hasClauses()) {
|
||||
return new MatchAllDocsQuery();
|
||||
}
|
||||
|
||||
BooleanQuery boolQuery = new BooleanQuery(queryBuilder.disableCoord());
|
||||
boolQuery.setBoost(queryBuilder.boost());
|
||||
addBooleanClauses(context, boolQuery, queryBuilder.must(), BooleanClause.Occur.MUST);
|
||||
addBooleanClauses(context, boolQuery, queryBuilder.mustNot(), BooleanClause.Occur.MUST_NOT);
|
||||
addBooleanClauses(context, boolQuery, queryBuilder.should(), BooleanClause.Occur.SHOULD);
|
||||
addBooleanClauses(context, boolQuery, queryBuilder.filter(), BooleanClause.Occur.FILTER);
|
||||
|
||||
Queries.applyMinimumShouldMatch(boolQuery, queryBuilder.minimumNumberShouldMatch());
|
||||
Query returnedQuery = queryBuilder.adjustPureNegative() ? fixNegativeQueryIfNeeded(boolQuery) : boolQuery;
|
||||
return returnedQuery;
|
||||
}
|
||||
|
||||
private static void addBooleanClauses(QueryParseContext parseContext, BooleanQuery booleanQuery, List<QueryBuilder> clauses, Occur occurs)
|
||||
throws IOException {
|
||||
for (QueryBuilder query : clauses) {
|
||||
booleanQuery.add(new BooleanClause(query.toQuery(parseContext), occurs));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.generators.RandomInts;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Utility class for creating random QueryBuilders.
|
||||
* So far only leaf queries like {@link MatchAllQueryBuilder}, {@link TermQueryBuilder} or
|
||||
* {@link IdsQueryBuilder} are returned.
|
||||
*/
|
||||
public class RandomQueryBuilder {
|
||||
|
||||
/**
|
||||
* @param r random seed
|
||||
* @return a random {@link QueryBuilder}
|
||||
*/
|
||||
public static QueryBuilder create(Random r) {
|
||||
QueryBuilder query = null;
|
||||
switch (RandomInts.randomIntBetween(r, 0, 2)) {
|
||||
case 0:
|
||||
return new MatchAllQueryBuilderTest().createTestQueryBuilder();
|
||||
case 1:
|
||||
return new TermQueryBuilderTest().createTestQueryBuilder();
|
||||
case 2:
|
||||
return new IdsQueryBuilderTest().createTestQueryBuilder();
|
||||
}
|
||||
return query;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue