Handle leniency for cross_fields type in multi_match query (#27045)

This commit is contained in:
Alexander Kazakov 2017-10-19 11:29:28 +03:00 committed by Jim Ferenczi
parent 8a05e5b92c
commit 9a3a1cd1b7
3 changed files with 39 additions and 23 deletions

View File

@ -43,6 +43,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.elasticsearch.common.lucene.search.Queries.newLenientFieldQuery;
public class MultiMatchQuery extends MatchQuery {
private Float groupTieBreaker = null;
@ -204,7 +206,7 @@ public class MultiMatchQuery extends MatchQuery {
for (int i = 0; i < terms.length; i++) {
values[i] = terms[i].bytes();
}
return MultiMatchQuery.blendTerms(context, values, commonTermsCutoff, tieBreaker, blendedFields);
return MultiMatchQuery.blendTerms(context, values, commonTermsCutoff, tieBreaker, lenient, blendedFields);
}
@Override
@ -212,7 +214,7 @@ public class MultiMatchQuery extends MatchQuery {
if (blendedFields == null) {
return super.blendTerm(term, fieldType);
}
return MultiMatchQuery.blendTerm(context, term.bytes(), commonTermsCutoff, tieBreaker, blendedFields);
return MultiMatchQuery.blendTerm(context, term.bytes(), commonTermsCutoff, tieBreaker, lenient, blendedFields);
}
@Override
@ -227,12 +229,12 @@ public class MultiMatchQuery extends MatchQuery {
}
static Query blendTerm(QueryShardContext context, BytesRef value, Float commonTermsCutoff, float tieBreaker,
FieldAndFieldType... blendedFields) {
return blendTerms(context, new BytesRef[] {value}, commonTermsCutoff, tieBreaker, blendedFields);
boolean lenient, FieldAndFieldType... blendedFields) {
return blendTerms(context, new BytesRef[] {value}, commonTermsCutoff, tieBreaker, lenient, blendedFields);
}
static Query blendTerms(QueryShardContext context, BytesRef[] values, Float commonTermsCutoff, float tieBreaker,
FieldAndFieldType... blendedFields) {
boolean lenient, FieldAndFieldType... blendedFields) {
List<Query> queries = new ArrayList<>();
Term[] terms = new Term[blendedFields.length * values.length];
float[] blendedBoost = new float[blendedFields.length * values.length];
@ -242,19 +244,12 @@ public class MultiMatchQuery extends MatchQuery {
Query query;
try {
query = ft.fieldType.termQuery(term, context);
} catch (IllegalArgumentException e) {
// the query expects a certain class of values such as numbers
// of ip addresses and the value can't be parsed, so ignore this
// field
continue;
} catch (ElasticsearchParseException parseException) {
// date fields throw an ElasticsearchParseException with the
// underlying IAE as the cause, ignore this field if that is
// the case
if (parseException.getCause() instanceof IllegalArgumentException) {
continue;
} catch (RuntimeException e) {
if (lenient) {
query = newLenientFieldQuery(ft.fieldType.name(), e);
} else {
throw e;
}
throw parseException;
}
float boost = ft.boost;
while (query instanceof BoostQuery) {

View File

@ -30,6 +30,7 @@ import org.apache.lucene.search.SynonymQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexService;
import org.elasticsearch.index.engine.Engine;
@ -110,7 +111,7 @@ public class MultiMatchQueryTests extends ESSingleNodeTestCase {
Query expected = BlendedTermQuery.dismaxBlendedQuery(terms, boosts, 1.0f);
Query actual = MultiMatchQuery.blendTerm(
indexService.newQueryShardContext(randomInt(20), null, () -> { throw new UnsupportedOperationException(); }, null),
new BytesRef("baz"), null, 1f, new FieldAndFieldType(ft1, 2), new FieldAndFieldType(ft2, 3));
new BytesRef("baz"), null, 1f, false, new FieldAndFieldType(ft1, 2), new FieldAndFieldType(ft2, 3));
assertEquals(expected, actual);
}
@ -126,11 +127,11 @@ public class MultiMatchQueryTests extends ESSingleNodeTestCase {
Query expected = BlendedTermQuery.dismaxBlendedQuery(terms, boosts, 1.0f);
Query actual = MultiMatchQuery.blendTerm(
indexService.newQueryShardContext(randomInt(20), null, () -> { throw new UnsupportedOperationException(); }, null),
new BytesRef("baz"), null, 1f, new FieldAndFieldType(ft1, 2), new FieldAndFieldType(ft2, 3));
new BytesRef("baz"), null, 1f, false, new FieldAndFieldType(ft1, 2), new FieldAndFieldType(ft2, 3));
assertEquals(expected, actual);
}
public void testBlendTermsUnsupportedValue() {
public void testBlendTermsUnsupportedValueWithLenient() {
FakeFieldType ft1 = new FakeFieldType();
ft1.setName("foo");
FakeFieldType ft2 = new FakeFieldType() {
@ -142,13 +143,29 @@ public class MultiMatchQueryTests extends ESSingleNodeTestCase {
ft2.setName("bar");
Term[] terms = new Term[] { new Term("foo", "baz") };
float[] boosts = new float[] {2};
Query expected = BlendedTermQuery.dismaxBlendedQuery(terms, boosts, 1.0f);
Query expected = new DisjunctionMaxQuery(Arrays.asList(
Queries.newMatchNoDocsQuery("failed [" + ft2.name() + "] query, caused by illegal_argument_exception:[null]"),
BlendedTermQuery.dismaxBlendedQuery(terms, boosts, 1.0f)
), 1f);
Query actual = MultiMatchQuery.blendTerm(
indexService.newQueryShardContext(randomInt(20), null, () -> { throw new UnsupportedOperationException(); }, null),
new BytesRef("baz"), null, 1f, new FieldAndFieldType(ft1, 2), new FieldAndFieldType(ft2, 3));
new BytesRef("baz"), null, 1f, true, new FieldAndFieldType(ft1, 2), new FieldAndFieldType(ft2, 3));
assertEquals(expected, actual);
}
public void testBlendTermsUnsupportedValueWithoutLenient() {
FakeFieldType ft = new FakeFieldType() {
@Override
public Query termQuery(Object value, QueryShardContext context) {
throw new IllegalArgumentException();
}
};
ft.setName("bar");
expectThrows(IllegalArgumentException.class, () -> MultiMatchQuery.blendTerm(
indexService.newQueryShardContext(randomInt(20), null, () -> { throw new UnsupportedOperationException(); }, null),
new BytesRef("baz"), null, 1f, false, new FieldAndFieldType(ft, 1)));
}
public void testBlendNoTermQuery() {
FakeFieldType ft1 = new FakeFieldType();
ft1.setName("foo");
@ -170,7 +187,7 @@ public class MultiMatchQueryTests extends ESSingleNodeTestCase {
), 1.0f);
Query actual = MultiMatchQuery.blendTerm(
indexService.newQueryShardContext(randomInt(20), null, () -> { throw new UnsupportedOperationException(); }, null),
new BytesRef("baz"), null, 1f, new FieldAndFieldType(ft1, 2), new FieldAndFieldType(ft2, 3));
new BytesRef("baz"), null, 1f, false, new FieldAndFieldType(ft1, 2), new FieldAndFieldType(ft2, 3));
assertEquals(expected, actual);
}

View File

@ -472,6 +472,7 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
.setQuery(randomizeType(multiMatchQuery("captain america 15", "full_name", "first_name", "last_name", "category", "skill")
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
.analyzer("category")
.lenient(true)
.operator(Operator.AND))).get();
assertHitCount(searchResponse, 1L);
assertFirstHit(searchResponse, hasId("theone"));
@ -480,6 +481,7 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
.setQuery(randomizeType(multiMatchQuery("captain america 15", "full_name", "first_name", "last_name", "category", "skill", "int-field")
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
.analyzer("category")
.lenient(true)
.operator(Operator.AND))).get();
assertHitCount(searchResponse, 1L);
assertFirstHit(searchResponse, hasId("theone"));
@ -488,6 +490,7 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
.setQuery(randomizeType(multiMatchQuery("captain america 15", "skill", "full_name", "first_name", "last_name", "category", "int-field")
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
.analyzer("category")
.lenient(true)
.operator(Operator.AND))).get();
assertHitCount(searchResponse, 1L);
assertFirstHit(searchResponse, hasId("theone"));
@ -496,6 +499,7 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
searchResponse = client().prepareSearch("test")
.setQuery(randomizeType(multiMatchQuery("captain america 15", "first_name", "last_name", "skill")
.type(MultiMatchQueryBuilder.Type.CROSS_FIELDS)
.lenient(true)
.analyzer("category"))).get();
assertFirstHit(searchResponse, hasId("theone"));