From 525101b64d9a22ffff9a29c0309444f721ebb763 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Thu, 20 Apr 2017 22:12:20 +0200 Subject: [PATCH] Query string default field (#24214) Currently any `query_string` query that use a wildcard field with no matching field is rewritten with the `_all` field. For instance: ```` #creating test doc PUT testing/t/1 { "test": { "field_one": "hello", "field_two": "world" } } #searching abc.* (does not exist) -> hit GET testing/t/_search { "query": { "query_string": { "fields": [ "abc.*" ], "query": "hello" } } } ```` This bug first appeared in 5.0 after the query refactoring and impacts only users that use `_all` as default field. Indices created in 6.x will not have this problem since `_all` is deactivated in this version. This change fixes this bug by returning a MatchNoDocsQuery for any term that expand to an empty list of field. --- .../classic/MapperQueryParser.java | 15 ++++++++--- .../index/query/QueryStringQueryBuilder.java | 6 ++++- .../query/QueryStringQueryBuilderTests.java | 26 ++++++++++++++++--- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java b/core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java index 79f522e8c1f..015972c5684 100644 --- a/core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java +++ b/core/src/main/java/org/apache/lucene/queryparser/classic/MapperQueryParser.java @@ -57,7 +57,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; - +import java.util.Collections; import static java.util.Collections.unmodifiableMap; import static org.elasticsearch.common.lucene.search.Queries.fixNegativeQueryIfNeeded; @@ -91,7 +91,8 @@ public class MapperQueryParser extends QueryParser { public void reset(QueryParserSettings settings) { this.settings = settings; - if (settings.fieldsAndWeights().isEmpty()) { + if (settings.fieldsAndWeights() == null) { + // this query has no explicit fields to query so we fallback to the default field this.field = settings.defaultField(); } else if (settings.fieldsAndWeights().size() == 1) { this.field = settings.fieldsAndWeights().keySet().iterator().next(); @@ -148,6 +149,11 @@ public class MapperQueryParser extends QueryParser { if (fields != null) { if (fields.size() == 1) { return getFieldQuerySingle(fields.iterator().next(), queryText, quoted); + } else if (fields.isEmpty()) { + // the requested fields do not match any field in the mapping + // happens for wildcard fields only since we cannot expand to a valid field name + // if there is no match in the mappings. + return new MatchNoDocsQuery("empty fields"); } if (settings.useDisMax()) { List queries = new ArrayList<>(); @@ -721,7 +727,7 @@ public class MapperQueryParser extends QueryParser { } private Query applyBoost(String field, Query q) { - Float fieldBoost = settings.fieldsAndWeights().get(field); + Float fieldBoost = settings.fieldsAndWeights() == null ? null : settings.fieldsAndWeights().get(field); if (fieldBoost != null && fieldBoost != 1f) { return new BoostQuery(q, fieldBoost); } @@ -780,7 +786,8 @@ public class MapperQueryParser extends QueryParser { if (field != null) { fields = context.simpleMatchToIndexNames(field); } else { - fields = settings.fieldsAndWeights().keySet(); + Map fieldsAndWeights = settings.fieldsAndWeights(); + fields = fieldsAndWeights == null ? Collections.emptyList() : settings.fieldsAndWeights().keySet(); } return fields; } diff --git a/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java b/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java index ca716372571..fd6f33e27ba 100644 --- a/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java +++ b/core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java @@ -981,7 +981,11 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder 0); + QueryStringQueryBuilder queryStringQueryBuilder = + new QueryStringQueryBuilder("foo bar").field("invalid*"); + Query query = queryStringQueryBuilder.toQuery(createShardContext()); + Query expectedQuery = new BooleanQuery.Builder() + .add(new MatchNoDocsQuery("empty fields"), Occur.SHOULD) + .add(new MatchNoDocsQuery("empty fields"), Occur.SHOULD) + .build(); + assertThat(expectedQuery, equalTo(query)); + + queryStringQueryBuilder = + new QueryStringQueryBuilder("field:foo bar").field("invalid*"); + query = queryStringQueryBuilder.toQuery(createShardContext()); + expectedQuery = new BooleanQuery.Builder() + .add(new TermQuery(new Term("field", "foo")), Occur.SHOULD) + .add(new MatchNoDocsQuery("empty fields"), Occur.SHOULD) + .build(); + assertThat(expectedQuery, equalTo(query)); + + + } + public void testToQuerySplitOnWhitespace() throws IOException { assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0); // splitOnWhitespace=false