Procházet zdrojové kódy

Accept an array of field names and boosts in the index.query.default_field setting (#26320)

* Accept an array of field names and boosts in the index.query.default_field setting

This commit allows to define an array of field names and boosts for the index setting `index.query.default_field`.
The format is equivalent to the `fields` options of the full text search queries (e.g. field_name^boost).
This commit also makes this setting dynamically updatable.

Fixes #25946
Jim Ferenczi před 8 roky
rodič
revize
de1e4e0c15

+ 5 - 2
core/src/main/java/org/elasticsearch/action/admin/indices/analyze/TransportAnalyzeAction.java

@@ -61,7 +61,6 @@ import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.shard.ShardId;
 import org.elasticsearch.indices.IndicesService;
 import org.elasticsearch.indices.analysis.AnalysisModule;
-import org.elasticsearch.indices.analysis.PreBuiltTokenizers;
 import org.elasticsearch.threadpool.ThreadPool;
 import org.elasticsearch.transport.TransportService;
 
@@ -151,8 +150,12 @@ public class TransportAnalyzeAction extends TransportSingleShardAction<AnalyzeRe
                 }
             }
             if (field == null) {
+                /**
+                 * TODO: _all is disabled by default and index.query.default_field can define multiple fields or pattterns so we should
+                 * probably makes the field name mandatory in analyze query.
+                 **/
                 if (indexService != null) {
-                    field = indexService.getIndexSettings().getDefaultField();
+                    field = indexService.getIndexSettings().getDefaultFields().get(0);
                 } else {
                     field = AllFieldMapper.NAME;
                 }

+ 16 - 11
core/src/main/java/org/elasticsearch/index/IndexSettings.java

@@ -23,7 +23,6 @@ import org.apache.lucene.index.MergePolicy;
 import org.elasticsearch.Version;
 import org.elasticsearch.cluster.metadata.IndexMetaData;
 import org.elasticsearch.common.logging.Loggers;
-import org.elasticsearch.common.lucene.all.AllField;
 import org.elasticsearch.common.settings.IndexScopedSettings;
 import org.elasticsearch.common.settings.Setting;
 import org.elasticsearch.common.settings.Setting.Property;
@@ -32,10 +31,11 @@ import org.elasticsearch.common.unit.ByteSizeUnit;
 import org.elasticsearch.common.unit.ByteSizeValue;
 import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.index.mapper.AllFieldMapper;
-import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.index.translog.Translog;
 import org.elasticsearch.node.Node;
 
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
@@ -50,9 +50,9 @@ import java.util.function.Function;
  */
 public final class IndexSettings {
     public static final String DEFAULT_FIELD_SETTING_KEY = "index.query.default_field";
-    public static final Setting<String> DEFAULT_FIELD_SETTING;
+    public static final Setting<List<String>> DEFAULT_FIELD_SETTING;
     static {
-        Function<Settings, String> defValue = settings -> {
+        Function<Settings, List<String>> defValue = settings -> {
             final String defaultField;
             if (settings.getAsVersion(IndexMetaData.SETTING_VERSION_CREATED, null) != null &&
                     Version.indexCreated(settings).before(Version.V_6_0_0_alpha1)) {
@@ -60,9 +60,9 @@ public final class IndexSettings {
             } else {
                 defaultField = "*";
             }
-            return defaultField;
+            return Collections.singletonList(defaultField);
         };
-        DEFAULT_FIELD_SETTING = new Setting<>(DEFAULT_FIELD_SETTING_KEY, defValue, Function.identity(), Property.IndexScope, Property.Dynamic);
+        DEFAULT_FIELD_SETTING = Setting.listSetting(DEFAULT_FIELD_SETTING_KEY, defValue, Function.identity(), Property.IndexScope, Property.Dynamic);
     }
     public static final Setting<Boolean> QUERY_STRING_LENIENT_SETTING =
         Setting.boolSetting("index.query_string.lenient", false, Property.IndexScope);
@@ -205,7 +205,7 @@ public final class IndexSettings {
     // volatile fields are updated via #updateIndexMetaData(IndexMetaData) under lock
     private volatile Settings settings;
     private volatile IndexMetaData indexMetaData;
-    private final String defaultField;
+    private volatile List<String> defaultFields;
     private final boolean queryStringLenient;
     private final boolean queryStringAnalyzeWildcard;
     private final boolean queryStringAllowLeadingWildcard;
@@ -241,10 +241,14 @@ public final class IndexSettings {
     private final boolean singleType;
 
     /**
-     * Returns the default search field for this index.
+     * Returns the default search fields for this index.
      */
-    public String getDefaultField() {
-        return defaultField;
+    public List<String> getDefaultFields() {
+        return defaultFields;
+    }
+
+    private void setDefaultFields(List<String> defaultFields) {
+        this.defaultFields = defaultFields;
     }
 
     /**
@@ -304,12 +308,12 @@ public final class IndexSettings {
         this.indexMetaData = indexMetaData;
         numberOfShards = settings.getAsInt(IndexMetaData.SETTING_NUMBER_OF_SHARDS, null);
 
-        this.defaultField = DEFAULT_FIELD_SETTING.get(settings);
         this.queryStringLenient = QUERY_STRING_LENIENT_SETTING.get(settings);
         this.queryStringAnalyzeWildcard = QUERY_STRING_ANALYZE_WILDCARD.get(nodeSettings);
         this.queryStringAllowLeadingWildcard = QUERY_STRING_ALLOW_LEADING_WILDCARD.get(nodeSettings);
         this.defaultAllowUnmappedFields = scopedSettings.get(ALLOW_UNMAPPED);
         this.durability = scopedSettings.get(INDEX_TRANSLOG_DURABILITY_SETTING);
+        defaultFields = scopedSettings.get(DEFAULT_FIELD_SETTING);
         syncInterval = INDEX_TRANSLOG_SYNC_INTERVAL_SETTING.get(settings);
         refreshInterval = scopedSettings.get(INDEX_REFRESH_INTERVAL_SETTING);
         flushThresholdSize = scopedSettings.get(INDEX_TRANSLOG_FLUSH_THRESHOLD_SIZE_SETTING);
@@ -361,6 +365,7 @@ public final class IndexSettings {
         scopedSettings.addSettingsUpdateConsumer(INDEX_REFRESH_INTERVAL_SETTING, this::setRefreshInterval);
         scopedSettings.addSettingsUpdateConsumer(MAX_REFRESH_LISTENERS_PER_SHARD, this::setMaxRefreshListeners);
         scopedSettings.addSettingsUpdateConsumer(MAX_SLICES_PER_SCROLL, this::setMaxSlicesPerScroll);
+        scopedSettings.addSettingsUpdateConsumer(DEFAULT_FIELD_SETTING, this::setDefaultFields);
     }
 
     private void setTranslogFlushThresholdSize(ByteSizeValue byteSizeValue) {

+ 1 - 2
core/src/main/java/org/elasticsearch/index/query/MoreLikeThisQueryBuilder.java

@@ -59,7 +59,6 @@ import org.elasticsearch.index.mapper.UidFieldMapper;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
@@ -1033,7 +1032,7 @@ public class MoreLikeThisQueryBuilder extends AbstractQueryBuilder<MoreLikeThisQ
         boolean useDefaultField = (fields == null);
         List<String> moreLikeFields = new ArrayList<>();
         if (useDefaultField) {
-            moreLikeFields = Collections.singletonList(context.defaultField());
+            moreLikeFields = context.defaultFields();
         } else {
             for (String field : fields) {
                 MappedFieldType fieldType = context.fieldMapper(field);

+ 3 - 6
core/src/main/java/org/elasticsearch/index/query/QueryShardContext.java

@@ -40,14 +40,11 @@ import org.elasticsearch.index.IndexSettings;
 import org.elasticsearch.index.analysis.IndexAnalyzers;
 import org.elasticsearch.index.cache.bitset.BitsetFilterCache;
 import org.elasticsearch.index.fielddata.IndexFieldData;
-import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData;
 import org.elasticsearch.index.mapper.ContentPath;
 import org.elasticsearch.index.mapper.DocumentMapper;
-import org.elasticsearch.index.mapper.IndexFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
 import org.elasticsearch.index.mapper.Mapper;
 import org.elasticsearch.index.mapper.MapperService;
-import org.elasticsearch.index.mapper.MetadataFieldMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.mapper.TextFieldMapper;
 import org.elasticsearch.index.query.support.NestedScope;
@@ -60,10 +57,10 @@ import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.function.BiConsumer;
 import java.util.function.BiFunction;
-import java.util.function.Function;
 import java.util.function.LongSupplier;
 
 import static java.util.Collections.unmodifiableMap;
@@ -144,8 +141,8 @@ public class QueryShardContext extends QueryRewriteContext {
         return similarityService != null ? similarityService.similarity(mapperService) : null;
     }
 
-    public String defaultField() {
-        return indexSettings.getDefaultField();
+    public List<String> defaultFields() {
+        return indexSettings.getDefaultFields();
     }
 
     public boolean queryStringLenient() {

+ 17 - 25
core/src/main/java/org/elasticsearch/index/query/QueryStringQueryBuilder.java

@@ -42,7 +42,7 @@ import org.joda.time.DateTimeZone;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -738,31 +738,18 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         Fuzziness fuzziness = QueryStringQueryBuilder.DEFAULT_FUZZINESS;
         String fuzzyRewrite = null;
         String rewrite = null;
-        Map<String, Float> fieldsAndWeights = new HashMap<>();
+        Map<String, Float> fieldsAndWeights = null;
         boolean autoGenerateSynonymsPhraseQuery = true;
         while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
             if (token == XContentParser.Token.FIELD_NAME) {
                 currentFieldName = parser.currentName();
             } else if (token == XContentParser.Token.START_ARRAY) {
                 if (FIELDS_FIELD.match(currentFieldName)) {
-                    while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
-                        String fField = null;
-                        float fBoost = AbstractQueryBuilder.DEFAULT_BOOST;
-                        char[] text = parser.textCharacters();
-                        int end = parser.textOffset() + parser.textLength();
-                        for (int i = parser.textOffset(); i < end; i++) {
-                            if (text[i] == '^') {
-                                int relativeLocation = i - parser.textOffset();
-                                fField = new String(text, parser.textOffset(), relativeLocation);
-                                fBoost = Float.parseFloat(new String(text, i + 1, parser.textLength() - relativeLocation - 1));
-                                break;
-                            }
-                        }
-                        if (fField == null) {
-                            fField = parser.text();
-                        }
-                        fieldsAndWeights.put(fField, fBoost);
+                    List<String> fields = new ArrayList<>();
+                    while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
+                        fields.add(parser.text());
                     }
+                    fieldsAndWeights = QueryParserHelper.parseFieldsAndWeights(fields);
                 } else {
                     throw new ParsingException(parser.getTokenLocation(), "[" + QueryStringQueryBuilder.NAME +
                             "] query does not support [" + currentFieldName + "]");
@@ -851,7 +838,9 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
         }
 
         QueryStringQueryBuilder queryStringQuery = new QueryStringQueryBuilder(queryString);
-        queryStringQuery.fields(fieldsAndWeights);
+        if (fieldsAndWeights != null) {
+            queryStringQuery.fields(fieldsAndWeights);
+        }
         queryStringQuery.defaultField(defaultField);
         queryStringQuery.defaultOperator(defaultOperator);
         queryStringQuery.analyzer(analyzer);
@@ -943,16 +932,19 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
             final Map<String, Float> resolvedFields = QueryParserHelper.resolveMappingFields(context, fieldsAndWeights);
             queryParser = new QueryStringQueryParser(context, resolvedFields, isLenient);
         } else {
-            String defaultField = context.defaultField();
+            List<String> defaultFields = context.defaultFields();
             if (context.getMapperService().allEnabled() == false &&
-                    AllFieldMapper.NAME.equals(defaultField)) {
+                    defaultFields.size() == 1 && AllFieldMapper.NAME.equals(defaultFields.get(0))) {
                 // For indices created before 6.0 with _all disabled
-                defaultField = "*";
+                defaultFields = Collections.singletonList("*");
             }
-            if (Regex.isMatchAllPattern(defaultField)) {
+            boolean isAllField = defaultFields.size() == 1 && Regex.isMatchAllPattern(defaultFields.get(0));
+            if (isAllField) {
                 queryParser = new QueryStringQueryParser(context, lenient == null ? true : lenient);
             } else {
-                queryParser = new QueryStringQueryParser(context, defaultField, isLenient);
+                final Map<String, Float> resolvedFields = QueryParserHelper.resolveMappingFields(context,
+                    QueryParserHelper.parseFieldsAndWeights(defaultFields));
+                queryParser = new QueryStringQueryParser(context, resolvedFields, isLenient);
             }
         }
 

+ 17 - 25
core/src/main/java/org/elasticsearch/index/query/SimpleQueryStringBuilder.java

@@ -37,8 +37,10 @@ import org.elasticsearch.index.search.SimpleQueryStringQueryParser;
 import org.elasticsearch.index.search.SimpleQueryStringQueryParser.Settings;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
@@ -402,18 +404,18 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder<SimpleQuerySt
         if (fieldsAndWeights.isEmpty() == false) {
             resolvedFieldsAndWeights = QueryParserHelper.resolveMappingFields(context, fieldsAndWeights);
         } else {
-            String defaultField = context.defaultField();
+            List<String> defaultFields = context.defaultFields();
             if (context.getMapperService().allEnabled() == false &&
-                    AllFieldMapper.NAME.equals(defaultField)) {
+                    defaultFields.size() == 1 && AllFieldMapper.NAME.equals(defaultFields.get(0))) {
                 // For indices created before 6.0 with _all disabled
-                defaultField = "*";
+                defaultFields = Collections.singletonList("*");
             }
-            boolean isAllField = Regex.isMatchAllPattern(defaultField);
+            boolean isAllField = defaultFields.size() == 1 && Regex.isMatchAllPattern(defaultFields.get(0));
             if (isAllField) {
                 newSettings.lenient(lenientSet ? settings.lenient() : true);
             }
-            resolvedFieldsAndWeights = QueryParserHelper.resolveMappingField(context, defaultField, 1.0f,
-                false, !isAllField);
+            resolvedFieldsAndWeights = QueryParserHelper.resolveMappingFields(context,
+                QueryParserHelper.parseFieldsAndWeights(defaultFields));
         }
 
         final SimpleQueryStringQueryParser sqp;
@@ -474,7 +476,7 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder<SimpleQuerySt
         float boost = AbstractQueryBuilder.DEFAULT_BOOST;
         String queryName = null;
         String minimumShouldMatch = null;
-        Map<String, Float> fieldsAndWeights = new HashMap<>();
+        Map<String, Float> fieldsAndWeights = null;
         Operator defaultOperator = null;
         String analyzerName = null;
         int flags = SimpleQueryStringFlag.ALL.value();
@@ -489,24 +491,11 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder<SimpleQuerySt
                 currentFieldName = parser.currentName();
             } else if (token == XContentParser.Token.START_ARRAY) {
                 if (FIELDS_FIELD.match(currentFieldName)) {
-                    while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
-                        String fField = null;
-                        float fBoost = 1;
-                        char[] text = parser.textCharacters();
-                        int end = parser.textOffset() + parser.textLength();
-                        for (int i = parser.textOffset(); i < end; i++) {
-                            if (text[i] == '^') {
-                                int relativeLocation = i - parser.textOffset();
-                                fField = new String(text, parser.textOffset(), relativeLocation);
-                                fBoost = Float.parseFloat(new String(text, i + 1, parser.textLength() - relativeLocation - 1));
-                                break;
-                            }
-                        }
-                        if (fField == null) {
-                            fField = parser.text();
-                        }
-                        fieldsAndWeights.put(fField, fBoost);
+                    List<String> fields = new ArrayList<>();
+                    while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
+                        fields.add(parser.text());
                     }
+                    fieldsAndWeights = QueryParserHelper.parseFieldsAndWeights(fields);
                 } else {
                     throw new ParsingException(parser.getTokenLocation(), "[" + SimpleQueryStringBuilder.NAME +
                             "] query does not support [" + currentFieldName + "]");
@@ -565,7 +554,10 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder<SimpleQuerySt
         }
 
         SimpleQueryStringBuilder qb = new SimpleQueryStringBuilder(queryBody);
-        qb.boost(boost).fields(fieldsAndWeights).analyzer(analyzerName).queryName(queryName).minimumShouldMatch(minimumShouldMatch);
+        if (fieldsAndWeights != null) {
+            qb.fields(fieldsAndWeights);
+        }
+        qb.boost(boost).analyzer(analyzerName).queryName(queryName).minimumShouldMatch(minimumShouldMatch);
         qb.flags(flags).defaultOperator(defaultOperator);
         if (lenient != null) {
             qb.lenient(lenient);

+ 25 - 1
core/src/main/java/org/elasticsearch/index/search/QueryParserHelper.java

@@ -35,11 +35,12 @@ import org.elasticsearch.index.query.QueryShardContext;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 /**
- * Helpers to extract and expand field names from a mapping
+ * Helpers to extract and expand field names and boosts
  */
 public final class QueryParserHelper {
     // Mapping types the "all-ish" query can be executed against
@@ -59,6 +60,29 @@ public final class QueryParserHelper {
 
     private QueryParserHelper() {}
 
+    /**
+     * Convert a list of field names encoded with optional boosts to a map that associates
+     * the field name and its boost.
+     * @param fields The list of fields encoded with optional boosts (e.g. ^0.35).
+     * @return The converted map with field names and associated boosts.
+     */
+    public static Map<String, Float> parseFieldsAndWeights(List<String> fields) {
+        final Map<String, Float> fieldsAndWeights = new HashMap<>();
+        for (String field : fields) {
+            int boostIndex = field.indexOf('^');
+            String fieldName;
+            float boost = 1.0f;
+            if (boostIndex != -1) {
+                fieldName = field.substring(0, boostIndex);
+                boost = Float.parseFloat(field.substring(boostIndex+1, field.length()));
+            } else {
+                fieldName = field;
+            }
+            fieldsAndWeights.put(fieldName, boost);
+        }
+        return fieldsAndWeights;
+    }
+
     /**
      * Get a {@link FieldMapper} associated with a field name or null.
      * @param mapperService The mapper service where to find the mapping.

+ 6 - 6
core/src/main/java/org/elasticsearch/index/search/QueryStringQueryParser.java

@@ -43,6 +43,7 @@ import org.apache.lucene.search.spans.SpanOrQuery;
 import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.IOUtils;
+import org.elasticsearch.common.lucene.all.AllField;
 import org.elasticsearch.common.lucene.search.Queries;
 import org.elasticsearch.common.regex.Regex;
 import org.elasticsearch.common.unit.Fuzziness;
@@ -610,18 +611,15 @@ public class QueryStringQueryParser extends XQueryParser {
 
     @Override
     protected Query getWildcardQuery(String field, String termStr) throws ParseException {
-        if (termStr.equals("*") && field != null) {
+        String actualField = field != null ? field : this.field;
+        if (termStr.equals("*") && actualField != null) {
             /**
              * We rewrite _all:* to a match all query.
              * TODO: We can remove this special case when _all is completely removed.
              */
-            if (Regex.isMatchAllPattern(field) || AllFieldMapper.NAME.equals(field)) {
+            if (Regex.isMatchAllPattern(actualField) || AllFieldMapper.NAME.equals(actualField)) {
                 return newMatchAllDocsQuery();
             }
-            String actualField = field;
-            if (actualField == null) {
-                actualField = this.field;
-            }
             // effectively, we check if a field exists or not
             return existsQuery(actualField);
         }
@@ -629,6 +627,8 @@ public class QueryStringQueryParser extends XQueryParser {
         Map<String, Float> fields = extractMultiFields(field, false);
         if (fields.isEmpty()) {
             return newUnmappedFieldQuery(termStr);
+        } else if (fields.containsKey(AllFieldMapper.NAME)) {
+            return newMatchAllDocsQuery();
         }
         List<Query> queries = new ArrayList<>();
         for (Map.Entry<String, Float> entry : fields.entrySet()) {

+ 16 - 0
core/src/test/java/org/elasticsearch/index/IndexSettingsTests.java

@@ -32,6 +32,7 @@ import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.VersionUtils;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -479,4 +480,19 @@ public class IndexSettingsTests extends ESTestCase {
             }
         }
     }
+
+    public void testQueryDefaultField() {
+        IndexSettings index = newIndexSettings(
+            newIndexMeta("index", Settings.EMPTY), Settings.EMPTY
+        );
+        assertThat(index.getDefaultFields(), equalTo(Collections.singletonList("*")));
+        index = newIndexSettings(
+            newIndexMeta("index", Settings.EMPTY), Settings.builder().put("index.query.default_field", "body").build()
+        );
+        assertThat(index.getDefaultFields(), equalTo(Collections.singletonList("body")));
+        index.updateIndexMetaData(
+            newIndexMeta("index", Settings.builder().putArray("index.query.default_field", "body", "title").build())
+        );
+        assertThat(index.getDefaultFields(), equalTo(Arrays.asList("body", "title")));
+    }
 }

+ 33 - 1
core/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java

@@ -47,10 +47,12 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.automaton.TooComplexToDeterminizeException;
 import org.elasticsearch.Version;
 import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
+import org.elasticsearch.cluster.metadata.IndexMetaData;
 import org.elasticsearch.common.ParsingException;
 import org.elasticsearch.common.compress.CompressedXContent;
 import org.elasticsearch.common.lucene.all.AllTermQuery;
 import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
+import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.common.unit.Fuzziness;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.json.JsonXContent;
@@ -63,6 +65,7 @@ import org.joda.time.DateTimeZone;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder;
@@ -75,7 +78,6 @@ import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.instanceOf;
 
 public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStringQueryBuilder> {
-
     @Override
     protected QueryStringQueryBuilder doCreateTestQueryBuilder() {
         int numTerms = randomIntBetween(0, 5);
@@ -989,4 +991,34 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
             .toQuery(createShardContext());
         assertEquals(new MatchNoDocsQuery(""), query);
     }
+
+    public void testDefaultField() throws Exception {
+        assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
+        QueryShardContext context = createShardContext();
+        context.getIndexSettings().updateIndexMetaData(
+            newIndexMeta("index", context.getIndexSettings().getSettings(), Settings.builder().putArray("index.query.default_field",
+                STRING_FIELD_NAME, STRING_FIELD_NAME_2 + "^5").build())
+        );
+        Query query = new QueryStringQueryBuilder("hello")
+            .toQuery(context);
+        Query expected = new DisjunctionMaxQuery(
+            Arrays.asList(
+                new TermQuery(new Term(STRING_FIELD_NAME, "hello")),
+                new BoostQuery(new TermQuery(new Term(STRING_FIELD_NAME_2, "hello")), 5.0f)
+            ), 0.0f
+        );
+        assertEquals(expected, query);
+        // Reset the default value
+        context.getIndexSettings().updateIndexMetaData(
+            newIndexMeta("index",
+                context.getIndexSettings().getSettings(), Settings.builder().putArray("index.query.default_field", "*").build())
+        );
+    }
+
+    private static IndexMetaData newIndexMeta(String name, Settings oldIndexSettings, Settings indexSettings) {
+        Settings build = Settings.builder().put(oldIndexSettings)
+            .put(indexSettings)
+            .build();
+        return IndexMetaData.builder(name).settings(build).build();
+    }
 }

+ 31 - 1
core/src/test/java/org/elasticsearch/index/query/SimpleQueryStringBuilderTests.java

@@ -39,11 +39,15 @@ import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.search.spans.SpanTermQuery;
 import org.apache.lucene.util.TestUtil;
 import org.elasticsearch.Version;
+import org.elasticsearch.cluster.metadata.IndexMetaData;
+import org.elasticsearch.common.lucene.all.AllTermQuery;
+import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.index.search.SimpleQueryStringQueryParser;
 import org.elasticsearch.search.internal.SearchContext;
 import org.elasticsearch.test.AbstractQueryTestCase;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -264,7 +268,8 @@ public class SimpleQueryStringBuilderTests extends AbstractQueryTestCase<SimpleQ
                 assertTermOrBoostQuery(query, field.getKey(), queryBuilder.value(), field.getValue());
             }
         } else if (queryBuilder.fields().size() == 0) {
-            assertThat(query, either(instanceOf(DisjunctionMaxQuery.class)).or(instanceOf(MatchNoDocsQuery.class)));
+            assertThat(query, either(instanceOf(DisjunctionMaxQuery.class))
+                .or(instanceOf(MatchNoDocsQuery.class)).or(instanceOf(TermQuery.class)).or(instanceOf(AllTermQuery.class)));
             if (query instanceof DisjunctionMaxQuery) {
                 for (Query disjunct : (DisjunctionMaxQuery) query) {
                     assertThat(disjunct, either(instanceOf(TermQuery.class)).or(instanceOf(MatchNoDocsQuery.class)));
@@ -539,4 +544,29 @@ public class SimpleQueryStringBuilderTests extends AbstractQueryTestCase<SimpleQ
         assertEquals(new TermQuery(new Term(STRING_FIELD_NAME, "bar")), parser.parse("bar"));
         assertEquals(new TermQuery(new Term(STRING_FIELD_NAME, "bar")), parser.parse("\"bar\""));
     }
+
+    public void testDefaultField() throws Exception {
+        assumeTrue("test runs only when at least a type is registered", getCurrentTypes().length > 0);
+        QueryShardContext context = createShardContext();
+        context.getIndexSettings().updateIndexMetaData(
+            newIndexMeta("index", context.getIndexSettings().getSettings(), Settings.builder().putArray("index.query.default_field",
+                STRING_FIELD_NAME, STRING_FIELD_NAME_2 + "^5").build())
+        );
+        Query query = new SimpleQueryStringBuilder("hello")
+            .toQuery(context);
+        Query expected = new DisjunctionMaxQuery(
+            Arrays.asList(
+                new TermQuery(new Term(STRING_FIELD_NAME, "hello")),
+                new BoostQuery(new TermQuery(new Term(STRING_FIELD_NAME_2, "hello")), 5.0f)
+            ), 1.0f
+        );
+        assertEquals(expected, query);
+    }
+
+    private static IndexMetaData newIndexMeta(String name, Settings oldIndexSettings, Settings indexSettings) {
+        Settings build = Settings.builder().put(oldIndexSettings)
+            .put(indexSettings)
+            .build();
+        return IndexMetaData.builder(name).settings(build).build();
+    }
 }

+ 1 - 1
docs/reference/query-dsl/simple-query-string-query.asciidoc

@@ -116,7 +116,7 @@ documents that contain "baz".
 ==== Default Field
 When not explicitly specifying the field to search on in the query
 string syntax, the `index.query.default_field` will be used to derive
-which field to search on. It defaults to `*` and the query will automatically
+which fields to search on. It defaults to `*` and the query will automatically
 attempt to determine the existing fields in the index's mapping that are queryable,
 and perform the search on those fields.