Throw parsing error if match query contains multiple fields

Match Query, like many other queries, used to parse even when the query referred to multiple fields and the first one would win. We rather throw an exception now instead.
Also added test for short prefix query variant and modified the parsing code to consume the whole query object.
This commit is contained in:
javanna 2016-08-03 19:30:37 +02:00 committed by Luca Cavanna
parent f7b3dce4bc
commit 1e45fd5850
2 changed files with 108 additions and 74 deletions

View File

@ -510,13 +510,7 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
public static Optional<MatchQueryBuilder> fromXContent(QueryParseContext parseContext) throws IOException { public static Optional<MatchQueryBuilder> fromXContent(QueryParseContext parseContext) throws IOException {
XContentParser parser = parseContext.parser(); XContentParser parser = parseContext.parser();
String fieldName = null;
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();
MatchQuery.Type type = MatchQuery.Type.BOOLEAN; MatchQuery.Type type = MatchQuery.Type.BOOLEAN;
Object value = null; Object value = null;
float boost = AbstractQueryBuilder.DEFAULT_BOOST; float boost = AbstractQueryBuilder.DEFAULT_BOOST;
@ -533,80 +527,84 @@ public class MatchQueryBuilder extends AbstractQueryBuilder<MatchQueryBuilder> {
Float cutOffFrequency = null; Float cutOffFrequency = null;
ZeroTermsQuery zeroTermsQuery = MatchQuery.DEFAULT_ZERO_TERMS_QUERY; ZeroTermsQuery zeroTermsQuery = MatchQuery.DEFAULT_ZERO_TERMS_QUERY;
String queryName = null; String queryName = null;
String currentFieldName = null;
token = parser.nextToken(); XContentParser.Token token;
if (token == XContentParser.Token.START_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
String currentFieldName = null; if (token == XContentParser.Token.FIELD_NAME) {
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { currentFieldName = parser.currentName();
if (token == XContentParser.Token.FIELD_NAME) { } else if (parseContext.isDeprecatedSetting(currentFieldName)) {
currentFieldName = parser.currentName(); // skip
} else if (token.isValue()) { } else if (token == XContentParser.Token.START_OBJECT) {
if (parseContext.getParseFieldMatcher().match(currentFieldName, QUERY_FIELD)) { if (fieldName != null) {
value = parser.objectText(); throw new ParsingException(parser.getTokenLocation(), "[match] query doesn't support multiple fields, found ["
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, TYPE_FIELD)) { + fieldName + "] and [" + currentFieldName + "]");
String tStr = parser.text(); }
if ("boolean".equals(tStr)) { fieldName = currentFieldName;
type = MatchQuery.Type.BOOLEAN; while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
} else if ("phrase".equals(tStr)) { if (token == XContentParser.Token.FIELD_NAME) {
type = MatchQuery.Type.PHRASE; currentFieldName = parser.currentName();
} else if ("phrase_prefix".equals(tStr) || ("phrasePrefix".equals(tStr))) { } else if (token.isValue()) {
type = MatchQuery.Type.PHRASE_PREFIX; if (parseContext.getParseFieldMatcher().match(currentFieldName, QUERY_FIELD)) {
} else { value = parser.objectText();
throw new ParsingException(parser.getTokenLocation(), "[" + NAME + "] query does not support type " + tStr); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
} String tStr = parser.text();
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, ANALYZER_FIELD)) { if ("boolean".equals(tStr)) {
analyzer = parser.text(); type = MatchQuery.Type.BOOLEAN;
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) { } else if ("phrase".equals(tStr)) {
boost = parser.floatValue(); type = MatchQuery.Type.PHRASE;
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, SLOP_FIELD)) { } else if ("phrase_prefix".equals(tStr) || ("phrasePrefix".equals(tStr))) {
slop = parser.intValue(); type = MatchQuery.Type.PHRASE_PREFIX;
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, Fuzziness.FIELD)) { } else {
fuzziness = Fuzziness.parse(parser); throw new ParsingException(parser.getTokenLocation(), "[" + NAME + "] query does not support type " + tStr);
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, PREFIX_LENGTH_FIELD)) { }
prefixLength = parser.intValue(); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, ANALYZER_FIELD)) {
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, MAX_EXPANSIONS_FIELD)) { analyzer = parser.text();
maxExpansion = parser.intValue(); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.BOOST_FIELD)) {
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, OPERATOR_FIELD)) { boost = parser.floatValue();
operator = Operator.fromString(parser.text()); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, SLOP_FIELD)) {
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, MINIMUM_SHOULD_MATCH_FIELD)) { slop = parser.intValue();
minimumShouldMatch = parser.textOrNull(); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, Fuzziness.FIELD)) {
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, FUZZY_REWRITE_FIELD)) { fuzziness = Fuzziness.parse(parser);
fuzzyRewrite = parser.textOrNull(); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, PREFIX_LENGTH_FIELD)) {
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, FUZZY_TRANSPOSITIONS_FIELD)) { prefixLength = parser.intValue();
fuzzyTranspositions = parser.booleanValue(); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, MAX_EXPANSIONS_FIELD)) {
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, LENIENT_FIELD)) { maxExpansion = parser.intValue();
lenient = parser.booleanValue(); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, OPERATOR_FIELD)) {
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, CUTOFF_FREQUENCY_FIELD)) { operator = Operator.fromString(parser.text());
cutOffFrequency = parser.floatValue(); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, MINIMUM_SHOULD_MATCH_FIELD)) {
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, ZERO_TERMS_QUERY_FIELD)) { minimumShouldMatch = parser.textOrNull();
String zeroTermsDocs = parser.text(); } else if (parseContext.getParseFieldMatcher().match(currentFieldName, FUZZY_REWRITE_FIELD)) {
if ("none".equalsIgnoreCase(zeroTermsDocs)) { fuzzyRewrite = parser.textOrNull();
zeroTermsQuery = MatchQuery.ZeroTermsQuery.NONE; } else if (parseContext.getParseFieldMatcher().match(currentFieldName, FUZZY_TRANSPOSITIONS_FIELD)) {
} else if ("all".equalsIgnoreCase(zeroTermsDocs)) { fuzzyTranspositions = parser.booleanValue();
zeroTermsQuery = MatchQuery.ZeroTermsQuery.ALL; } else if (parseContext.getParseFieldMatcher().match(currentFieldName, LENIENT_FIELD)) {
lenient = parser.booleanValue();
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, CUTOFF_FREQUENCY_FIELD)) {
cutOffFrequency = parser.floatValue();
} else if (parseContext.getParseFieldMatcher().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.getParseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
queryName = parser.text();
} else { } else {
throw new ParsingException(parser.getTokenLocation(), throw new ParsingException(parser.getTokenLocation(),
"Unsupported zero_terms_docs value [" + zeroTermsDocs + "]"); "[" + NAME + "] query does not support [" + currentFieldName + "]");
} }
} else if (parseContext.getParseFieldMatcher().match(currentFieldName, AbstractQueryBuilder.NAME_FIELD)) {
queryName = parser.text();
} else { } else {
throw new ParsingException(parser.getTokenLocation(), throw new ParsingException(parser.getTokenLocation(),
"[" + NAME + "] query does not support [" + currentFieldName + "]"); "[" + NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]");
} }
} else {
throw new ParsingException(parser.getTokenLocation(),
"[" + NAME + "] unknown token [" + token + "] after [" + currentFieldName + "]");
} }
} } else {
parser.nextToken(); fieldName = parser.currentName();
} else { value = parser.objectText();
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?");
} }
} }

View File

@ -29,6 +29,7 @@ import org.apache.lucene.search.PointRangeQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermQuery;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.lucene.search.MatchNoDocsQuery; import org.elasticsearch.common.lucene.search.MatchNoDocsQuery;
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.lucene.search.Queries;
@ -40,7 +41,9 @@ import org.elasticsearch.test.AbstractQueryTestCase;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import static org.hamcrest.CoreMatchers.either; import static org.hamcrest.CoreMatchers.either;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
@ -118,6 +121,19 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
return matchQuery; return matchQuery;
} }
@Override
protected Map<String, MatchQueryBuilder> getAlternateVersions() {
Map<String, MatchQueryBuilder> alternateVersions = new HashMap<>();
MatchQueryBuilder matchQuery = new MatchQueryBuilder(randomAsciiOfLengthBetween(1, 10), randomAsciiOfLengthBetween(1, 10));
String contentString = "{\n" +
" \"match\" : {\n" +
" \"" + matchQuery.fieldName() + "\" : \"" + matchQuery.value() + "\"\n" +
" }\n" +
"}";
alternateVersions.put(contentString, matchQuery);
return alternateVersions;
}
@Override @Override
protected void doAssertLuceneQuery(MatchQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException { protected void doAssertLuceneQuery(MatchQueryBuilder queryBuilder, Query query, QueryShardContext context) throws IOException {
assertThat(query, notNullValue()); assertThat(query, notNullValue());
@ -406,4 +422,24 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
query.lenient(true); query.lenient(true);
query.toQuery(context); // no exception query.toQuery(context); // no exception
} }
public void testParseFailsWithMultipleFields() throws IOException {
String json = "{\n" +
" \"match\" : {\n" +
" \"message1\" : {\n" +
" \"query\" : \"this is a test\"\n" +
" },\n" +
" \"message2\" : {\n" +
" \"query\" : \"this is a test\"\n" +
" }\n" +
" }\n" +
"}";
try {
parseQuery(json);
fail("parseQuery should have failed");
} catch(ParsingException e) {
assertEquals("[match] query doesn't support multiple fields, found [message1] and [message2]", e.getMessage());
}
}
} }