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
This commit is contained in:
Jim Ferenczi 2017-08-23 15:39:54 +02:00 committed by GitHub
parent c1452ff9ea
commit de1e4e0c15
12 changed files with 171 additions and 81 deletions

View File

@ -61,7 +61,6 @@ import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.indices.analysis.AnalysisModule; import org.elasticsearch.indices.analysis.AnalysisModule;
import org.elasticsearch.indices.analysis.PreBuiltTokenizers;
import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService; import org.elasticsearch.transport.TransportService;
@ -151,8 +150,12 @@ public class TransportAnalyzeAction extends TransportSingleShardAction<AnalyzeRe
} }
} }
if (field == null) { 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) { if (indexService != null) {
field = indexService.getIndexSettings().getDefaultField(); field = indexService.getIndexSettings().getDefaultFields().get(0);
} else { } else {
field = AllFieldMapper.NAME; field = AllFieldMapper.NAME;
} }

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ import org.joda.time.DateTimeZone;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -738,31 +738,18 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
Fuzziness fuzziness = QueryStringQueryBuilder.DEFAULT_FUZZINESS; Fuzziness fuzziness = QueryStringQueryBuilder.DEFAULT_FUZZINESS;
String fuzzyRewrite = null; String fuzzyRewrite = null;
String rewrite = null; String rewrite = null;
Map<String, Float> fieldsAndWeights = new HashMap<>(); Map<String, Float> fieldsAndWeights = null;
boolean autoGenerateSynonymsPhraseQuery = true; boolean autoGenerateSynonymsPhraseQuery = true;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) { if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName(); currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_ARRAY) { } else if (token == XContentParser.Token.START_ARRAY) {
if (FIELDS_FIELD.match(currentFieldName)) { if (FIELDS_FIELD.match(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { List<String> fields = new ArrayList<>();
String fField = null; while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
float fBoost = AbstractQueryBuilder.DEFAULT_BOOST; fields.add(parser.text());
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);
} }
fieldsAndWeights = QueryParserHelper.parseFieldsAndWeights(fields);
} else { } else {
throw new ParsingException(parser.getTokenLocation(), "[" + QueryStringQueryBuilder.NAME + throw new ParsingException(parser.getTokenLocation(), "[" + QueryStringQueryBuilder.NAME +
"] query does not support [" + currentFieldName + "]"); "] query does not support [" + currentFieldName + "]");
@ -851,7 +838,9 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
} }
QueryStringQueryBuilder queryStringQuery = new QueryStringQueryBuilder(queryString); QueryStringQueryBuilder queryStringQuery = new QueryStringQueryBuilder(queryString);
queryStringQuery.fields(fieldsAndWeights); if (fieldsAndWeights != null) {
queryStringQuery.fields(fieldsAndWeights);
}
queryStringQuery.defaultField(defaultField); queryStringQuery.defaultField(defaultField);
queryStringQuery.defaultOperator(defaultOperator); queryStringQuery.defaultOperator(defaultOperator);
queryStringQuery.analyzer(analyzer); queryStringQuery.analyzer(analyzer);
@ -943,16 +932,19 @@ public class QueryStringQueryBuilder extends AbstractQueryBuilder<QueryStringQue
final Map<String, Float> resolvedFields = QueryParserHelper.resolveMappingFields(context, fieldsAndWeights); final Map<String, Float> resolvedFields = QueryParserHelper.resolveMappingFields(context, fieldsAndWeights);
queryParser = new QueryStringQueryParser(context, resolvedFields, isLenient); queryParser = new QueryStringQueryParser(context, resolvedFields, isLenient);
} else { } else {
String defaultField = context.defaultField(); List<String> defaultFields = context.defaultFields();
if (context.getMapperService().allEnabled() == false && 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 // 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); queryParser = new QueryStringQueryParser(context, lenient == null ? true : lenient);
} else { } else {
queryParser = new QueryStringQueryParser(context, defaultField, isLenient); final Map<String, Float> resolvedFields = QueryParserHelper.resolveMappingFields(context,
QueryParserHelper.parseFieldsAndWeights(defaultFields));
queryParser = new QueryStringQueryParser(context, resolvedFields, isLenient);
} }
} }

View File

@ -37,8 +37,10 @@ import org.elasticsearch.index.search.SimpleQueryStringQueryParser;
import org.elasticsearch.index.search.SimpleQueryStringQueryParser.Settings; import org.elasticsearch.index.search.SimpleQueryStringQueryParser.Settings;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@ -402,18 +404,18 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder<SimpleQuerySt
if (fieldsAndWeights.isEmpty() == false) { if (fieldsAndWeights.isEmpty() == false) {
resolvedFieldsAndWeights = QueryParserHelper.resolveMappingFields(context, fieldsAndWeights); resolvedFieldsAndWeights = QueryParserHelper.resolveMappingFields(context, fieldsAndWeights);
} else { } else {
String defaultField = context.defaultField(); List<String> defaultFields = context.defaultFields();
if (context.getMapperService().allEnabled() == false && 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 // 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) { if (isAllField) {
newSettings.lenient(lenientSet ? settings.lenient() : true); newSettings.lenient(lenientSet ? settings.lenient() : true);
} }
resolvedFieldsAndWeights = QueryParserHelper.resolveMappingField(context, defaultField, 1.0f, resolvedFieldsAndWeights = QueryParserHelper.resolveMappingFields(context,
false, !isAllField); QueryParserHelper.parseFieldsAndWeights(defaultFields));
} }
final SimpleQueryStringQueryParser sqp; final SimpleQueryStringQueryParser sqp;
@ -474,7 +476,7 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder<SimpleQuerySt
float boost = AbstractQueryBuilder.DEFAULT_BOOST; float boost = AbstractQueryBuilder.DEFAULT_BOOST;
String queryName = null; String queryName = null;
String minimumShouldMatch = null; String minimumShouldMatch = null;
Map<String, Float> fieldsAndWeights = new HashMap<>(); Map<String, Float> fieldsAndWeights = null;
Operator defaultOperator = null; Operator defaultOperator = null;
String analyzerName = null; String analyzerName = null;
int flags = SimpleQueryStringFlag.ALL.value(); int flags = SimpleQueryStringFlag.ALL.value();
@ -489,24 +491,11 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder<SimpleQuerySt
currentFieldName = parser.currentName(); currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_ARRAY) { } else if (token == XContentParser.Token.START_ARRAY) {
if (FIELDS_FIELD.match(currentFieldName)) { if (FIELDS_FIELD.match(currentFieldName)) {
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { List<String> fields = new ArrayList<>();
String fField = null; while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
float fBoost = 1; fields.add(parser.text());
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);
} }
fieldsAndWeights = QueryParserHelper.parseFieldsAndWeights(fields);
} else { } else {
throw new ParsingException(parser.getTokenLocation(), "[" + SimpleQueryStringBuilder.NAME + throw new ParsingException(parser.getTokenLocation(), "[" + SimpleQueryStringBuilder.NAME +
"] query does not support [" + currentFieldName + "]"); "] query does not support [" + currentFieldName + "]");
@ -565,7 +554,10 @@ public class SimpleQueryStringBuilder extends AbstractQueryBuilder<SimpleQuerySt
} }
SimpleQueryStringBuilder qb = new SimpleQueryStringBuilder(queryBody); 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); qb.flags(flags).defaultOperator(defaultOperator);
if (lenient != null) { if (lenient != null) {
qb.lenient(lenient); qb.lenient(lenient);

View File

@ -35,11 +35,12 @@ import org.elasticsearch.index.query.QueryShardContext;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; 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 { public final class QueryParserHelper {
// Mapping types the "all-ish" query can be executed against // Mapping types the "all-ish" query can be executed against
@ -59,6 +60,29 @@ public final class QueryParserHelper {
private 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. * Get a {@link FieldMapper} associated with a field name or null.
* @param mapperService The mapper service where to find the mapping. * @param mapperService The mapper service where to find the mapping.

View File

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

View File

@ -32,6 +32,7 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.VersionUtils; import org.elasticsearch.test.VersionUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; 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")));
}
} }

View File

@ -47,10 +47,12 @@ import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.automaton.TooComplexToDeterminizeException; import org.apache.lucene.util.automaton.TooComplexToDeterminizeException;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.lucene.all.AllTermQuery; import org.elasticsearch.common.lucene.all.AllTermQuery;
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.json.JsonXContent; import org.elasticsearch.common.xcontent.json.JsonXContent;
@ -63,6 +65,7 @@ import org.joda.time.DateTimeZone;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder; import static org.elasticsearch.index.query.AbstractQueryBuilder.parseInnerQueryBuilder;
@ -75,7 +78,6 @@ import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStringQueryBuilder> { public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStringQueryBuilder> {
@Override @Override
protected QueryStringQueryBuilder doCreateTestQueryBuilder() { protected QueryStringQueryBuilder doCreateTestQueryBuilder() {
int numTerms = randomIntBetween(0, 5); int numTerms = randomIntBetween(0, 5);
@ -989,4 +991,34 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase<QueryStr
.toQuery(createShardContext()); .toQuery(createShardContext());
assertEquals(new MatchNoDocsQuery(""), query); 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();
}
} }

View File

@ -39,11 +39,15 @@ import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery; import org.apache.lucene.search.spans.SpanTermQuery;
import org.apache.lucene.util.TestUtil; import org.apache.lucene.util.TestUtil;
import org.elasticsearch.Version; 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.index.search.SimpleQueryStringQueryParser;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.test.AbstractQueryTestCase; import org.elasticsearch.test.AbstractQueryTestCase;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -264,7 +268,8 @@ public class SimpleQueryStringBuilderTests extends AbstractQueryTestCase<SimpleQ
assertTermOrBoostQuery(query, field.getKey(), queryBuilder.value(), field.getValue()); assertTermOrBoostQuery(query, field.getKey(), queryBuilder.value(), field.getValue());
} }
} else if (queryBuilder.fields().size() == 0) { } 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) { if (query instanceof DisjunctionMaxQuery) {
for (Query disjunct : (DisjunctionMaxQuery) query) { for (Query disjunct : (DisjunctionMaxQuery) query) {
assertThat(disjunct, either(instanceOf(TermQuery.class)).or(instanceOf(MatchNoDocsQuery.class))); 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"));
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();
}
} }

View File

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