Merge pull request #19665 from cbuescher/missing-field-MultiMatchQuery

`multi_match` query should produce MatchNoDocs query on unknown field
This commit is contained in:
Christoph Büscher 2016-08-01 10:59:52 +02:00 committed by GitHub
commit 87a4995bed
5 changed files with 43 additions and 29 deletions

View File

@ -745,11 +745,7 @@ public class MultiMatchQueryBuilder extends AbstractQueryBuilder<MultiMatchQuery
Map<String, Float> newFieldsBoosts = handleFieldsMatchPattern(context.getMapperService(), fieldsBoosts); Map<String, Float> newFieldsBoosts = handleFieldsMatchPattern(context.getMapperService(), fieldsBoosts);
Query query = multiMatchQuery.parse(type, newFieldsBoosts, value, minimumShouldMatch); return multiMatchQuery.parse(type, newFieldsBoosts, value, minimumShouldMatch);
if (query == null) {
return null;
}
return query;
} }
private static Map<String, Float> handleFieldsMatchPattern(MapperService mapperService, Map<String, Float> fieldsBoosts) { private static Map<String, Float> handleFieldsMatchPattern(MapperService mapperService, Map<String, Float> fieldsBoosts) {

View File

@ -280,7 +280,6 @@ public class MatchQuery {
if (zeroTermsQuery == DEFAULT_ZERO_TERMS_QUERY) { if (zeroTermsQuery == DEFAULT_ZERO_TERMS_QUERY) {
return Queries.newMatchNoDocsQuery("Matching no documents because no terms present."); return Queries.newMatchNoDocsQuery("Matching no documents because no terms present.");
} }
return Queries.newMatchAllQuery(); return Queries.newMatchAllQuery();
} }

View File

@ -23,14 +23,15 @@ import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.queries.BlendedTermQuery; import org.apache.lucene.queries.BlendedTermQuery;
import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.DisjunctionMaxQuery; import org.apache.lucene.search.DisjunctionMaxQuery;
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.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.search.BooleanClause.Occur;
import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.lucene.search.MatchNoDocsQuery;
import org.elasticsearch.common.lucene.search.Queries; import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.query.AbstractQueryBuilder; import org.elasticsearch.index.query.AbstractQueryBuilder;
@ -72,28 +73,31 @@ public class MultiMatchQuery extends MatchQuery {
} }
public Query parse(MultiMatchQueryBuilder.Type type, Map<String, Float> fieldNames, Object value, String minimumShouldMatch) throws IOException { public Query parse(MultiMatchQueryBuilder.Type type, Map<String, Float> fieldNames, Object value, String minimumShouldMatch) throws IOException {
Query result;
if (fieldNames.size() == 1) { if (fieldNames.size() == 1) {
Map.Entry<String, Float> fieldBoost = fieldNames.entrySet().iterator().next(); Map.Entry<String, Float> fieldBoost = fieldNames.entrySet().iterator().next();
Float boostValue = fieldBoost.getValue(); Float boostValue = fieldBoost.getValue();
return parseAndApply(type.matchQueryType(), fieldBoost.getKey(), value, minimumShouldMatch, boostValue); result = parseAndApply(type.matchQueryType(), fieldBoost.getKey(), value, minimumShouldMatch, boostValue);
} else {
final float tieBreaker = groupTieBreaker == null ? type.tieBreaker() : groupTieBreaker;
switch (type) {
case PHRASE:
case PHRASE_PREFIX:
case BEST_FIELDS:
case MOST_FIELDS:
queryBuilder = new QueryBuilder(tieBreaker);
break;
case CROSS_FIELDS:
queryBuilder = new CrossFieldsQueryBuilder(tieBreaker);
break;
default:
throw new IllegalStateException("No such type: " + type);
}
final List<? extends Query> queries = queryBuilder.buildGroupedQueries(type, fieldNames, value, minimumShouldMatch);
result = queryBuilder.combineGrouped(queries);
} }
assert result != null;
final float tieBreaker = groupTieBreaker == null ? type.tieBreaker() : groupTieBreaker; return result;
switch (type) {
case PHRASE:
case PHRASE_PREFIX:
case BEST_FIELDS:
case MOST_FIELDS:
queryBuilder = new QueryBuilder(tieBreaker);
break;
case CROSS_FIELDS:
queryBuilder = new CrossFieldsQueryBuilder(tieBreaker);
break;
default:
throw new IllegalStateException("No such type: " + type);
}
final List<? extends Query> queries = queryBuilder.buildGroupedQueries(type, fieldNames, value, minimumShouldMatch);
return queryBuilder.combineGrouped(queries);
} }
private QueryBuilder queryBuilder; private QueryBuilder queryBuilder;
@ -127,9 +131,9 @@ public class MultiMatchQuery extends MatchQuery {
return parseAndApply(type, field, value, minimumShouldMatch, boostValue); return parseAndApply(type, field, value, minimumShouldMatch, boostValue);
} }
public Query combineGrouped(List<? extends Query> groupQuery) { private Query combineGrouped(List<? extends Query> groupQuery) {
if (groupQuery == null || groupQuery.isEmpty()) { if (groupQuery == null || groupQuery.isEmpty()) {
return null; return new MatchNoDocsQuery("[multi_match] list of group queries was empty");
} }
if (groupQuery.size() == 1) { if (groupQuery.size() == 1) {
return groupQuery.get(0); return groupQuery.get(0);

View File

@ -53,12 +53,17 @@ import static org.hamcrest.CoreMatchers.instanceOf;
public class MultiMatchQueryBuilderTests extends AbstractQueryTestCase<MultiMatchQueryBuilder> { public class MultiMatchQueryBuilderTests extends AbstractQueryTestCase<MultiMatchQueryBuilder> {
private static final String MISSING_WILDCARD_FIELD_NAME = "missing_*";
private static final String MISSING_FIELD_NAME = "missing";
@Override @Override
protected MultiMatchQueryBuilder doCreateTestQueryBuilder() { protected MultiMatchQueryBuilder doCreateTestQueryBuilder() {
String fieldName = randomFrom(STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME); String fieldName = randomFrom(STRING_FIELD_NAME, INT_FIELD_NAME, DOUBLE_FIELD_NAME, BOOLEAN_FIELD_NAME, DATE_FIELD_NAME,
MISSING_FIELD_NAME, MISSING_WILDCARD_FIELD_NAME);
if (fieldName.equals(DATE_FIELD_NAME)) { if (fieldName.equals(DATE_FIELD_NAME)) {
assumeTrue("test with date fields runs only when at least a type is registered", getCurrentTypes().length > 0); assumeTrue("test with date fields runs only when at least a type is registered", getCurrentTypes().length > 0);
} }
// creates the query with random value and field name // creates the query with random value and field name
Object value; Object value;
if (fieldName.equals(STRING_FIELD_NAME)) { if (fieldName.equals(STRING_FIELD_NAME)) {
@ -238,6 +243,12 @@ public class MultiMatchQueryBuilderTests extends AbstractQueryTestCase<MultiMatc
assertThat(assertBooleanSubQuery(query, TermQuery.class, 1).getTerm(), equalTo(new Term(STRING_FIELD_NAME_2, "test"))); assertThat(assertBooleanSubQuery(query, TermQuery.class, 1).getTerm(), equalTo(new Term(STRING_FIELD_NAME_2, "test")));
} }
public void testToQueryFieldMissing() throws Exception {
assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
assertThat(multiMatchQuery("test").field(MISSING_WILDCARD_FIELD_NAME).toQuery(createShardContext()), instanceOf(MatchNoDocsQuery.class));
assertThat(multiMatchQuery("test").field(MISSING_FIELD_NAME).toQuery(createShardContext()), instanceOf(TermQuery.class));
}
public void testFromJson() throws IOException { public void testFromJson() throws IOException {
String json = String json =
"{\n" + "{\n" +

View File

@ -237,7 +237,8 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
assertNoFailures(searchResponse); assertNoFailures(searchResponse);
assertFirstHit(searchResponse, hasId("theone")); assertFirstHit(searchResponse, hasId("theone"));
String[] fields = {"full_name", "first_name", "last_name", "last_name_phrase", "first_name_phrase", "category_phrase", "category"}; String[] fields = { "full_name", "first_name", "last_name", "last_name_phrase", "first_name_phrase", "category_phrase", "category",
"missing_field", "missing_fields*" };
String[] query = {"marvel","hero", "captain", "america", "15", "17", "1", "5", "ultimate", "Man", String[] query = {"marvel","hero", "captain", "america", "15", "17", "1", "5", "ultimate", "Man",
"marvel", "wolferine", "ninja"}; "marvel", "wolferine", "ninja"};
@ -269,6 +270,9 @@ public class MultiMatchQueryIT extends ESIntegTestCase {
.setQuery(matchQueryBuilder).get(); .setQuery(matchQueryBuilder).get();
assertThat("field: " + field + " query: " + builder.toString(), multiMatchResp.getHits().getTotalHits(), equalTo(matchResp.getHits().getTotalHits())); assertThat("field: " + field + " query: " + builder.toString(), multiMatchResp.getHits().getTotalHits(), equalTo(matchResp.getHits().getTotalHits()));
SearchHits hits = multiMatchResp.getHits(); SearchHits hits = multiMatchResp.getHits();
if (field.startsWith("missing")) {
assertEquals(0, hits.hits().length);
}
for (int j = 0; j < hits.hits().length; j++) { for (int j = 0; j < hits.hits().length; j++) {
assertThat(hits.getHits()[j].score(), equalTo(matchResp.getHits().getHits()[j].score())); assertThat(hits.getHits()[j].score(), equalTo(matchResp.getHits().getHits()[j].score()));
assertThat(hits.getHits()[j].getId(), equalTo(matchResp.getHits().getHits()[j].getId())); assertThat(hits.getHits()[j].getId(), equalTo(matchResp.getHits().getHits()[j].getId()));