Start to rework query registration
Changes QueryParser into a @FunctionalInterface and provides a way to register queries using that. Cuts match and function_score queries over to that registration method as a proof of concept. Once all queries have been cut over we can remove their PROTOTYPES.
This commit is contained in:
parent
f355bb2a9b
commit
75a9899813
|
@ -23,13 +23,17 @@ import org.apache.lucene.queries.ExtendedCommonTermsQuery;
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.FuzzyQuery;
|
import org.apache.lucene.search.FuzzyQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
|
import org.elasticsearch.common.ParsingException;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.lucene.search.Queries;
|
import org.elasticsearch.common.lucene.search.Queries;
|
||||||
import org.elasticsearch.common.unit.Fuzziness;
|
import org.elasticsearch.common.unit.Fuzziness;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.query.support.QueryParsers;
|
import org.elasticsearch.index.query.support.QueryParsers;
|
||||||
import org.elasticsearch.index.search.MatchQuery;
|
import org.elasticsearch.index.search.MatchQuery;
|
||||||
|
import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -40,6 +44,21 @@ import java.util.Objects;
|
||||||
* can construct different queries based on the type provided.
|
* can construct different queries based on the type provided.
|
||||||
*/
|
*/
|
||||||
public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
|
public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
|
||||||
|
public static final ParseField MATCH_PHRASE_FIELD = new ParseField("match_phrase", "text_phrase");
|
||||||
|
public static final ParseField MATCH_PHRASE_PREFIX_FIELD = new ParseField("match_phrase_prefix", "text_phrase_prefix");
|
||||||
|
public static final ParseField SLOP_FIELD = new ParseField("slop", "phrase_slop");
|
||||||
|
public static final ParseField ZERO_TERMS_QUERY_FIELD = new ParseField("zero_terms_query");
|
||||||
|
public static final ParseField CUTOFF_FREQUENCY_FIELD = new ParseField("cutoff_frequency");
|
||||||
|
public static final ParseField LENIENT_FIELD = new ParseField("lenient");
|
||||||
|
public static final ParseField FUZZY_TRANSPOSITIONS_FIELD = new ParseField("fuzzy_transpositions");
|
||||||
|
public static final ParseField FUZZY_REWRITE_FIELD = new ParseField("fuzzy_rewrite");
|
||||||
|
public static final ParseField MINIMUM_SHOULD_MATCH_FIELD = new ParseField("minimum_should_match");
|
||||||
|
public static final ParseField OPERATOR_FIELD = new ParseField("operator");
|
||||||
|
public static final ParseField MAX_EXPANSIONS_FIELD = new ParseField("max_expansions");
|
||||||
|
public static final ParseField PREFIX_LENGTH_FIELD = new ParseField("prefix_length");
|
||||||
|
public static final ParseField ANALYZER_FIELD = new ParseField("analyzer");
|
||||||
|
public static final ParseField TYPE_FIELD = new ParseField("type");
|
||||||
|
public static final ParseField QUERY_FIELD = new ParseField("query");
|
||||||
|
|
||||||
/** The default name for the match query */
|
/** The default name for the match query */
|
||||||
public static final String NAME = "match";
|
public static final String NAME = "match";
|
||||||
|
@ -80,7 +99,7 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
|
||||||
|
|
||||||
private Float cutoffFrequency = null;
|
private Float cutoffFrequency = null;
|
||||||
|
|
||||||
static final MatchQueryBuilder PROTOTYPE = new MatchQueryBuilder("","");
|
public static final MatchQueryBuilder PROTOTYPE = new MatchQueryBuilder("","");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new match query.
|
* Constructs a new match query.
|
||||||
|
@ -317,30 +336,30 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
|
||||||
builder.startObject(NAME);
|
builder.startObject(NAME);
|
||||||
builder.startObject(fieldName);
|
builder.startObject(fieldName);
|
||||||
|
|
||||||
builder.field(MatchQueryParser.QUERY_FIELD.getPreferredName(), value);
|
builder.field(QUERY_FIELD.getPreferredName(), value);
|
||||||
builder.field(MatchQueryParser.TYPE_FIELD.getPreferredName(), type.toString().toLowerCase(Locale.ENGLISH));
|
builder.field(TYPE_FIELD.getPreferredName(), type.toString().toLowerCase(Locale.ENGLISH));
|
||||||
builder.field(MatchQueryParser.OPERATOR_FIELD.getPreferredName(), operator.toString());
|
builder.field(OPERATOR_FIELD.getPreferredName(), operator.toString());
|
||||||
if (analyzer != null) {
|
if (analyzer != null) {
|
||||||
builder.field(MatchQueryParser.ANALYZER_FIELD.getPreferredName(), analyzer);
|
builder.field(ANALYZER_FIELD.getPreferredName(), analyzer);
|
||||||
}
|
}
|
||||||
builder.field(MatchQueryParser.SLOP_FIELD.getPreferredName(), slop);
|
builder.field(SLOP_FIELD.getPreferredName(), slop);
|
||||||
if (fuzziness != null) {
|
if (fuzziness != null) {
|
||||||
fuzziness.toXContent(builder, params);
|
fuzziness.toXContent(builder, params);
|
||||||
}
|
}
|
||||||
builder.field(MatchQueryParser.PREFIX_LENGTH_FIELD.getPreferredName(), prefixLength);
|
builder.field(PREFIX_LENGTH_FIELD.getPreferredName(), prefixLength);
|
||||||
builder.field(MatchQueryParser.MAX_EXPANSIONS_FIELD.getPreferredName(), maxExpansions);
|
builder.field(MAX_EXPANSIONS_FIELD.getPreferredName(), maxExpansions);
|
||||||
if (minimumShouldMatch != null) {
|
if (minimumShouldMatch != null) {
|
||||||
builder.field(MatchQueryParser.MINIMUM_SHOULD_MATCH_FIELD.getPreferredName(), minimumShouldMatch);
|
builder.field(MINIMUM_SHOULD_MATCH_FIELD.getPreferredName(), minimumShouldMatch);
|
||||||
}
|
}
|
||||||
if (fuzzyRewrite != null) {
|
if (fuzzyRewrite != null) {
|
||||||
builder.field(MatchQueryParser.FUZZY_REWRITE_FIELD.getPreferredName(), fuzzyRewrite);
|
builder.field(FUZZY_REWRITE_FIELD.getPreferredName(), fuzzyRewrite);
|
||||||
}
|
}
|
||||||
// LUCENE 4 UPGRADE we need to document this & test this
|
// LUCENE 4 UPGRADE we need to document this & test this
|
||||||
builder.field(MatchQueryParser.FUZZY_TRANSPOSITIONS_FIELD.getPreferredName(), fuzzyTranspositions);
|
builder.field(FUZZY_TRANSPOSITIONS_FIELD.getPreferredName(), fuzzyTranspositions);
|
||||||
builder.field(MatchQueryParser.LENIENT_FIELD.getPreferredName(), lenient);
|
builder.field(LENIENT_FIELD.getPreferredName(), lenient);
|
||||||
builder.field(MatchQueryParser.ZERO_TERMS_QUERY_FIELD.getPreferredName(), zeroTermsQuery.toString());
|
builder.field(ZERO_TERMS_QUERY_FIELD.getPreferredName(), zeroTermsQuery.toString());
|
||||||
if (cutoffFrequency != null) {
|
if (cutoffFrequency != null) {
|
||||||
builder.field(MatchQueryParser.CUTOFF_FREQUENCY_FIELD.getPreferredName(), cutoffFrequency);
|
builder.field(CUTOFF_FREQUENCY_FIELD.getPreferredName(), cutoffFrequency);
|
||||||
}
|
}
|
||||||
printBoostAndQueryName(builder);
|
printBoostAndQueryName(builder);
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
|
@ -467,4 +486,141 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
|
||||||
public String getWriteableName() {
|
public String getWriteableName() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MatchQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
|
||||||
|
XContentParser parser = parseContext.parser();
|
||||||
|
|
||||||
|
MatchQuery.Type type = MatchQuery.Type.BOOLEAN;
|
||||||
|
if (parseContext.parseFieldMatcher().match(parser.currentName(), MATCH_PHRASE_FIELD)) {
|
||||||
|
type = MatchQuery.Type.PHRASE;
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(parser.currentName(), MATCH_PHRASE_PREFIX_FIELD)) {
|
||||||
|
type = MatchQuery.Type.PHRASE_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
XContentParser.Token token = parser.nextToken();
|
||||||
|
if (token != XContentParser.Token.FIELD_NAME) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "[" + MatchQueryBuilder.NAME + "] query malformed, no field");
|
||||||
|
}
|
||||||
|
String fieldName = parser.currentName();
|
||||||
|
|
||||||
|
Object value = null;
|
||||||
|
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
||||||
|
String minimumShouldMatch = null;
|
||||||
|
String analyzer = null;
|
||||||
|
Operator operator = MatchQueryBuilder.DEFAULT_OPERATOR;
|
||||||
|
int slop = MatchQuery.DEFAULT_PHRASE_SLOP;
|
||||||
|
Fuzziness fuzziness = null;
|
||||||
|
int prefixLength = FuzzyQuery.defaultPrefixLength;
|
||||||
|
int maxExpansion = FuzzyQuery.defaultMaxExpansions;
|
||||||
|
boolean fuzzyTranspositions = FuzzyQuery.defaultTranspositions;
|
||||||
|
String fuzzyRewrite = null;
|
||||||
|
boolean lenient = MatchQuery.DEFAULT_LENIENCY;
|
||||||
|
Float cutOffFrequency = null;
|
||||||
|
ZeroTermsQuery zeroTermsQuery = MatchQuery.DEFAULT_ZERO_TERMS_QUERY;
|
||||||
|
String queryName = null;
|
||||||
|
|
||||||
|
token = parser.nextToken();
|
||||||
|
if (token == XContentParser.Token.START_OBJECT) {
|
||||||
|
String currentFieldName = null;
|
||||||
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
currentFieldName = parser.currentName();
|
||||||
|
} else if (token.isValue()) {
|
||||||
|
if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
|
||||||
|
value = parser.objectText();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
|
||||||
|
String tStr = parser.text();
|
||||||
|
if ("boolean".equals(tStr)) {
|
||||||
|
type = MatchQuery.Type.BOOLEAN;
|
||||||
|
} else if ("phrase".equals(tStr)) {
|
||||||
|
type = MatchQuery.Type.PHRASE;
|
||||||
|
} else if ("phrase_prefix".equals(tStr) || ("phrasePrefix".equals(tStr))) {
|
||||||
|
type = MatchQuery.Type.PHRASE_PREFIX;
|
||||||
|
} else {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(),
|
||||||
|
"[" + MatchQueryBuilder.NAME + "] query does not support type " + tStr);
|
||||||
|
}
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ANALYZER_FIELD)) {
|
||||||
|
analyzer = parser.text();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
||||||
|
boost = parser.floatValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SLOP_FIELD)) {
|
||||||
|
slop = parser.intValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, Fuzziness.FIELD)) {
|
||||||
|
fuzziness = Fuzziness.parse(parser);
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, PREFIX_LENGTH_FIELD)) {
|
||||||
|
prefixLength = parser.intValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_EXPANSIONS_FIELD)) {
|
||||||
|
maxExpansion = parser.intValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, OPERATOR_FIELD)) {
|
||||||
|
operator = Operator.fromString(parser.text());
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MINIMUM_SHOULD_MATCH_FIELD)) {
|
||||||
|
minimumShouldMatch = parser.textOrNull();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FUZZY_REWRITE_FIELD)) {
|
||||||
|
fuzzyRewrite = parser.textOrNull();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FUZZY_TRANSPOSITIONS_FIELD)) {
|
||||||
|
fuzzyTranspositions = parser.booleanValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, LENIENT_FIELD)) {
|
||||||
|
lenient = parser.booleanValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, CUTOFF_FREQUENCY_FIELD)) {
|
||||||
|
cutOffFrequency = parser.floatValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ZERO_TERMS_QUERY_FIELD)) {
|
||||||
|
String zeroTermsDocs = parser.text();
|
||||||
|
if ("none".equalsIgnoreCase(zeroTermsDocs)) {
|
||||||
|
zeroTermsQuery = MatchQuery.ZeroTermsQuery.NONE;
|
||||||
|
} else if ("all".equalsIgnoreCase(zeroTermsDocs)) {
|
||||||
|
zeroTermsQuery = MatchQuery.ZeroTermsQuery.ALL;
|
||||||
|
} else {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(),
|
||||||
|
"Unsupported zero_terms_docs value [" + zeroTermsDocs + "]");
|
||||||
|
}
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
||||||
|
queryName = parser.text();
|
||||||
|
} else {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(),
|
||||||
|
"[" + MatchQueryBuilder.NAME + "] query does not support [" + currentFieldName + "]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(),
|
||||||
|
"[" + MatchQueryBuilder.NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parser.nextToken();
|
||||||
|
} else {
|
||||||
|
value = parser.objectText();
|
||||||
|
// move to the next token
|
||||||
|
token = parser.nextToken();
|
||||||
|
if (token != XContentParser.Token.END_OBJECT) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "[match] query parsed in simplified form, with direct field name, "
|
||||||
|
+ "but included more options than just the field name, possibly use its 'options' form, with 'query' element?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "No text specified for text query");
|
||||||
|
}
|
||||||
|
|
||||||
|
MatchQueryBuilder matchQuery = new MatchQueryBuilder(fieldName, value);
|
||||||
|
matchQuery.operator(operator);
|
||||||
|
matchQuery.type(type);
|
||||||
|
matchQuery.analyzer(analyzer);
|
||||||
|
matchQuery.slop(slop);
|
||||||
|
matchQuery.minimumShouldMatch(minimumShouldMatch);
|
||||||
|
if (fuzziness != null) {
|
||||||
|
matchQuery.fuzziness(fuzziness);
|
||||||
|
}
|
||||||
|
matchQuery.fuzzyRewrite(fuzzyRewrite);
|
||||||
|
matchQuery.prefixLength(prefixLength);
|
||||||
|
matchQuery.fuzzyTranspositions(fuzzyTranspositions);
|
||||||
|
matchQuery.maxExpansions(maxExpansion);
|
||||||
|
matchQuery.lenient(lenient);
|
||||||
|
if (cutOffFrequency != null) {
|
||||||
|
matchQuery.cutoffFrequency(cutOffFrequency);
|
||||||
|
}
|
||||||
|
matchQuery.zeroTermsQuery(zeroTermsQuery);
|
||||||
|
matchQuery.queryName(queryName);
|
||||||
|
matchQuery.boost(boost);
|
||||||
|
return matchQuery;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,197 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.FuzzyQuery;
|
|
||||||
import org.elasticsearch.common.ParseField;
|
|
||||||
import org.elasticsearch.common.ParsingException;
|
|
||||||
import org.elasticsearch.common.unit.Fuzziness;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
|
||||||
import org.elasticsearch.index.search.MatchQuery;
|
|
||||||
import org.elasticsearch.index.search.MatchQuery.ZeroTermsQuery;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MatchQueryParser implements QueryParser<MatchQueryBuilder> {
|
|
||||||
|
|
||||||
public static final ParseField MATCH_PHRASE_FIELD = new ParseField("match_phrase", "text_phrase");
|
|
||||||
public static final ParseField MATCH_PHRASE_PREFIX_FIELD = new ParseField("match_phrase_prefix", "text_phrase_prefix");
|
|
||||||
public static final ParseField SLOP_FIELD = new ParseField("slop", "phrase_slop");
|
|
||||||
public static final ParseField ZERO_TERMS_QUERY_FIELD = new ParseField("zero_terms_query");
|
|
||||||
public static final ParseField CUTOFF_FREQUENCY_FIELD = new ParseField("cutoff_frequency");
|
|
||||||
public static final ParseField LENIENT_FIELD = new ParseField("lenient");
|
|
||||||
public static final ParseField FUZZY_TRANSPOSITIONS_FIELD = new ParseField("fuzzy_transpositions");
|
|
||||||
public static final ParseField FUZZY_REWRITE_FIELD = new ParseField("fuzzy_rewrite");
|
|
||||||
public static final ParseField MINIMUM_SHOULD_MATCH_FIELD = new ParseField("minimum_should_match");
|
|
||||||
public static final ParseField OPERATOR_FIELD = new ParseField("operator");
|
|
||||||
public static final ParseField MAX_EXPANSIONS_FIELD = new ParseField("max_expansions");
|
|
||||||
public static final ParseField PREFIX_LENGTH_FIELD = new ParseField("prefix_length");
|
|
||||||
public static final ParseField ANALYZER_FIELD = new ParseField("analyzer");
|
|
||||||
public static final ParseField TYPE_FIELD = new ParseField("type");
|
|
||||||
public static final ParseField QUERY_FIELD = new ParseField("query");
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] names() {
|
|
||||||
return new String[]{
|
|
||||||
MatchQueryBuilder.NAME, "match_phrase", "matchPhrase", "match_phrase_prefix", "matchPhrasePrefix", "matchFuzzy", "match_fuzzy", "fuzzy_match"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MatchQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
|
|
||||||
XContentParser parser = parseContext.parser();
|
|
||||||
|
|
||||||
MatchQuery.Type type = MatchQuery.Type.BOOLEAN;
|
|
||||||
if (parseContext.parseFieldMatcher().match(parser.currentName(), MATCH_PHRASE_FIELD)) {
|
|
||||||
type = MatchQuery.Type.PHRASE;
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(parser.currentName(), MATCH_PHRASE_PREFIX_FIELD)) {
|
|
||||||
type = MatchQuery.Type.PHRASE_PREFIX;
|
|
||||||
}
|
|
||||||
|
|
||||||
XContentParser.Token token = parser.nextToken();
|
|
||||||
if (token != XContentParser.Token.FIELD_NAME) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "[" + MatchQueryBuilder.NAME + "] query malformed, no field");
|
|
||||||
}
|
|
||||||
String fieldName = parser.currentName();
|
|
||||||
|
|
||||||
Object value = null;
|
|
||||||
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
|
||||||
String minimumShouldMatch = null;
|
|
||||||
String analyzer = null;
|
|
||||||
Operator operator = MatchQueryBuilder.DEFAULT_OPERATOR;
|
|
||||||
int slop = MatchQuery.DEFAULT_PHRASE_SLOP;
|
|
||||||
Fuzziness fuzziness = null;
|
|
||||||
int prefixLength = FuzzyQuery.defaultPrefixLength;
|
|
||||||
int maxExpansion = FuzzyQuery.defaultMaxExpansions;
|
|
||||||
boolean fuzzyTranspositions = FuzzyQuery.defaultTranspositions;
|
|
||||||
String fuzzyRewrite = null;
|
|
||||||
boolean lenient = MatchQuery.DEFAULT_LENIENCY;
|
|
||||||
Float cutOffFrequency = null;
|
|
||||||
ZeroTermsQuery zeroTermsQuery = MatchQuery.DEFAULT_ZERO_TERMS_QUERY;
|
|
||||||
String queryName = null;
|
|
||||||
|
|
||||||
token = parser.nextToken();
|
|
||||||
if (token == XContentParser.Token.START_OBJECT) {
|
|
||||||
String currentFieldName = null;
|
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
|
||||||
currentFieldName = parser.currentName();
|
|
||||||
} else if (token.isValue()) {
|
|
||||||
if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
|
|
||||||
value = parser.objectText();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
|
|
||||||
String tStr = parser.text();
|
|
||||||
if ("boolean".equals(tStr)) {
|
|
||||||
type = MatchQuery.Type.BOOLEAN;
|
|
||||||
} else if ("phrase".equals(tStr)) {
|
|
||||||
type = MatchQuery.Type.PHRASE;
|
|
||||||
} else if ("phrase_prefix".equals(tStr) || ("phrasePrefix".equals(tStr))) {
|
|
||||||
type = MatchQuery.Type.PHRASE_PREFIX;
|
|
||||||
} else {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "[" + MatchQueryBuilder.NAME + "] query does not support type " + tStr);
|
|
||||||
}
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ANALYZER_FIELD)) {
|
|
||||||
analyzer = parser.text();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
|
||||||
boost = parser.floatValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, SLOP_FIELD)) {
|
|
||||||
slop = parser.intValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, Fuzziness.FIELD)) {
|
|
||||||
fuzziness = Fuzziness.parse(parser);
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, PREFIX_LENGTH_FIELD)) {
|
|
||||||
prefixLength = parser.intValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_EXPANSIONS_FIELD)) {
|
|
||||||
maxExpansion = parser.intValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, OPERATOR_FIELD)) {
|
|
||||||
operator = Operator.fromString(parser.text());
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MINIMUM_SHOULD_MATCH_FIELD)) {
|
|
||||||
minimumShouldMatch = parser.textOrNull();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FUZZY_REWRITE_FIELD)) {
|
|
||||||
fuzzyRewrite = parser.textOrNull();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FUZZY_TRANSPOSITIONS_FIELD)) {
|
|
||||||
fuzzyTranspositions = parser.booleanValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, LENIENT_FIELD)) {
|
|
||||||
lenient = parser.booleanValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, CUTOFF_FREQUENCY_FIELD)) {
|
|
||||||
cutOffFrequency = parser.floatValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ZERO_TERMS_QUERY_FIELD)) {
|
|
||||||
String zeroTermsDocs = parser.text();
|
|
||||||
if ("none".equalsIgnoreCase(zeroTermsDocs)) {
|
|
||||||
zeroTermsQuery = MatchQuery.ZeroTermsQuery.NONE;
|
|
||||||
} else if ("all".equalsIgnoreCase(zeroTermsDocs)) {
|
|
||||||
zeroTermsQuery = MatchQuery.ZeroTermsQuery.ALL;
|
|
||||||
} else {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "Unsupported zero_terms_docs value [" + zeroTermsDocs + "]");
|
|
||||||
}
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
|
||||||
queryName = parser.text();
|
|
||||||
} else {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "[" + MatchQueryBuilder.NAME + "] query does not support [" + currentFieldName + "]");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "[" + MatchQueryBuilder.NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parser.nextToken();
|
|
||||||
} else {
|
|
||||||
value = parser.objectText();
|
|
||||||
// move to the next token
|
|
||||||
token = parser.nextToken();
|
|
||||||
if (token != XContentParser.Token.END_OBJECT) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(),
|
|
||||||
"[match] query parsed in simplified form, with direct field name, but included more options than just the field name, possibly use its 'options' form, with 'query' element?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "No text specified for text query");
|
|
||||||
}
|
|
||||||
|
|
||||||
MatchQueryBuilder matchQuery = new MatchQueryBuilder(fieldName, value);
|
|
||||||
matchQuery.operator(operator);
|
|
||||||
matchQuery.type(type);
|
|
||||||
matchQuery.analyzer(analyzer);
|
|
||||||
matchQuery.slop(slop);
|
|
||||||
matchQuery.minimumShouldMatch(minimumShouldMatch);
|
|
||||||
if (fuzziness != null) {
|
|
||||||
matchQuery.fuzziness(fuzziness);
|
|
||||||
}
|
|
||||||
matchQuery.fuzzyRewrite(fuzzyRewrite);
|
|
||||||
matchQuery.prefixLength(prefixLength);
|
|
||||||
matchQuery.fuzzyTranspositions(fuzzyTranspositions);
|
|
||||||
matchQuery.maxExpansions(maxExpansion);
|
|
||||||
matchQuery.lenient(lenient);
|
|
||||||
if (cutOffFrequency != null) {
|
|
||||||
matchQuery.cutoffFrequency(cutOffFrequency);
|
|
||||||
}
|
|
||||||
matchQuery.zeroTermsQuery(zeroTermsQuery);
|
|
||||||
matchQuery.queryName(queryName);
|
|
||||||
matchQuery.boost(boost);
|
|
||||||
return matchQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MatchQueryBuilder getBuilderPrototype() {
|
|
||||||
return MatchQueryBuilder.PROTOTYPE;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -30,7 +30,7 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link MatchQueryParser} but has support for multiple fields.
|
* Same as {@link MatchQueryBuilder} but has support for multiple fields.
|
||||||
*/
|
*/
|
||||||
public class MultiMatchQueryParser implements QueryParser<MultiMatchQueryBuilder> {
|
public class MultiMatchQueryParser implements QueryParser<MultiMatchQueryBuilder> {
|
||||||
|
|
||||||
|
|
|
@ -25,12 +25,15 @@ import java.io.IOException;
|
||||||
* Defines a query parser that is able to read and parse a query object in {@link org.elasticsearch.common.xcontent.XContent}
|
* Defines a query parser that is able to read and parse a query object in {@link org.elasticsearch.common.xcontent.XContent}
|
||||||
* format and create an internal object representing the query, implementing {@link QueryBuilder}, which can be streamed to other nodes.
|
* format and create an internal object representing the query, implementing {@link QueryBuilder}, which can be streamed to other nodes.
|
||||||
*/
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
public interface QueryParser<QB extends QueryBuilder<QB>> {
|
public interface QueryParser<QB extends QueryBuilder<QB>> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The names this query parser is registered under.
|
* The names this query parser is registered under.
|
||||||
*/
|
*/
|
||||||
String[] names();
|
default String[] names() { // TODO remove this when nothing implements it
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link QueryBuilder} from the query held by the {@link QueryParseContext}
|
* Creates a new {@link QueryBuilder} from the query held by the {@link QueryParseContext}
|
||||||
|
@ -47,5 +50,7 @@ public interface QueryParser<QB extends QueryBuilder<QB>> {
|
||||||
/**
|
/**
|
||||||
* @return an empty {@link QueryBuilder} instance for this parser that can be used for deserialization
|
* @return an empty {@link QueryBuilder} instance for this parser that can be used for deserialization
|
||||||
*/
|
*/
|
||||||
QB getBuilderPrototype();
|
default QB getBuilderPrototype() { // TODO remove this when nothing implements it
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ package org.elasticsearch.index.query.functionscore;
|
||||||
|
|
||||||
import org.apache.lucene.search.MatchAllDocsQuery;
|
import org.apache.lucene.search.MatchAllDocsQuery;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
|
import org.elasticsearch.common.ParsingException;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
@ -30,18 +32,25 @@ import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
|
||||||
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
|
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentLocation;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.query.AbstractQueryBuilder;
|
import org.elasticsearch.index.query.AbstractQueryBuilder;
|
||||||
import org.elasticsearch.index.query.EmptyQueryBuilder;
|
import org.elasticsearch.index.query.EmptyQueryBuilder;
|
||||||
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.QueryRewriteContext;
|
import org.elasticsearch.index.query.QueryRewriteContext;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.index.query.functionscore.random.RandomScoreFunctionBuilder;
|
import org.elasticsearch.index.query.functionscore.random.RandomScoreFunctionBuilder;
|
||||||
|
import org.elasticsearch.index.query.functionscore.weight.WeightBuilder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A query that uses a filters with a script associated with them to compute the
|
* A query that uses a filters with a script associated with them to compute the
|
||||||
|
@ -49,8 +58,23 @@ import java.util.Objects;
|
||||||
*/
|
*/
|
||||||
public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScoreQueryBuilder> {
|
public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScoreQueryBuilder> {
|
||||||
|
|
||||||
|
public static final FunctionScoreQueryBuilder PROTOTYPE = new FunctionScoreQueryBuilder(EmptyQueryBuilder.PROTOTYPE,
|
||||||
|
new FunctionScoreQueryBuilder.FilterFunctionBuilder[0]);
|
||||||
|
|
||||||
public static final String NAME = "function_score";
|
public static final String NAME = "function_score";
|
||||||
|
|
||||||
|
// For better readability of error message
|
||||||
|
static final String MISPLACED_FUNCTION_MESSAGE_PREFIX = "you can either define [functions] array or a single function, not both. ";
|
||||||
|
|
||||||
|
public static final ParseField WEIGHT_FIELD = new ParseField("weight");
|
||||||
|
public static final ParseField QUERY_FIELD = new ParseField("query");
|
||||||
|
public static final ParseField FILTER_FIELD = new ParseField("filter");
|
||||||
|
public static final ParseField FUNCTIONS_FIELD = new ParseField("functions");
|
||||||
|
public static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode");
|
||||||
|
public static final ParseField BOOST_MODE_FIELD = new ParseField("boost_mode");
|
||||||
|
public static final ParseField MAX_BOOST_FIELD = new ParseField("max_boost");
|
||||||
|
public static final ParseField MIN_SCORE_FIELD = new ParseField("min_score");
|
||||||
|
|
||||||
public static final CombineFunction DEFAULT_BOOST_MODE = CombineFunction.MULTIPLY;
|
public static final CombineFunction DEFAULT_BOOST_MODE = CombineFunction.MULTIPLY;
|
||||||
public static final FiltersFunctionScoreQuery.ScoreMode DEFAULT_SCORE_MODE = FiltersFunctionScoreQuery.ScoreMode.MULTIPLY;
|
public static final FiltersFunctionScoreQuery.ScoreMode DEFAULT_SCORE_MODE = FiltersFunctionScoreQuery.ScoreMode.MULTIPLY;
|
||||||
|
|
||||||
|
@ -198,22 +222,22 @@ public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScor
|
||||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.startObject(NAME);
|
builder.startObject(NAME);
|
||||||
if (query != null) {
|
if (query != null) {
|
||||||
builder.field(FunctionScoreQueryParser.QUERY_FIELD.getPreferredName());
|
builder.field(QUERY_FIELD.getPreferredName());
|
||||||
query.toXContent(builder, params);
|
query.toXContent(builder, params);
|
||||||
}
|
}
|
||||||
builder.startArray(FunctionScoreQueryParser.FUNCTIONS_FIELD.getPreferredName());
|
builder.startArray(FUNCTIONS_FIELD.getPreferredName());
|
||||||
for (FilterFunctionBuilder filterFunctionBuilder : filterFunctionBuilders) {
|
for (FilterFunctionBuilder filterFunctionBuilder : filterFunctionBuilders) {
|
||||||
filterFunctionBuilder.toXContent(builder, params);
|
filterFunctionBuilder.toXContent(builder, params);
|
||||||
}
|
}
|
||||||
builder.endArray();
|
builder.endArray();
|
||||||
|
|
||||||
builder.field(FunctionScoreQueryParser.SCORE_MODE_FIELD.getPreferredName(), scoreMode.name().toLowerCase(Locale.ROOT));
|
builder.field(SCORE_MODE_FIELD.getPreferredName(), scoreMode.name().toLowerCase(Locale.ROOT));
|
||||||
if (boostMode != null) {
|
if (boostMode != null) {
|
||||||
builder.field(FunctionScoreQueryParser.BOOST_MODE_FIELD.getPreferredName(), boostMode.name().toLowerCase(Locale.ROOT));
|
builder.field(BOOST_MODE_FIELD.getPreferredName(), boostMode.name().toLowerCase(Locale.ROOT));
|
||||||
}
|
}
|
||||||
builder.field(FunctionScoreQueryParser.MAX_BOOST_FIELD.getPreferredName(), maxBoost);
|
builder.field(MAX_BOOST_FIELD.getPreferredName(), maxBoost);
|
||||||
if (minScore != null) {
|
if (minScore != null) {
|
||||||
builder.field(FunctionScoreQueryParser.MIN_SCORE_FIELD.getPreferredName(), minScore);
|
builder.field(MIN_SCORE_FIELD.getPreferredName(), minScore);
|
||||||
}
|
}
|
||||||
printBoostAndQueryName(builder);
|
printBoostAndQueryName(builder);
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
|
@ -359,7 +383,7 @@ public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScor
|
||||||
@Override
|
@Override
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.startObject();
|
builder.startObject();
|
||||||
builder.field(FunctionScoreQueryParser.FILTER_FIELD.getPreferredName());
|
builder.field(FILTER_FIELD.getPreferredName());
|
||||||
filter.toXContent(builder, params);
|
filter.toXContent(builder, params);
|
||||||
scoreFunction.toXContent(builder, params);
|
scoreFunction.toXContent(builder, params);
|
||||||
builder.endObject();
|
builder.endObject();
|
||||||
|
@ -423,4 +447,179 @@ public class FunctionScoreQueryBuilder extends AbstractQueryBuilder<FunctionScor
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FunctionScoreQueryBuilder fromXContent(Function<String, ScoreFunctionParser<?>> scoreFunctionLookup,
|
||||||
|
QueryParseContext parseContext) throws IOException {
|
||||||
|
XContentParser parser = parseContext.parser();
|
||||||
|
|
||||||
|
QueryBuilder<?> query = null;
|
||||||
|
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
||||||
|
String queryName = null;
|
||||||
|
|
||||||
|
FiltersFunctionScoreQuery.ScoreMode scoreMode = FunctionScoreQueryBuilder.DEFAULT_SCORE_MODE;
|
||||||
|
float maxBoost = FunctionScoreQuery.DEFAULT_MAX_BOOST;
|
||||||
|
Float minScore = null;
|
||||||
|
|
||||||
|
String currentFieldName = null;
|
||||||
|
XContentParser.Token token;
|
||||||
|
CombineFunction combineFunction = null;
|
||||||
|
// Either define array of functions and filters or only one function
|
||||||
|
boolean functionArrayFound = false;
|
||||||
|
boolean singleFunctionFound = false;
|
||||||
|
String singleFunctionName = null;
|
||||||
|
List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
|
||||||
|
|
||||||
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
currentFieldName = parser.currentName();
|
||||||
|
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||||
|
if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
|
||||||
|
if (query != null) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. [query] is already defined.", FunctionScoreQueryBuilder.NAME);
|
||||||
|
}
|
||||||
|
query = parseContext.parseInnerQueryBuilder();
|
||||||
|
} else {
|
||||||
|
if (singleFunctionFound) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. already found function [{}], now encountering [{}]. use [functions] array if you want to define several functions.", FunctionScoreQueryBuilder.NAME, singleFunctionName, currentFieldName);
|
||||||
|
}
|
||||||
|
if (functionArrayFound) {
|
||||||
|
String errorString = "already found [functions] array, now encountering [" + currentFieldName + "].";
|
||||||
|
handleMisplacedFunctionsDeclaration(parser.getTokenLocation(), errorString);
|
||||||
|
}
|
||||||
|
singleFunctionFound = true;
|
||||||
|
singleFunctionName = currentFieldName;
|
||||||
|
|
||||||
|
// we try to parse a score function. If there is no score function for the current field name,
|
||||||
|
// functionParserMapper.get() may throw an Exception.
|
||||||
|
ScoreFunctionBuilder<?> scoreFunction = lookupFunctionParser(scoreFunctionLookup, parser.getTokenLocation(), currentFieldName).fromXContent(parseContext, parser);
|
||||||
|
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(scoreFunction));
|
||||||
|
}
|
||||||
|
} else if (token == XContentParser.Token.START_ARRAY) {
|
||||||
|
if (parseContext.parseFieldMatcher().match(currentFieldName, FUNCTIONS_FIELD)) {
|
||||||
|
if (singleFunctionFound) {
|
||||||
|
String errorString = "already found [" + singleFunctionName + "], now encountering [functions].";
|
||||||
|
handleMisplacedFunctionsDeclaration(parser.getTokenLocation(), errorString);
|
||||||
|
}
|
||||||
|
functionArrayFound = true;
|
||||||
|
currentFieldName = parseFiltersAndFunctions(scoreFunctionLookup, parseContext, parser, filterFunctionBuilders);
|
||||||
|
} else {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. array [{}] is not supported", FunctionScoreQueryBuilder.NAME, currentFieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (token.isValue()) {
|
||||||
|
if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_MODE_FIELD)) {
|
||||||
|
scoreMode = FiltersFunctionScoreQuery.ScoreMode.fromString(parser.text());
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, BOOST_MODE_FIELD)) {
|
||||||
|
combineFunction = CombineFunction.fromString(parser.text());
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_BOOST_FIELD)) {
|
||||||
|
maxBoost = parser.floatValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
||||||
|
boost = parser.floatValue();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
||||||
|
queryName = parser.text();
|
||||||
|
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MIN_SCORE_FIELD)) {
|
||||||
|
minScore = parser.floatValue();
|
||||||
|
} else {
|
||||||
|
if (singleFunctionFound) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. already found function [{}], now encountering [{}]. use [functions] array if you want to define several functions.", FunctionScoreQueryBuilder.NAME, singleFunctionName, currentFieldName);
|
||||||
|
}
|
||||||
|
if (functionArrayFound) {
|
||||||
|
String errorString = "already found [functions] array, now encountering [" + currentFieldName + "].";
|
||||||
|
handleMisplacedFunctionsDeclaration(parser.getTokenLocation(), errorString);
|
||||||
|
}
|
||||||
|
if (parseContext.parseFieldMatcher().match(currentFieldName, WEIGHT_FIELD)) {
|
||||||
|
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(new WeightBuilder().setWeight(parser.floatValue())));
|
||||||
|
singleFunctionFound = true;
|
||||||
|
singleFunctionName = currentFieldName;
|
||||||
|
} else {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. field [{}] is not supported", FunctionScoreQueryBuilder.NAME, currentFieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query == null) {
|
||||||
|
query = new MatchAllQueryBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(query,
|
||||||
|
filterFunctionBuilders.toArray(new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]));
|
||||||
|
if (combineFunction != null) {
|
||||||
|
functionScoreQueryBuilder.boostMode(combineFunction);
|
||||||
|
}
|
||||||
|
functionScoreQueryBuilder.scoreMode(scoreMode);
|
||||||
|
functionScoreQueryBuilder.maxBoost(maxBoost);
|
||||||
|
if (minScore != null) {
|
||||||
|
functionScoreQueryBuilder.setMinScore(minScore);
|
||||||
|
}
|
||||||
|
functionScoreQueryBuilder.boost(boost);
|
||||||
|
functionScoreQueryBuilder.queryName(queryName);
|
||||||
|
return functionScoreQueryBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleMisplacedFunctionsDeclaration(XContentLocation contentLocation, String errorString) {
|
||||||
|
throw new ParsingException(contentLocation, "failed to parse [{}] query. [{}]", FunctionScoreQueryBuilder.NAME, MISPLACED_FUNCTION_MESSAGE_PREFIX + errorString);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseFiltersAndFunctions(Function<String, ScoreFunctionParser<?>> scoreFunctionLookup,
|
||||||
|
QueryParseContext parseContext, XContentParser parser,
|
||||||
|
List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders) throws IOException {
|
||||||
|
String currentFieldName = null;
|
||||||
|
XContentParser.Token token;
|
||||||
|
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||||
|
QueryBuilder filter = null;
|
||||||
|
ScoreFunctionBuilder<?> scoreFunction = null;
|
||||||
|
Float functionWeight = null;
|
||||||
|
if (token != XContentParser.Token.START_OBJECT) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}]. malformed query, expected a [{}] while parsing functions but got a [{}] instead", XContentParser.Token.START_OBJECT, token, FunctionScoreQueryBuilder.NAME);
|
||||||
|
} else {
|
||||||
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
currentFieldName = parser.currentName();
|
||||||
|
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||||
|
if (parseContext.parseFieldMatcher().match(currentFieldName, FILTER_FIELD)) {
|
||||||
|
filter = parseContext.parseInnerQueryBuilder();
|
||||||
|
} else {
|
||||||
|
if (scoreFunction != null) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse function_score functions. already found [{}], now encountering [{}].", scoreFunction.getName(), currentFieldName);
|
||||||
|
}
|
||||||
|
// do not need to check null here, functionParserMapper does it already
|
||||||
|
ScoreFunctionParser functionParser = lookupFunctionParser(scoreFunctionLookup, parser.getTokenLocation(), currentFieldName);
|
||||||
|
scoreFunction = functionParser.fromXContent(parseContext, parser);
|
||||||
|
}
|
||||||
|
} else if (token.isValue()) {
|
||||||
|
if (parseContext.parseFieldMatcher().match(currentFieldName, WEIGHT_FIELD)) {
|
||||||
|
functionWeight = parser.floatValue();
|
||||||
|
} else {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. field [{}] is not supported", FunctionScoreQueryBuilder.NAME, currentFieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (functionWeight != null) {
|
||||||
|
if (scoreFunction == null) {
|
||||||
|
scoreFunction = new WeightBuilder().setWeight(functionWeight);
|
||||||
|
} else {
|
||||||
|
scoreFunction.setWeight(functionWeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filter == null) {
|
||||||
|
filter = new MatchAllQueryBuilder();
|
||||||
|
}
|
||||||
|
if (scoreFunction == null) {
|
||||||
|
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. an entry in functions list is missing a function.", FunctionScoreQueryBuilder.NAME);
|
||||||
|
}
|
||||||
|
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(filter, scoreFunction));
|
||||||
|
}
|
||||||
|
return currentFieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ScoreFunctionParser<?> lookupFunctionParser(Function<String, ScoreFunctionParser<?>> scoreFunctionLookup,
|
||||||
|
XContentLocation contentLocation, String parserName) {
|
||||||
|
ScoreFunctionParser<?> functionParser = scoreFunctionLookup.apply(parserName);
|
||||||
|
if (functionParser == null) {
|
||||||
|
throw new ParsingException(contentLocation, "No function with the name [" + parserName + "] is registered.");
|
||||||
|
}
|
||||||
|
return functionParser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,240 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.functionscore;
|
|
||||||
|
|
||||||
import org.elasticsearch.common.ParseField;
|
|
||||||
import org.elasticsearch.common.ParsingException;
|
|
||||||
import org.elasticsearch.common.Strings;
|
|
||||||
import org.elasticsearch.common.lucene.search.function.CombineFunction;
|
|
||||||
import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery;
|
|
||||||
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentLocation;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
|
||||||
import org.elasticsearch.index.query.AbstractQueryBuilder;
|
|
||||||
import org.elasticsearch.index.query.EmptyQueryBuilder;
|
|
||||||
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
|
||||||
import org.elasticsearch.index.query.QueryParser;
|
|
||||||
import org.elasticsearch.index.query.functionscore.weight.WeightBuilder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parser for function_score query
|
|
||||||
*/
|
|
||||||
public class FunctionScoreQueryParser implements QueryParser<FunctionScoreQueryBuilder> {
|
|
||||||
|
|
||||||
private static final FunctionScoreQueryBuilder PROTOTYPE = new FunctionScoreQueryBuilder(EmptyQueryBuilder.PROTOTYPE, new FunctionScoreQueryBuilder.FilterFunctionBuilder[0]);
|
|
||||||
|
|
||||||
// For better readability of error message
|
|
||||||
static final String MISPLACED_FUNCTION_MESSAGE_PREFIX = "you can either define [functions] array or a single function, not both. ";
|
|
||||||
|
|
||||||
public static final ParseField WEIGHT_FIELD = new ParseField("weight");
|
|
||||||
public static final ParseField QUERY_FIELD = new ParseField("query");
|
|
||||||
public static final ParseField FILTER_FIELD = new ParseField("filter");
|
|
||||||
public static final ParseField FUNCTIONS_FIELD = new ParseField("functions");
|
|
||||||
public static final ParseField SCORE_MODE_FIELD = new ParseField("score_mode");
|
|
||||||
public static final ParseField BOOST_MODE_FIELD = new ParseField("boost_mode");
|
|
||||||
public static final ParseField MAX_BOOST_FIELD = new ParseField("max_boost");
|
|
||||||
public static final ParseField MIN_SCORE_FIELD = new ParseField("min_score");
|
|
||||||
|
|
||||||
private final ScoreFunctionParserMapper functionParserMapper;
|
|
||||||
|
|
||||||
public FunctionScoreQueryParser(ScoreFunctionParserMapper functionParserMapper) {
|
|
||||||
this.functionParserMapper = functionParserMapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] names() {
|
|
||||||
return new String[] { FunctionScoreQueryBuilder.NAME, Strings.toCamelCase(FunctionScoreQueryBuilder.NAME) };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FunctionScoreQueryBuilder fromXContent(QueryParseContext parseContext) throws IOException {
|
|
||||||
XContentParser parser = parseContext.parser();
|
|
||||||
|
|
||||||
QueryBuilder query = null;
|
|
||||||
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
|
||||||
String queryName = null;
|
|
||||||
|
|
||||||
FiltersFunctionScoreQuery.ScoreMode scoreMode = FunctionScoreQueryBuilder.DEFAULT_SCORE_MODE;
|
|
||||||
float maxBoost = FunctionScoreQuery.DEFAULT_MAX_BOOST;
|
|
||||||
Float minScore = null;
|
|
||||||
|
|
||||||
String currentFieldName = null;
|
|
||||||
XContentParser.Token token;
|
|
||||||
CombineFunction combineFunction = null;
|
|
||||||
// Either define array of functions and filters or only one function
|
|
||||||
boolean functionArrayFound = false;
|
|
||||||
boolean singleFunctionFound = false;
|
|
||||||
String singleFunctionName = null;
|
|
||||||
List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
|
|
||||||
|
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
|
||||||
currentFieldName = parser.currentName();
|
|
||||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
|
||||||
if (parseContext.parseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
|
|
||||||
if (query != null) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. [query] is already defined.", FunctionScoreQueryBuilder.NAME);
|
|
||||||
}
|
|
||||||
query = parseContext.parseInnerQueryBuilder();
|
|
||||||
} else {
|
|
||||||
if (singleFunctionFound) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. already found function [{}], now encountering [{}]. use [functions] array if you want to define several functions.", FunctionScoreQueryBuilder.NAME, singleFunctionName, currentFieldName);
|
|
||||||
}
|
|
||||||
if (functionArrayFound) {
|
|
||||||
String errorString = "already found [functions] array, now encountering [" + currentFieldName + "].";
|
|
||||||
handleMisplacedFunctionsDeclaration(parser.getTokenLocation(), errorString);
|
|
||||||
}
|
|
||||||
singleFunctionFound = true;
|
|
||||||
singleFunctionName = currentFieldName;
|
|
||||||
|
|
||||||
// we try to parse a score function. If there is no score function for the current field name,
|
|
||||||
// functionParserMapper.get() may throw an Exception.
|
|
||||||
ScoreFunctionBuilder<?> scoreFunction = functionParserMapper.get(parser.getTokenLocation(), currentFieldName).fromXContent(parseContext, parser);
|
|
||||||
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(scoreFunction));
|
|
||||||
}
|
|
||||||
} else if (token == XContentParser.Token.START_ARRAY) {
|
|
||||||
if (parseContext.parseFieldMatcher().match(currentFieldName, FUNCTIONS_FIELD)) {
|
|
||||||
if (singleFunctionFound) {
|
|
||||||
String errorString = "already found [" + singleFunctionName + "], now encountering [functions].";
|
|
||||||
handleMisplacedFunctionsDeclaration(parser.getTokenLocation(), errorString);
|
|
||||||
}
|
|
||||||
functionArrayFound = true;
|
|
||||||
currentFieldName = parseFiltersAndFunctions(parseContext, parser, filterFunctionBuilders);
|
|
||||||
} else {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. array [{}] is not supported", FunctionScoreQueryBuilder.NAME, currentFieldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (token.isValue()) {
|
|
||||||
if (parseContext.parseFieldMatcher().match(currentFieldName, SCORE_MODE_FIELD)) {
|
|
||||||
scoreMode = FiltersFunctionScoreQuery.ScoreMode.fromString(parser.text());
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, BOOST_MODE_FIELD)) {
|
|
||||||
combineFunction = CombineFunction.fromString(parser.text());
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MAX_BOOST_FIELD)) {
|
|
||||||
maxBoost = parser.floatValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
|
|
||||||
boost = parser.floatValue();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
|
|
||||||
queryName = parser.text();
|
|
||||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MIN_SCORE_FIELD)) {
|
|
||||||
minScore = parser.floatValue();
|
|
||||||
} else {
|
|
||||||
if (singleFunctionFound) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. already found function [{}], now encountering [{}]. use [functions] array if you want to define several functions.", FunctionScoreQueryBuilder.NAME, singleFunctionName, currentFieldName);
|
|
||||||
}
|
|
||||||
if (functionArrayFound) {
|
|
||||||
String errorString = "already found [functions] array, now encountering [" + currentFieldName + "].";
|
|
||||||
handleMisplacedFunctionsDeclaration(parser.getTokenLocation(), errorString);
|
|
||||||
}
|
|
||||||
if (parseContext.parseFieldMatcher().match(currentFieldName, WEIGHT_FIELD)) {
|
|
||||||
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(new WeightBuilder().setWeight(parser.floatValue())));
|
|
||||||
singleFunctionFound = true;
|
|
||||||
singleFunctionName = currentFieldName;
|
|
||||||
} else {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. field [{}] is not supported", FunctionScoreQueryBuilder.NAME, currentFieldName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query == null) {
|
|
||||||
query = new MatchAllQueryBuilder();
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionScoreQueryBuilder functionScoreQueryBuilder = new FunctionScoreQueryBuilder(query,
|
|
||||||
filterFunctionBuilders.toArray(new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()]));
|
|
||||||
if (combineFunction != null) {
|
|
||||||
functionScoreQueryBuilder.boostMode(combineFunction);
|
|
||||||
}
|
|
||||||
functionScoreQueryBuilder.scoreMode(scoreMode);
|
|
||||||
functionScoreQueryBuilder.maxBoost(maxBoost);
|
|
||||||
if (minScore != null) {
|
|
||||||
functionScoreQueryBuilder.setMinScore(minScore);
|
|
||||||
}
|
|
||||||
functionScoreQueryBuilder.boost(boost);
|
|
||||||
functionScoreQueryBuilder.queryName(queryName);
|
|
||||||
return functionScoreQueryBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void handleMisplacedFunctionsDeclaration(XContentLocation contentLocation, String errorString) {
|
|
||||||
throw new ParsingException(contentLocation, "failed to parse [{}] query. [{}]", FunctionScoreQueryBuilder.NAME, MISPLACED_FUNCTION_MESSAGE_PREFIX + errorString);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String parseFiltersAndFunctions(QueryParseContext parseContext, XContentParser parser, List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders) throws IOException {
|
|
||||||
String currentFieldName = null;
|
|
||||||
XContentParser.Token token;
|
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
|
||||||
QueryBuilder filter = null;
|
|
||||||
ScoreFunctionBuilder<?> scoreFunction = null;
|
|
||||||
Float functionWeight = null;
|
|
||||||
if (token != XContentParser.Token.START_OBJECT) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}]. malformed query, expected a [{}] while parsing functions but got a [{}] instead", XContentParser.Token.START_OBJECT, token, FunctionScoreQueryBuilder.NAME);
|
|
||||||
} else {
|
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
|
||||||
currentFieldName = parser.currentName();
|
|
||||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
|
||||||
if (parseContext.parseFieldMatcher().match(currentFieldName, FILTER_FIELD)) {
|
|
||||||
filter = parseContext.parseInnerQueryBuilder();
|
|
||||||
} else {
|
|
||||||
if (scoreFunction != null) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse function_score functions. already found [{}], now encountering [{}].", scoreFunction.getName(), currentFieldName);
|
|
||||||
}
|
|
||||||
// do not need to check null here, functionParserMapper does it already
|
|
||||||
ScoreFunctionParser functionParser = functionParserMapper.get(parser.getTokenLocation(), currentFieldName);
|
|
||||||
scoreFunction = functionParser.fromXContent(parseContext, parser);
|
|
||||||
}
|
|
||||||
} else if (token.isValue()) {
|
|
||||||
if (parseContext.parseFieldMatcher().match(currentFieldName, WEIGHT_FIELD)) {
|
|
||||||
functionWeight = parser.floatValue();
|
|
||||||
} else {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. field [{}] is not supported", FunctionScoreQueryBuilder.NAME, currentFieldName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (functionWeight != null) {
|
|
||||||
if (scoreFunction == null) {
|
|
||||||
scoreFunction = new WeightBuilder().setWeight(functionWeight);
|
|
||||||
} else {
|
|
||||||
scoreFunction.setWeight(functionWeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (filter == null) {
|
|
||||||
filter = new MatchAllQueryBuilder();
|
|
||||||
}
|
|
||||||
if (scoreFunction == null) {
|
|
||||||
throw new ParsingException(parser.getTokenLocation(), "failed to parse [{}] query. an entry in functions list is missing a function.", FunctionScoreQueryBuilder.NAME);
|
|
||||||
}
|
|
||||||
filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(filter, scoreFunction));
|
|
||||||
}
|
|
||||||
return currentFieldName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FunctionScoreQueryBuilder getBuilderPrototype() {
|
|
||||||
return PROTOTYPE;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -48,7 +48,7 @@ public abstract class ScoreFunctionBuilder<FB extends ScoreFunctionBuilder> impl
|
||||||
|
|
||||||
protected void buildWeight(XContentBuilder builder) throws IOException {
|
protected void buildWeight(XContentBuilder builder) throws IOException {
|
||||||
if (weight != null) {
|
if (weight != null) {
|
||||||
builder.field(FunctionScoreQueryParser.WEIGHT_FIELD.getPreferredName(), weight);
|
builder.field(FunctionScoreQueryBuilder.WEIGHT_FIELD.getPreferredName(), weight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ public interface ScoreFunctionParser<FB extends ScoreFunctionBuilder<FB>> {
|
||||||
/**
|
/**
|
||||||
* Returns the name of the function, for example "linear", "gauss" etc. This
|
* Returns the name of the function, for example "linear", "gauss" etc. This
|
||||||
* name is used for registering the parser in
|
* name is used for registering the parser in
|
||||||
* {@link FunctionScoreQueryParser}.
|
* {@link FunctionScoreQueryBuilder}.
|
||||||
* */
|
* */
|
||||||
String[] getNames();
|
String[] getNames();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.functionscore;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.elasticsearch.common.ParsingException;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentLocation;
|
|
||||||
|
|
||||||
import static java.util.Collections.unmodifiableMap;
|
|
||||||
|
|
||||||
public class ScoreFunctionParserMapper {
|
|
||||||
|
|
||||||
protected Map<String, ScoreFunctionParser<?>> functionParsers;
|
|
||||||
|
|
||||||
public ScoreFunctionParserMapper(Map<String, ScoreFunctionParser<?>> functionParsers) {
|
|
||||||
this.functionParsers = unmodifiableMap(functionParsers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScoreFunctionParser<?> get(XContentLocation contentLocation, String parserName) {
|
|
||||||
ScoreFunctionParser<?> functionParser = get(parserName);
|
|
||||||
if (functionParser == null) {
|
|
||||||
throw new ParsingException(contentLocation, "No function with the name [" + parserName + "] is registered.");
|
|
||||||
}
|
|
||||||
return functionParser;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ScoreFunctionParser<?> get(String parserName) {
|
|
||||||
return functionParsers.get(parserName);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,12 +20,15 @@
|
||||||
package org.elasticsearch.search;
|
package org.elasticsearch.search;
|
||||||
|
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.geo.ShapesAvailability;
|
import org.elasticsearch.common.geo.ShapesAvailability;
|
||||||
import org.elasticsearch.common.geo.builders.ShapeBuilders;
|
import org.elasticsearch.common.geo.builders.ShapeBuilders;
|
||||||
import org.elasticsearch.common.inject.AbstractModule;
|
import org.elasticsearch.common.inject.AbstractModule;
|
||||||
import org.elasticsearch.common.inject.multibindings.Multibinder;
|
import org.elasticsearch.common.inject.multibindings.Multibinder;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteable;
|
import org.elasticsearch.common.io.stream.NamedWriteable;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable.Reader;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.index.percolator.PercolatorHighlightSubFetchPhase;
|
import org.elasticsearch.index.percolator.PercolatorHighlightSubFetchPhase;
|
||||||
import org.elasticsearch.index.query.BoolQueryParser;
|
import org.elasticsearch.index.query.BoolQueryParser;
|
||||||
|
@ -49,7 +52,7 @@ import org.elasticsearch.index.query.IdsQueryParser;
|
||||||
import org.elasticsearch.index.query.IndicesQueryParser;
|
import org.elasticsearch.index.query.IndicesQueryParser;
|
||||||
import org.elasticsearch.index.query.MatchAllQueryParser;
|
import org.elasticsearch.index.query.MatchAllQueryParser;
|
||||||
import org.elasticsearch.index.query.MatchNoneQueryParser;
|
import org.elasticsearch.index.query.MatchNoneQueryParser;
|
||||||
import org.elasticsearch.index.query.MatchQueryParser;
|
import org.elasticsearch.index.query.MatchQueryBuilder;
|
||||||
import org.elasticsearch.index.query.MoreLikeThisQueryParser;
|
import org.elasticsearch.index.query.MoreLikeThisQueryParser;
|
||||||
import org.elasticsearch.index.query.MultiMatchQueryParser;
|
import org.elasticsearch.index.query.MultiMatchQueryParser;
|
||||||
import org.elasticsearch.index.query.NestedQueryParser;
|
import org.elasticsearch.index.query.NestedQueryParser;
|
||||||
|
@ -57,6 +60,7 @@ import org.elasticsearch.index.query.ParentIdQueryParser;
|
||||||
import org.elasticsearch.index.query.PercolatorQueryParser;
|
import org.elasticsearch.index.query.PercolatorQueryParser;
|
||||||
import org.elasticsearch.index.query.PrefixQueryParser;
|
import org.elasticsearch.index.query.PrefixQueryParser;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.QueryParser;
|
import org.elasticsearch.index.query.QueryParser;
|
||||||
import org.elasticsearch.index.query.QueryStringQueryParser;
|
import org.elasticsearch.index.query.QueryStringQueryParser;
|
||||||
import org.elasticsearch.index.query.RangeQueryParser;
|
import org.elasticsearch.index.query.RangeQueryParser;
|
||||||
|
@ -77,10 +81,9 @@ import org.elasticsearch.index.query.TermsQueryParser;
|
||||||
import org.elasticsearch.index.query.TypeQueryParser;
|
import org.elasticsearch.index.query.TypeQueryParser;
|
||||||
import org.elasticsearch.index.query.WildcardQueryParser;
|
import org.elasticsearch.index.query.WildcardQueryParser;
|
||||||
import org.elasticsearch.index.query.WrapperQueryParser;
|
import org.elasticsearch.index.query.WrapperQueryParser;
|
||||||
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryParser;
|
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
|
||||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
|
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilder;
|
||||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
|
import org.elasticsearch.index.query.functionscore.ScoreFunctionParser;
|
||||||
import org.elasticsearch.index.query.functionscore.ScoreFunctionParserMapper;
|
|
||||||
import org.elasticsearch.index.query.functionscore.exp.ExponentialDecayFunctionParser;
|
import org.elasticsearch.index.query.functionscore.exp.ExponentialDecayFunctionParser;
|
||||||
import org.elasticsearch.index.query.functionscore.fieldvaluefactor.FieldValueFactorFunctionParser;
|
import org.elasticsearch.index.query.functionscore.fieldvaluefactor.FieldValueFactorFunctionParser;
|
||||||
import org.elasticsearch.index.query.functionscore.gauss.GaussDecayFunctionParser;
|
import org.elasticsearch.index.query.functionscore.gauss.GaussDecayFunctionParser;
|
||||||
|
@ -224,14 +227,6 @@ import org.elasticsearch.search.sort.ScriptSortBuilder;
|
||||||
import org.elasticsearch.search.sort.SortBuilder;
|
import org.elasticsearch.search.sort.SortBuilder;
|
||||||
import org.elasticsearch.search.suggest.Suggester;
|
import org.elasticsearch.search.suggest.Suggester;
|
||||||
import org.elasticsearch.search.suggest.Suggesters;
|
import org.elasticsearch.search.suggest.Suggesters;
|
||||||
import org.elasticsearch.search.suggest.SuggestionBuilder;
|
|
||||||
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
|
|
||||||
import org.elasticsearch.search.suggest.phrase.Laplace;
|
|
||||||
import org.elasticsearch.search.suggest.phrase.LinearInterpolation;
|
|
||||||
import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder;
|
|
||||||
import org.elasticsearch.search.suggest.phrase.SmoothingModel;
|
|
||||||
import org.elasticsearch.search.suggest.phrase.StupidBackoff;
|
|
||||||
import org.elasticsearch.search.suggest.term.TermSuggestionBuilder;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -260,6 +255,7 @@ public class SearchModule extends AbstractModule {
|
||||||
* at configure time because they depend on things that are registered by
|
* at configure time because they depend on things that are registered by
|
||||||
* plugins (function score parsers).
|
* plugins (function score parsers).
|
||||||
*/
|
*/
|
||||||
|
private final List<QueryRegistration<?>> queries = new ArrayList<>();
|
||||||
private final List<Supplier<QueryParser<?>>> queryParsers = new ArrayList<>();
|
private final List<Supplier<QueryParser<?>>> queryParsers = new ArrayList<>();
|
||||||
private final Set<Class<? extends FetchSubPhase>> fetchSubPhases = new HashSet<>();
|
private final Set<Class<? extends FetchSubPhase>> fetchSubPhases = new HashSet<>();
|
||||||
private final Set<SignificanceHeuristicParser> heuristicParsers = new HashSet<>();
|
private final Set<SignificanceHeuristicParser> heuristicParsers = new HashSet<>();
|
||||||
|
@ -304,6 +300,23 @@ public class SearchModule extends AbstractModule {
|
||||||
namedWriteableRegistry.registerPrototype(ScoreFunctionBuilder.class, sfb);
|
namedWriteableRegistry.registerPrototype(ScoreFunctionBuilder.class, sfb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a query.
|
||||||
|
*
|
||||||
|
* @param reader the reader registered for this query's builder. Typically a reference to a constructor that takes a
|
||||||
|
* {@link org.elasticsearch.common.io.stream.StreamInput}
|
||||||
|
* @param parser the parser the reads the query builder from xcontent
|
||||||
|
* @param names all names by which this query might be parsed. The first name is special as it is the name by under which the reader is
|
||||||
|
* registered. So it is the name that the query should use as its {@link NamedWriteable#getWriteableName()}.
|
||||||
|
*/
|
||||||
|
public <QB extends QueryBuilder<QB>> void registerQuery(Writeable.Reader<QB> reader, QueryParser<QB> parser, String... names) {
|
||||||
|
queries.add(new QueryRegistration<QB>(names, reader, parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a query.
|
||||||
|
* TODO remove this in favor of registerQuery
|
||||||
|
*/
|
||||||
public void registerQueryParser(Supplier<QueryParser<?>> parser) {
|
public void registerQueryParser(Supplier<QueryParser<?>> parser) {
|
||||||
queryParsers.add(parser);
|
queryParsers.add(parser);
|
||||||
}
|
}
|
||||||
|
@ -364,16 +377,28 @@ public class SearchModule extends AbstractModule {
|
||||||
|
|
||||||
public IndicesQueriesRegistry buildQueryParserRegistry() {
|
public IndicesQueriesRegistry buildQueryParserRegistry() {
|
||||||
Map<String, QueryParser<?>> queryParsersMap = new HashMap<>();
|
Map<String, QueryParser<?>> queryParsersMap = new HashMap<>();
|
||||||
|
|
||||||
|
// TODO remove this when we retire registerQueryParser
|
||||||
for (Supplier<QueryParser<?>> parserSupplier : queryParsers) {
|
for (Supplier<QueryParser<?>> parserSupplier : queryParsers) {
|
||||||
QueryParser<? extends QueryBuilder> parser = parserSupplier.get();
|
QueryParser<? extends QueryBuilder<?>> parser = parserSupplier.get();
|
||||||
for (String name: parser.names()) {
|
for (String name: parser.names()) {
|
||||||
Object oldValue = queryParsersMap.putIfAbsent(name, parser);
|
Object oldValue = queryParsersMap.putIfAbsent(name, parser);
|
||||||
if (oldValue != null) {
|
if (oldValue != null) {
|
||||||
throw new IllegalArgumentException("Query parser [" + oldValue + "] already registered for name [" + name + "] while trying to register [" + parser + "]");
|
throw new IllegalArgumentException("Query parser [" + oldValue + "] already registered for name [" + name + "] while trying to register [" + parser + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked") NamedWriteable<? extends QueryBuilder> qb = parser.getBuilderPrototype();
|
namedWriteableRegistry.registerPrototype(QueryBuilder.class, parser.getBuilderPrototype());
|
||||||
namedWriteableRegistry.registerPrototype(QueryBuilder.class, qb);
|
}
|
||||||
|
|
||||||
|
for (QueryRegistration<?> query : queries) {
|
||||||
|
QueryParser<? extends QueryBuilder<?>> parser = query.parser;
|
||||||
|
for (String name: query.names) {
|
||||||
|
Object oldValue = queryParsersMap.putIfAbsent(name, parser);
|
||||||
|
if (oldValue != null) {
|
||||||
|
throw new IllegalArgumentException("Query parser [" + oldValue + "] already registered for name [" + name + "] while trying to register [" + parser + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
namedWriteableRegistry.register(QueryBuilder.class, query.names[0], query.reader);
|
||||||
}
|
}
|
||||||
return new IndicesQueriesRegistry(settings, queryParsersMap);
|
return new IndicesQueriesRegistry(settings, queryParsersMap);
|
||||||
}
|
}
|
||||||
|
@ -488,7 +513,8 @@ public class SearchModule extends AbstractModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerBuiltinQueryParsers() {
|
private void registerBuiltinQueryParsers() {
|
||||||
registerQueryParser(MatchQueryParser::new);
|
registerQuery(MatchQueryBuilder.PROTOTYPE::readFrom, MatchQueryBuilder::fromXContent, MatchQueryBuilder.NAME,
|
||||||
|
"match_phrase", "matchPhrase", "match_phrase_prefix", "matchPhrasePrefix", "matchFuzzy", "match_fuzzy", "fuzzy_match");
|
||||||
registerQueryParser(MultiMatchQueryParser::new);
|
registerQueryParser(MultiMatchQueryParser::new);
|
||||||
registerQueryParser(NestedQueryParser::new);
|
registerQueryParser(NestedQueryParser::new);
|
||||||
registerQueryParser(HasChildQueryParser::new);
|
registerQueryParser(HasChildQueryParser::new);
|
||||||
|
@ -498,7 +524,8 @@ public class SearchModule extends AbstractModule {
|
||||||
registerQueryParser(MatchAllQueryParser::new);
|
registerQueryParser(MatchAllQueryParser::new);
|
||||||
registerQueryParser(QueryStringQueryParser::new);
|
registerQueryParser(QueryStringQueryParser::new);
|
||||||
registerQueryParser(BoostingQueryParser::new);
|
registerQueryParser(BoostingQueryParser::new);
|
||||||
BooleanQuery.setMaxClauseCount(settings.getAsInt("index.query.bool.max_clause_count", settings.getAsInt("indices.query.bool.max_clause_count", BooleanQuery.getMaxClauseCount())));
|
BooleanQuery.setMaxClauseCount(settings.getAsInt("index.query.bool.max_clause_count",
|
||||||
|
settings.getAsInt("indices.query.bool.max_clause_count", BooleanQuery.getMaxClauseCount())));
|
||||||
registerQueryParser(BoolQueryParser::new);
|
registerQueryParser(BoolQueryParser::new);
|
||||||
registerQueryParser(TermQueryParser::new);
|
registerQueryParser(TermQueryParser::new);
|
||||||
registerQueryParser(TermsQueryParser::new);
|
registerQueryParser(TermsQueryParser::new);
|
||||||
|
@ -521,8 +548,10 @@ public class SearchModule extends AbstractModule {
|
||||||
registerQueryParser(IndicesQueryParser::new);
|
registerQueryParser(IndicesQueryParser::new);
|
||||||
registerQueryParser(CommonTermsQueryParser::new);
|
registerQueryParser(CommonTermsQueryParser::new);
|
||||||
registerQueryParser(SpanMultiTermQueryParser::new);
|
registerQueryParser(SpanMultiTermQueryParser::new);
|
||||||
// This is delayed until configure time to give plugins a chance to register parsers
|
QueryParser<FunctionScoreQueryBuilder> functionScoreParser = (QueryParseContext c) -> FunctionScoreQueryBuilder
|
||||||
registerQueryParser(() -> new FunctionScoreQueryParser(new ScoreFunctionParserMapper(functionScoreParsers)));
|
.fromXContent((String name) -> functionScoreParsers.get(name), c);
|
||||||
|
registerQuery(FunctionScoreQueryBuilder.PROTOTYPE::readFrom, functionScoreParser, FunctionScoreQueryBuilder.NAME,
|
||||||
|
Strings.toCamelCase(FunctionScoreQueryBuilder.NAME));
|
||||||
registerQueryParser(SimpleQueryStringParser::new);
|
registerQueryParser(SimpleQueryStringParser::new);
|
||||||
registerQueryParser(TemplateQueryParser::new);
|
registerQueryParser(TemplateQueryParser::new);
|
||||||
registerQueryParser(TypeQueryParser::new);
|
registerQueryParser(TypeQueryParser::new);
|
||||||
|
@ -609,4 +638,16 @@ public class SearchModule extends AbstractModule {
|
||||||
public Suggesters getSuggesters() {
|
public Suggesters getSuggesters() {
|
||||||
return suggesters;
|
return suggesters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class QueryRegistration<QB extends QueryBuilder<QB>> {
|
||||||
|
private final String[] names;
|
||||||
|
private final Writeable.Reader<QB> reader;
|
||||||
|
private final QueryParser<QB> parser;
|
||||||
|
|
||||||
|
private QueryRegistration(String[] names, Reader<QB> reader, QueryParser<QB> parser) {
|
||||||
|
this.names = names;
|
||||||
|
this.reader = reader;
|
||||||
|
this.parser = parser;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -637,12 +637,9 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected <QB extends QueryBuilder> QB assertSerialization(QB testQuery) throws IOException {
|
protected <QB extends QueryBuilder> QB assertSerialization(QB testQuery) throws IOException {
|
||||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||||
testQuery.writeTo(output);
|
output.writeQuery(testQuery);
|
||||||
try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) {
|
try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) {
|
||||||
QueryParser<?> queryParser = queryParser(testQuery.getName());
|
QueryBuilder<?> deserializedQuery = in.readQuery();
|
||||||
assertNotNull("queryparser not found for query: [" + testQuery.getName() + "]", queryParser);
|
|
||||||
QueryBuilder<?> prototype = queryParser.getBuilderPrototype();
|
|
||||||
QueryBuilder<?> deserializedQuery = prototype.readFrom(in);
|
|
||||||
assertEquals(deserializedQuery, testQuery);
|
assertEquals(deserializedQuery, testQuery);
|
||||||
assertEquals(deserializedQuery.hashCode(), testQuery.hashCode());
|
assertEquals(deserializedQuery.hashCode(), testQuery.hashCode());
|
||||||
assertNotSame(deserializedQuery, testQuery);
|
assertNotSame(deserializedQuery, testQuery);
|
||||||
|
@ -685,38 +682,13 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private QueryParser<?> queryParser(String queryId) {
|
|
||||||
QueryParser<?> queryParser = indicesQueriesRegistry.queryParsers().get(queryId);
|
|
||||||
if (queryParser == null && EmptyQueryBuilder.NAME.equals(queryId)) {
|
|
||||||
return new QueryParser() {
|
|
||||||
@Override
|
|
||||||
public String[] names() {
|
|
||||||
return new String[]{EmptyQueryBuilder.NAME};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public QueryBuilder<?> fromXContent(QueryParseContext parseContext) throws IOException {
|
|
||||||
return new EmptyQueryBuilder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public QueryBuilder getBuilderPrototype() {
|
|
||||||
return EmptyQueryBuilder.PROTOTYPE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return queryParser;
|
|
||||||
}
|
|
||||||
|
|
||||||
//we use the streaming infra to create a copy of the query provided as argument
|
//we use the streaming infra to create a copy of the query provided as argument
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
protected QB copyQuery(QB query) throws IOException {
|
protected QB copyQuery(QB query) throws IOException {
|
||||||
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
try (BytesStreamOutput output = new BytesStreamOutput()) {
|
||||||
query.writeTo(output);
|
output.writeQuery(query);
|
||||||
try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) {
|
try (StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(output.bytes()), namedWriteableRegistry)) {
|
||||||
QueryBuilder<?> prototype = queryParser(query.getName()).getBuilderPrototype();
|
return (QB) in.readQuery();
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
QB secondQuery = (QB) prototype.readFrom(in);
|
|
||||||
return secondQuery;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,7 @@ public abstract class AbstractSortTestCase<T extends SortBuilder<T>> extends EST
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
namedWriteableRegistry = null;
|
namedWriteableRegistry = null;
|
||||||
|
indicesQueriesRegistry = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns random sort that is put under test */
|
/** Returns random sort that is put under test */
|
||||||
|
|
|
@ -57,6 +57,7 @@ public class SortBuilderTests extends ESTestCase {
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClass() throws Exception {
|
public static void afterClass() throws Exception {
|
||||||
namedWriteableRegistry = null;
|
namedWriteableRegistry = null;
|
||||||
|
indicesQueriesRegistry = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue