Fix alias field resolution in match query (#47369)

Synonym queries (when two tokens/paths start at the same position) use the alias field instead
of the concrete field to build Lucene queries. This commit fixes this bug by resolving the alias field upfront in order to provide the concrete field to the actual query parser.
This commit is contained in:
Jim Ferenczi 2019-10-02 09:54:10 +02:00 committed by jimczi
parent 033aa9cf9b
commit 42c5054e52
4 changed files with 32 additions and 10 deletions

View File

@ -62,6 +62,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import static org.elasticsearch.common.lucene.search.Queries.newLenientFieldQuery;
@ -248,6 +249,14 @@ public class MatchQuery {
if (fieldType == null) {
return newUnmappedFieldQuery(fieldName);
}
Set<String> fields = context.simpleMatchToIndexNames(fieldName);
if (fields.contains(fieldName)) {
assert fields.size() == 1;
// this field is a concrete field or an alias so we use the
// field type name directly
fieldName = fieldType.name();
}
Analyzer analyzer = getAnalyzer(fieldType, type == Type.PHRASE || type == Type.PHRASE_PREFIX);
assert analyzer != null;
@ -261,9 +270,9 @@ public class MatchQuery {
*/
if (analyzer == Lucene.KEYWORD_ANALYZER && type != Type.PHRASE_PREFIX) {
final Term term = new Term(fieldName, value.toString());
if ((fieldType instanceof TextFieldMapper.TextFieldType || fieldType instanceof KeywordFieldMapper.KeywordFieldType)
&& type == Type.BOOLEAN_PREFIX) {
return builder.newPrefixQuery(fieldName, term);
if (type == Type.BOOLEAN_PREFIX
&& (fieldType instanceof TextFieldMapper.TextFieldType || fieldType instanceof KeywordFieldMapper.KeywordFieldType)) {
return builder.newPrefixQuery(term);
} else {
return builder.newTermQuery(term);
}
@ -581,12 +590,12 @@ public class MatchQuery {
/**
* Builds a new prefix query instance.
*/
protected Query newPrefixQuery(String field, Term term) {
protected Query newPrefixQuery(Term term) {
try {
return fieldType.prefixQuery(term.text(), null, context);
} catch (RuntimeException e) {
if (lenient) {
return newLenientFieldQuery(field, e);
return newLenientFieldQuery(term.field(), e);
}
throw e;
}
@ -603,7 +612,7 @@ public class MatchQuery {
final Term term = new Term(field, termAtt.getBytesRef());
int lastOffset = offsetAtt.endOffset();
stream.end();
return isPrefix && lastOffset == offsetAtt.endOffset() ? newPrefixQuery(field, term) : newTermQuery(term);
return isPrefix && lastOffset == offsetAtt.endOffset() ? newPrefixQuery(term) : newTermQuery(term);
}
private void add(BooleanQuery.Builder q, String field, List<Term> current, BooleanClause.Occur operator, boolean isPrefix) {
@ -612,7 +621,7 @@ public class MatchQuery {
}
if (current.size() == 1) {
if (isPrefix) {
q.add(newPrefixQuery(field, current.get(0)), operator);
q.add(newPrefixQuery(current.get(0)), operator);
} else {
q.add(newTermQuery(current.get(0)), operator);
}
@ -729,7 +738,7 @@ public class MatchQuery {
Term[] terms = graph.getTerms(field, start);
assert terms.length > 0;
if (terms.length == 1) {
queryPos = usePrefix ? newPrefixQuery(field, terms[0]) : newTermQuery(terms[0]);
queryPos = usePrefix ? newPrefixQuery(terms[0]) : newTermQuery(terms[0]);
} else {
// We don't apply prefix on synonyms
queryPos = newSynonymQuery(terms);

View File

@ -194,7 +194,7 @@ public class MultiMatchQuery extends MatchQuery {
}
@Override
protected Query newPrefixQuery(String field, Term term) {
protected Query newPrefixQuery(Term term) {
List<Query> disjunctions = new ArrayList<>();
for (FieldAndBoost fieldType : blendedFields) {
Query query = fieldType.fieldType.prefixQuery(term.text(), null, context);

View File

@ -52,7 +52,7 @@ public final class QueryParserHelper {
float boost = 1.0f;
if (boostIndex != -1) {
fieldName = field.substring(0, boostIndex);
boost = Float.parseFloat(field.substring(boostIndex+1, field.length()));
boost = Float.parseFloat(field.substring(boostIndex+1));
} else {
fieldName = field;
}

View File

@ -32,6 +32,7 @@ import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.PointRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SynonymQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanOrQuery;
@ -488,6 +489,18 @@ public class MatchQueryBuilderTests extends AbstractQueryTestCase<MatchQueryBuil
assertEquals(expected, actual);
}
public void testAliasWithSynonyms() throws Exception {
final MatchQuery matchQuery = new MatchQuery(createShardContext());
matchQuery.setAnalyzer(new MockSynonymAnalyzer());
final Query actual = matchQuery.parse(Type.PHRASE, STRING_ALIAS_FIELD_NAME, "dogs");
Query expected = new SynonymQuery.Builder(STRING_FIELD_NAME)
.addTerm(new Term(STRING_FIELD_NAME, "dogs"))
.addTerm(new Term(STRING_FIELD_NAME, "dog"))
.build();
assertEquals(expected, actual);
}
public void testMaxBooleanClause() {
MatchQuery query = new MatchQuery(createShardContext());
query.setAnalyzer(new MockGraphAnalyzer(createGiantGraph(40)));