diff --git a/docs/reference/mapping/fields/field-names-field.asciidoc b/docs/reference/mapping/fields/field-names-field.asciidoc index 5c03da1124f..282750dc54f 100644 --- a/docs/reference/mapping/fields/field-names-field.asciidoc +++ b/docs/reference/mapping/fields/field-names-field.asciidoc @@ -33,4 +33,4 @@ PUT tweets } } -------------------------------------------------- -// TEST[warning:Index [tweets] uses the deprecated `enabled` setting for `_field_names`. Disabling _field_names is not necessary because it no longer carries a large index overhead. Support for this setting will be removed in a future major version. Please remove it from your mappings and templates.] +// TEST[warning:Disabling _field_names is not necessary because it no longer carries a large index overhead. Support for the `enabled` setting will be removed in a future major version. Please remove it from your mappings and templates.] diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/MapperExtrasPlugin.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/MapperExtrasPlugin.java index 569e20a1416..a769428c820 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/MapperExtrasPlugin.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/MapperExtrasPlugin.java @@ -45,7 +45,7 @@ public class MapperExtrasPlugin extends Plugin implements MapperPlugin, SearchPl @Override public Map getMetadataMappers() { - return Collections.singletonMap(RankFeatureMetaFieldMapper.CONTENT_TYPE, new RankFeatureMetaFieldMapper.TypeParser()); + return Collections.singletonMap(RankFeatureMetaFieldMapper.CONTENT_TYPE, RankFeatureMetaFieldMapper.PARSER); } @Override diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/RankFeatureMetaFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/RankFeatureMetaFieldMapper.java index e8b6fe981d4..347d018cb72 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/RankFeatureMetaFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/RankFeatureMetaFieldMapper.java @@ -19,16 +19,10 @@ package org.elasticsearch.index.mapper; -import org.apache.lucene.document.FieldType; -import org.apache.lucene.index.IndexOptions; import org.apache.lucene.search.Query; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.query.QueryShardContext; -import java.io.IOException; import java.util.Collections; -import java.util.Map; /** * This meta field only exists because rank feature fields index everything into a @@ -41,48 +35,7 @@ public class RankFeatureMetaFieldMapper extends MetadataFieldMapper { public static final String CONTENT_TYPE = "_feature"; - public static class Defaults { - public static final FieldType FIELD_TYPE = new FieldType(); - - static { - FIELD_TYPE.setIndexOptions(IndexOptions.DOCS_AND_FREQS); - FIELD_TYPE.setTokenized(true); - FIELD_TYPE.setStored(false); - FIELD_TYPE.setOmitNorms(true); - FIELD_TYPE.freeze(); - } - } - - public static class Builder extends MetadataFieldMapper.Builder { - - public Builder() { - super(NAME, Defaults.FIELD_TYPE); - } - - @Override - public RankFeatureMetaFieldMapper build(BuilderContext context) { - return new RankFeatureMetaFieldMapper(); - } - } - - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, - Map node, ParserContext parserContext) throws MapperParsingException { - return new Builder(); - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - final Settings indexSettings = context.mapperService().getIndexSettings().getSettings(); - if (fieldType != null) { - return new RankFeatureMetaFieldMapper(); - } else { - return parse(NAME, Collections.emptyMap(), context) - .build(new BuilderContext(indexSettings, new ContentPath(1))); - } - } - } + public static final TypeParser PARSER = new FixedTypeParser(c -> new RankFeatureMetaFieldMapper()); public static final class RankFeatureMetaFieldType extends MappedFieldType { @@ -109,27 +62,23 @@ public class RankFeatureMetaFieldMapper extends MetadataFieldMapper { } private RankFeatureMetaFieldMapper() { - super(Defaults.FIELD_TYPE, RankFeatureMetaFieldType.INSTANCE); + super(RankFeatureMetaFieldType.INSTANCE); } @Override - public void preParse(ParseContext context) throws IOException {} + public void preParse(ParseContext context) {} @Override - protected void parseCreateField(ParseContext context) throws IOException { + protected void parseCreateField(ParseContext context) { throw new AssertionError("Should never be called"); } @Override - public void postParse(ParseContext context) throws IOException {} + public void postParse(ParseContext context) {} @Override protected String contentType() { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder; - } } diff --git a/plugins/mapper-size/src/main/java/org/elasticsearch/index/mapper/size/SizeFieldMapper.java b/plugins/mapper-size/src/main/java/org/elasticsearch/index/mapper/size/SizeFieldMapper.java index d4a8050267b..50ac82f1250 100644 --- a/plugins/mapper-size/src/main/java/org/elasticsearch/index/mapper/size/SizeFieldMapper.java +++ b/plugins/mapper-size/src/main/java/org/elasticsearch/index/mapper/size/SizeFieldMapper.java @@ -19,88 +19,56 @@ package org.elasticsearch.index.mapper.size; -import org.apache.lucene.document.FieldType; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.support.XContentMapValues; -import org.elasticsearch.index.mapper.EnabledAttributeMapper; +import org.elasticsearch.common.Explicit; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; -import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MetadataFieldMapper; -import org.elasticsearch.index.mapper.NumberFieldMapper; +import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType; +import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType; +import org.elasticsearch.index.mapper.ParametrizedFieldMapper; import org.elasticsearch.index.mapper.ParseContext; import java.io.IOException; -import java.util.Iterator; +import java.util.Collections; import java.util.List; -import java.util.Map; public class SizeFieldMapper extends MetadataFieldMapper { public static final String NAME = "_size"; - public static class Defaults { - public static final EnabledAttributeMapper ENABLED_STATE = EnabledAttributeMapper.UNSET_DISABLED; - - public static final FieldType SIZE_FIELD_TYPE = new FieldType(); - - static { - SIZE_FIELD_TYPE.setStored(true); - SIZE_FIELD_TYPE.freeze(); - } + private static SizeFieldMapper toType(FieldMapper in) { + return (SizeFieldMapper) in; } - public static class Builder extends MetadataFieldMapper.Builder { + public static class Builder extends MetadataFieldMapper.Builder { - protected EnabledAttributeMapper enabledState = EnabledAttributeMapper.UNSET_DISABLED; + private final Parameter> enabled + = updateableBoolParam("enabled", m -> toType(m).enabled, false); private Builder() { - super(NAME, Defaults.SIZE_FIELD_TYPE); - builder = this; + super(NAME); } - public Builder enabled(EnabledAttributeMapper enabled) { - this.enabledState = enabled; - return builder; + @Override + protected List> getParameters() { + return Collections.singletonList(enabled); } @Override public SizeFieldMapper build(BuilderContext context) { - return new SizeFieldMapper(fieldType, enabledState, - new NumberFieldMapper.NumberFieldType(NAME, NumberFieldMapper.NumberType.INTEGER)); + return new SizeFieldMapper(enabled.getValue(), new NumberFieldType(NAME, NumberType.INTEGER)); } } - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - Builder builder = new Builder(); - for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = iterator.next(); - String fieldName = entry.getKey(); - Object fieldNode = entry.getValue(); - if (fieldName.equals("enabled")) { - boolean enabled = XContentMapValues.nodeBooleanValue(fieldNode, name + ".enabled"); - builder.enabled(enabled ? EnabledAttributeMapper.ENABLED : EnabledAttributeMapper.DISABLED); - iterator.remove(); - } - } - return builder; - } + public static final TypeParser PARSER = new ConfigurableTypeParser( + c -> new SizeFieldMapper(new Explicit<>(false, false), new NumberFieldType(NAME, NumberType.INTEGER)), + c -> new Builder() + ); - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - return new SizeFieldMapper(Defaults.SIZE_FIELD_TYPE, Defaults.ENABLED_STATE, - new NumberFieldMapper.NumberFieldType(NAME, NumberFieldMapper.NumberType.INTEGER)); - } - } + private final Explicit enabled; - private EnabledAttributeMapper enabledState; - - private SizeFieldMapper(FieldType fieldType, EnabledAttributeMapper enabled, - MappedFieldType mappedFieldType) { - super(fieldType, mappedFieldType); - this.enabledState = enabled; + private SizeFieldMapper(Explicit enabled, MappedFieldType mappedFieldType) { + super(mappedFieldType); + this.enabled = enabled; } @Override @@ -109,7 +77,7 @@ public class SizeFieldMapper extends MetadataFieldMapper { } public boolean enabled() { - return this.enabledState.enabled; + return this.enabled.value(); } @Override @@ -129,37 +97,15 @@ public class SizeFieldMapper extends MetadataFieldMapper { @Override protected void parseCreateField(ParseContext context) { - if (!enabledState.enabled) { + if (enabled.value() == false) { return; } final int value = context.sourceToParse().source().length(); - boolean indexed = fieldType().isSearchable(); - boolean docValued = fieldType().hasDocValues(); - boolean stored = fieldType.stored(); - context.doc().addAll(NumberFieldMapper.NumberType.INTEGER.createFields(name(), value, indexed, docValued, stored)); + context.doc().addAll(NumberType.INTEGER.createFields(name(), value, true, true, true)); } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - boolean includeDefaults = params.paramAsBoolean("include_defaults", false); - - // all are defaults, no need to write it at all - if (!includeDefaults && enabledState == Defaults.ENABLED_STATE) { - return builder; - } - builder.startObject(contentType()); - if (includeDefaults || enabledState != Defaults.ENABLED_STATE) { - builder.field("enabled", enabledState.enabled); - } - builder.endObject(); - return builder; - } - - @Override - protected void mergeOptions(FieldMapper other, List conflicts) { - SizeFieldMapper sizeFieldMapperMergeWith = (SizeFieldMapper) other; - if (sizeFieldMapperMergeWith.enabledState != enabledState && !sizeFieldMapperMergeWith.enabledState.unset()) { - this.enabledState = sizeFieldMapperMergeWith.enabledState; - } + public ParametrizedFieldMapper.Builder getMergeBuilder() { + return new Builder().init(this); } } diff --git a/plugins/mapper-size/src/main/java/org/elasticsearch/plugin/mapper/MapperSizePlugin.java b/plugins/mapper-size/src/main/java/org/elasticsearch/plugin/mapper/MapperSizePlugin.java index 1df48d5695a..a2af554d60e 100644 --- a/plugins/mapper-size/src/main/java/org/elasticsearch/plugin/mapper/MapperSizePlugin.java +++ b/plugins/mapper-size/src/main/java/org/elasticsearch/plugin/mapper/MapperSizePlugin.java @@ -19,18 +19,18 @@ package org.elasticsearch.plugin.mapper; -import java.util.Collections; -import java.util.Map; - import org.elasticsearch.index.mapper.MetadataFieldMapper; import org.elasticsearch.index.mapper.size.SizeFieldMapper; import org.elasticsearch.plugins.MapperPlugin; import org.elasticsearch.plugins.Plugin; +import java.util.Collections; +import java.util.Map; + public class MapperSizePlugin extends Plugin implements MapperPlugin { @Override public Map getMetadataMappers() { - return Collections.singletonMap(SizeFieldMapper.NAME, new SizeFieldMapper.TypeParser()); + return Collections.singletonMap(SizeFieldMapper.NAME, SizeFieldMapper.PARSER); } } diff --git a/server/src/main/java/org/elasticsearch/common/Explicit.java b/server/src/main/java/org/elasticsearch/common/Explicit.java index 3132f22daa0..9bf5b6135b6 100644 --- a/server/src/main/java/org/elasticsearch/common/Explicit.java +++ b/server/src/main/java/org/elasticsearch/common/Explicit.java @@ -19,15 +19,17 @@ package org.elasticsearch.common; +import java.util.Objects; + /** * Holds a value that is either: * a) set implicitly e.g. through some default value * b) set explicitly e.g. from a user selection - * + * * When merging conflicting configuration settings such as * field mapping settings it is preferable to preserve an explicit - * choice rather than a choice made only made implicitly by defaults. - * + * choice rather than a choice made only made implicitly by defaults. + * */ public class Explicit { @@ -48,10 +50,24 @@ public class Explicit { } /** - * + * * @return true if the value passed is a conscious decision, false if using some kind of default */ public boolean explicit() { return this.explicit; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Explicit explicit1 = (Explicit) o; + return explicit == explicit1.explicit && + Objects.equals(value, explicit1.value); + } + + @Override + public int hashCode() { + return Objects.hash(value, explicit); + } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/AllFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/AllFieldMapper.java index b3f54088838..07a5e3e9881 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/AllFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/AllFieldMapper.java @@ -23,14 +23,12 @@ import org.apache.lucene.document.FieldType; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.common.Explicit; import org.elasticsearch.index.query.QueryShardContext; import java.io.IOException; import java.util.Collections; -import java.util.Iterator; -import java.util.Map; +import java.util.List; /** * Noop mapper that ensures that mappings created in 6x that explicitly disable the _all field @@ -52,51 +50,37 @@ public class AllFieldMapper extends MetadataFieldMapper { } } - public static class Builder extends MetadataFieldMapper.Builder { - private boolean disableExplicit = false; + private static AllFieldMapper toType(FieldMapper in) { + return (AllFieldMapper) in; + } - public Builder(MappedFieldType existing) { - super(NAME, Defaults.FIELD_TYPE); - builder = this; + public static class Builder extends MetadataFieldMapper.Builder { + + private final Parameter> enabled = updateableBoolParam("enabled", m -> toType(m).enabled, false); + + public Builder() { + super(NAME); } - private Builder setDisableExplicit() { - this.disableExplicit = true; - return this; + @Override + protected List> getParameters() { + return Collections.singletonList(enabled); } @Override public AllFieldMapper build(BuilderContext context) { - return new AllFieldMapper(disableExplicit); - } - } - - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - Builder builder = new Builder(parserContext.mapperService().fieldType(NAME)); - for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = iterator.next(); - String fieldName = entry.getKey(); - if (fieldName.equals("enabled")) { - boolean enabled = XContentMapValues.nodeBooleanValue(entry.getValue(), "enabled"); - if (enabled) { - throw new IllegalArgumentException("[_all] is disabled in this version."); - } - builder.setDisableExplicit(); - iterator.remove(); - } + if (enabled.getValue().value()) { + throw new IllegalArgumentException("[_all] is disabled in this version."); } - return builder; - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - return new AllFieldMapper(false); + return new AllFieldMapper(enabled.getValue()); } } + public static final TypeParser PARSER = new ConfigurableTypeParser( + c -> new AllFieldMapper(new Explicit<>(true, false)), + c -> new Builder() + ); + static final class AllFieldType extends StringFieldType { AllFieldType() { super(NAME, false, false, TextSearchInfo.NONE, Collections.emptyMap()); @@ -113,15 +97,15 @@ public class AllFieldMapper extends MetadataFieldMapper { } } - private final boolean disableExplicit; + private final Explicit enabled; - private AllFieldMapper(boolean disableExplicit) { - super(Defaults.FIELD_TYPE, new AllFieldType()); - this.disableExplicit = disableExplicit; + private AllFieldMapper(Explicit enabled) { + super(new AllFieldType()); + this.enabled = enabled; } @Override - public void preParse(ParseContext context) throws IOException { + public void preParse(ParseContext context) { } @Override @@ -137,7 +121,6 @@ public class AllFieldMapper extends MetadataFieldMapper { @Override protected void parseCreateField(ParseContext context) throws IOException { // noop mapper - return; } @Override @@ -145,16 +128,4 @@ public class AllFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - boolean includeDefaults = params.paramAsBoolean("include_defaults", false); - if (includeDefaults || disableExplicit) { - builder.startObject(CONTENT_TYPE); - if (disableExplicit) { - builder.field("enabled", false); - } - builder.endObject(); - } - return builder; - } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/CompletionFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/CompletionFieldMapper.java index 762a21ef130..698c3ab5906 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/CompletionFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/CompletionFieldMapper.java @@ -140,7 +140,7 @@ public class CompletionFieldMapper extends ParametrizedFieldMapper { b.startArray(n); c.toXContent(b, ToXContent.EMPTY_PARAMS); b.endArray(); - }); + }, ContextMappings::toString); private final Parameter maxInputLength = Parameter.intParam("max_input_length", true, m -> toType(m).maxInputLength, Defaults.DEFAULT_MAX_INPUT_LENGTH) .addDeprecatedName("max_input_len") diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java index 526018ab33b..7f007d8ab93 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java @@ -96,7 +96,7 @@ public class DocumentMapper implements ToXContentFragment { return this; } - public Builder put(MetadataFieldMapper.Builder mapper) { + public Builder put(MetadataFieldMapper.Builder mapper) { MetadataFieldMapper metadataMapper = mapper.build(builderContext); metadataMappers.put(metadataMapper.getClass(), metadataMapper); return this; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/EnabledAttributeMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/EnabledAttributeMapper.java deleted file mode 100644 index f46de98f907..00000000000 --- a/server/src/main/java/org/elasticsearch/index/mapper/EnabledAttributeMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.elasticsearch.index.mapper; - -public enum EnabledAttributeMapper { - ENABLED(true), UNSET_ENABLED(true), DISABLED(false), UNSET_DISABLED(false); - - public final boolean enabled; - - EnabledAttributeMapper(boolean enabled) { - this.enabled = enabled; - } - - public boolean unset() { - return this == UNSET_DISABLED || this == UNSET_ENABLED; - } -} diff --git a/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java index 0d6d6d0b58b..c5d9516e2b2 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java @@ -26,10 +26,8 @@ import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.Query; import org.elasticsearch.Version; +import org.elasticsearch.common.Explicit; import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.query.QueryShardContext; import java.io.IOException; @@ -37,7 +35,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Map; /** * A mapper that indexes the field names of a document under _field_names. This mapper is typically useful in order @@ -54,10 +51,15 @@ public class FieldNamesFieldMapper extends MetadataFieldMapper { public static final String CONTENT_TYPE = "_field_names"; + @Override + public ParametrizedFieldMapper.Builder getMergeBuilder() { + return new Builder(indexVersionCreated).init(this); + } + public static class Defaults { public static final String NAME = FieldNamesFieldMapper.NAME; - public static final boolean ENABLED = true; + public static final Explicit ENABLED = new Explicit<>(true, false); public static final FieldType FIELD_TYPE = new FieldType(); static { @@ -69,69 +71,53 @@ public class FieldNamesFieldMapper extends MetadataFieldMapper { } } - static class Builder extends MetadataFieldMapper.Builder { - private boolean enabled = Defaults.ENABLED; + private static FieldNamesFieldMapper toType(FieldMapper in) { + return (FieldNamesFieldMapper) in; + } - Builder() { - super(Defaults.NAME, Defaults.FIELD_TYPE); + public static final String ENABLED_DEPRECATION_MESSAGE = + "Disabling _field_names is not necessary because it no longer carries a large index overhead. Support for the `enabled` " + + "setting will be removed in a future major version. Please remove it from your mappings and templates."; + + + static class Builder extends MetadataFieldMapper.Builder { + + private final Parameter> enabled + = updateableBoolParam("enabled", m -> toType(m).enabled, Defaults.ENABLED.value()); + + private final Version indexVersionCreated; + + Builder(Version indexVersionCreated) { + super(Defaults.NAME); + this.indexVersionCreated = indexVersionCreated; } - Builder enabled(boolean enabled) { - this.enabled = enabled; - return this; + protected List> getParameters() { + return Collections.singletonList(enabled); } @Override public FieldNamesFieldMapper build(BuilderContext context) { - FieldNamesFieldType fieldNamesFieldType = new FieldNamesFieldType(); - fieldNamesFieldType.setEnabled(enabled); - return new FieldNamesFieldMapper(fieldType, fieldNamesFieldType); + if (enabled.getValue().explicit()) { + deprecationLogger.deprecatedAndMaybeLog("field_names_enabled_parameter", ENABLED_DEPRECATION_MESSAGE); + } + FieldNamesFieldType fieldNamesFieldType = new FieldNamesFieldType(enabled.getValue().value()); + return new FieldNamesFieldMapper(enabled.getValue(), indexVersionCreated, fieldNamesFieldType); } } - public static class TypeParser implements MetadataFieldMapper.TypeParser { - - public static final String ENABLED_DEPRECATION_MESSAGE = "Index [{}] uses the deprecated `enabled` setting for `_field_names`. " - + "Disabling _field_names is not necessary because it no longer carries a large index overhead. Support for this setting " - + "will be removed in a future major version. Please remove it from your mappings and templates."; - - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - Builder builder = new Builder(); - - for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = iterator.next(); - String fieldName = entry.getKey(); - Object fieldNode = entry.getValue(); - if (fieldName.equals("enabled")) { - String indexName = parserContext.mapperService().index().getName(); - deprecationLogger.deprecatedAndMaybeLog("field_names_enabled_parameter", ENABLED_DEPRECATION_MESSAGE, indexName); - builder.enabled(XContentMapValues.nodeBooleanValue(fieldNode, name + ".enabled")); - iterator.remove(); - } - } - return builder; - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - final Settings indexSettings = context.mapperService().getIndexSettings().getSettings(); - if (fieldType != null) { - return new FieldNamesFieldMapper(Defaults.FIELD_TYPE, fieldType); - } else { - return parse(NAME, Collections.emptyMap(), context) - .build(new BuilderContext(indexSettings, new ContentPath(1))); - } - } - } + public static final TypeParser PARSER = new ConfigurableTypeParser( + c -> new FieldNamesFieldMapper(Defaults.ENABLED, c.indexVersionCreated(), new FieldNamesFieldType(Defaults.ENABLED.value())), + c -> new Builder(c.indexVersionCreated()) + ); public static final class FieldNamesFieldType extends TermBasedFieldType { - private boolean enabled = Defaults.ENABLED; + private final boolean enabled; - public FieldNamesFieldType() { + public FieldNamesFieldType(boolean enabled) { super(Defaults.NAME, true, false, TextSearchInfo.SIMPLE_MATCH_ONLY, Collections.emptyMap()); + this.enabled = enabled; } @Override @@ -139,10 +125,6 @@ public class FieldNamesFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - public boolean isEnabled() { return enabled; } @@ -164,8 +146,13 @@ public class FieldNamesFieldMapper extends MetadataFieldMapper { } } - private FieldNamesFieldMapper(FieldType fieldType, MappedFieldType mappedFieldType) { - super(fieldType, mappedFieldType); + private final Explicit enabled; + private final Version indexVersionCreated; + + private FieldNamesFieldMapper(Explicit enabled, Version indexVersionCreated, FieldNamesFieldType mappedFieldType) { + super(mappedFieldType); + this.enabled = enabled; + this.indexVersionCreated = indexVersionCreated; } @Override @@ -261,21 +248,4 @@ public class FieldNamesFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - boolean includeDefaults = params.paramAsBoolean("include_defaults", false); - - if (includeDefaults == false && fieldType().isEnabled() == Defaults.ENABLED) { - return builder; - } - - builder.startObject(NAME); - if (includeDefaults || fieldType().isEnabled() != Defaults.ENABLED) { - builder.field("enabled", fieldType().isEnabled()); - } - - builder.endObject(); - return builder; - } - } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java index 8914b86a18e..f132d6d7a1d 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java @@ -32,7 +32,6 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.util.BigArrays; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested; import org.elasticsearch.index.fielddata.IndexFieldDataCache; @@ -55,7 +54,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; /** * A mapper for the _id field. It does nothing since _id is neither indexed nor @@ -78,7 +76,6 @@ public class IdFieldMapper extends MetadataFieldMapper { public static final FieldType FIELD_TYPE = new FieldType(); public static final FieldType NESTED_FIELD_TYPE; - public static final MappedFieldType MAPPED_FIELD_TYPE = new IdFieldType(); static { FIELD_TYPE.setTokenized(false); @@ -97,18 +94,7 @@ public class IdFieldMapper extends MetadataFieldMapper { } } - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - throw new MapperParsingException(NAME + " is not configurable"); - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - return new IdFieldMapper(Defaults.FIELD_TYPE); - } - } + public static final TypeParser PARSER = new FixedTypeParser(c -> new IdFieldMapper()); static final class IdFieldType extends TermBasedFieldType { @@ -263,8 +249,8 @@ public class IdFieldMapper extends MetadataFieldMapper { }; } - private IdFieldMapper(FieldType fieldType) { - super(fieldType, new IdFieldType()); + private IdFieldMapper() { + super(new IdFieldType()); } @Override @@ -274,10 +260,8 @@ public class IdFieldMapper extends MetadataFieldMapper { @Override protected void parseCreateField(ParseContext context) throws IOException { - if (fieldType.indexOptions() != IndexOptions.NONE || fieldType.stored()) { - BytesRef id = Uid.encodeId(context.sourceToParse().id()); - context.doc().add(new Field(NAME, id, fieldType)); - } + BytesRef id = Uid.encodeId(context.sourceToParse().id()); + context.doc().add(new Field(NAME, id, Defaults.FIELD_TYPE)); } @Override @@ -285,8 +269,4 @@ public class IdFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder; - } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IgnoredFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IgnoredFieldMapper.java index 92a0092905d..766d80fecba 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IgnoredFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IgnoredFieldMapper.java @@ -24,12 +24,10 @@ import org.apache.lucene.document.FieldType; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermRangeQuery; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.query.QueryShardContext; import java.io.IOException; import java.util.Collections; -import java.util.Map; /** * A field mapper that records fields that have been ignored because they were malformed. @@ -54,30 +52,7 @@ public final class IgnoredFieldMapper extends MetadataFieldMapper { } } - public static class Builder extends MetadataFieldMapper.Builder { - - public Builder() { - super(Defaults.NAME, Defaults.FIELD_TYPE); - } - - @Override - public IgnoredFieldMapper build(BuilderContext context) { - return new IgnoredFieldMapper(); - } - } - - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - return new Builder(); - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - return new IgnoredFieldMapper(); - } - } + public static final TypeParser PARSER = new FixedTypeParser(c -> new IgnoredFieldMapper()); public static final class IgnoredFieldType extends StringFieldType { @@ -104,7 +79,7 @@ public final class IgnoredFieldMapper extends MetadataFieldMapper { } private IgnoredFieldMapper() { - super(Defaults.FIELD_TYPE, IgnoredFieldType.INSTANCE); + super(IgnoredFieldType.INSTANCE); } @Override @@ -124,7 +99,7 @@ public final class IgnoredFieldMapper extends MetadataFieldMapper { @Override protected void parseCreateField(ParseContext context) throws IOException { for (String field : context.getIgnoredFields()) { - context.doc().add(new Field(NAME, field, fieldType)); + context.doc().add(new Field(NAME, field, Defaults.FIELD_TYPE)); } } @@ -133,9 +108,4 @@ public final class IgnoredFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder; - } - } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java index 3eb1de49f61..6b28bebe6d4 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java @@ -19,11 +19,8 @@ package org.elasticsearch.index.mapper; -import org.apache.lucene.document.FieldType; -import org.apache.lucene.index.IndexOptions; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; @@ -31,7 +28,6 @@ import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; import java.io.IOException; import java.util.Collections; -import java.util.Map; public class IndexFieldMapper extends MetadataFieldMapper { @@ -40,44 +36,7 @@ public class IndexFieldMapper extends MetadataFieldMapper { public static final String CONTENT_TYPE = "_index"; - public static class Defaults { - public static final String NAME = IndexFieldMapper.NAME; - - public static final FieldType FIELD_TYPE = new FieldType(); - - static { - FIELD_TYPE.setIndexOptions(IndexOptions.NONE); - FIELD_TYPE.setTokenized(false); - FIELD_TYPE.setStored(false); - FIELD_TYPE.setOmitNorms(true); - FIELD_TYPE.freeze(); - } - } - - public static class Builder extends MetadataFieldMapper.Builder { - - public Builder() { - super(Defaults.NAME, Defaults.FIELD_TYPE); - } - - @Override - public IndexFieldMapper build(BuilderContext context) { - return new IndexFieldMapper(fieldType); - } - } - - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - throw new MapperParsingException(NAME + " is not configurable"); - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - return new IndexFieldMapper(Defaults.FIELD_TYPE); - } - } + public static final TypeParser PARSER = new FixedTypeParser(c -> new IndexFieldMapper()); static final class IndexFieldType extends ConstantFieldType { @@ -109,8 +68,8 @@ public class IndexFieldMapper extends MetadataFieldMapper { } - private IndexFieldMapper(FieldType fieldType) { - super(fieldType, IndexFieldType.INSTANCE); + public IndexFieldMapper() { + super(IndexFieldType.INSTANCE); } @Override @@ -124,8 +83,4 @@ public class IndexFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder; - } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java index 07b06a85b2b..fb98995d90b 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java @@ -33,6 +33,7 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.Nullable; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.network.InetAddresses; +import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData; @@ -72,7 +73,7 @@ public class IpFieldMapper extends ParametrizedFieldMapper { } else { b.field(f, InetAddresses.toAddrString(v)); } - }); + }, NetworkAddress::format); private final Parameter> meta = Parameter.metaParam(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java index 89746f3cc24..c7b262839ed 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java @@ -19,22 +19,24 @@ package org.elasticsearch.index.mapper; -import org.apache.lucene.document.FieldType; +import org.elasticsearch.common.Explicit; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.support.XContentMapValues; import java.io.IOException; -import java.util.List; import java.util.Map; +import java.util.function.Function; /** * A mapper for a builtin field containing metadata about a document. */ -public abstract class MetadataFieldMapper extends FieldMapper { +public abstract class MetadataFieldMapper extends ParametrizedFieldMapper { public interface TypeParser extends Mapper.TypeParser { @Override - MetadataFieldMapper.Builder parse(String name, Map node, + MetadataFieldMapper.Builder parse(String name, Map node, ParserContext parserContext) throws MapperParsingException; /** @@ -48,25 +50,109 @@ public abstract class MetadataFieldMapper extends FieldMapper { MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext parserContext); } - @SuppressWarnings("rawtypes") - public abstract static class Builder> extends FieldMapper.Builder { - public Builder(String name, FieldType fieldType) { - super(name, fieldType); + /** + * Declares an updateable boolean parameter for a metadata field + * + * We need to distinguish between explicit configuration and default value for metadata + * fields, because mapping updates will carry over the previous metadata values if a + * metadata field is not explicitly declared in the update. A standard boolean + * parameter explicitly configured with a default value will not be serialized (as + * we do not serialize default parameters for mapping updates), and as such will be + * ignored by the update merge. Instead, we use an {@link Explicit} object that + * will serialize its value if it has been configured, no matter what the value is. + */ + public static Parameter> updateableBoolParam(String name, Function> initializer, + boolean defaultValue) { + Explicit defaultExplicit = new Explicit<>(defaultValue, false); + return new Parameter<>(name, true, () -> defaultExplicit, + (n, c, o) -> new Explicit<>(XContentMapValues.nodeBooleanValue(o), true), initializer) + .setSerializer((b, n, v) -> b.field(n, v.value()), v -> Boolean.toString(v.value())); + } + + /** + * A type parser for an unconfigurable metadata field. + */ + public static class FixedTypeParser implements TypeParser { + + final Function mapperParser; + + public FixedTypeParser(Function mapperParser) { + this.mapperParser = mapperParser; } @Override - public T index(boolean index) { - if (index == false) { - throw new IllegalArgumentException("Metadata fields must be indexed"); - } + public Builder parse(String name, Map node, ParserContext parserContext) throws MapperParsingException { + throw new MapperParsingException(name + " is not configurable"); + } + + @Override + public MetadataFieldMapper getDefault(MappedFieldType defaultFieldType, ParserContext parserContext) { + return mapperParser.apply(parserContext); + } + } + + public static class ConfigurableTypeParser implements TypeParser { + + final Function defaultMapperParser; + final Function builderFunction; + + public ConfigurableTypeParser(Function defaultMapperParser, + Function builderFunction) { + this.defaultMapperParser = defaultMapperParser; + this.builderFunction = builderFunction; + } + + @Override + public Builder parse(String name, Map node, ParserContext parserContext) throws MapperParsingException { + Builder builder = builderFunction.apply(parserContext); + builder.parse(name, parserContext, node); return builder; } + @Override + public MetadataFieldMapper getDefault(MappedFieldType defaultFieldType, ParserContext parserContext) { + return defaultMapperParser.apply(parserContext); + } + } + + public abstract static class Builder extends ParametrizedFieldMapper.Builder { + + protected Builder(String name) { + super(name); + } + + boolean isConfigured() { + for (Parameter param : getParameters()) { + if (param.isConfigured()) { + return true; + } + } + return false; + } + + @Override public abstract MetadataFieldMapper build(BuilderContext context); } - protected MetadataFieldMapper(FieldType fieldType, MappedFieldType mappedFieldType) { - super(mappedFieldType.name(), fieldType, mappedFieldType, MultiFields.empty(), CopyTo.empty()); + protected MetadataFieldMapper(MappedFieldType mappedFieldType) { + super(mappedFieldType.name(), mappedFieldType, MultiFields.empty(), CopyTo.empty()); + } + + @Override + public ParametrizedFieldMapper.Builder getMergeBuilder() { + return null; // by default, things can't be configured so we have no builder + } + + @Override + public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + MetadataFieldMapper.Builder mergeBuilder = (MetadataFieldMapper.Builder) getMergeBuilder(); + if (mergeBuilder == null || mergeBuilder.isConfigured() == false) { + return builder; + } + builder.startObject(simpleName()); + boolean includeDefaults = params.paramAsBoolean("include_defaults", false); + getMergeBuilder().toXContent(builder, includeDefaults); + return builder.endObject(); } /** @@ -86,7 +172,4 @@ public abstract class MetadataFieldMapper extends FieldMapper { throw new UnsupportedOperationException("The " + typeName() + " field is not stored in _source."); } - @Override - protected void mergeOptions(FieldMapper other, List conflicts) { } - } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ParametrizedFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/ParametrizedFieldMapper.java index a9c589c298c..74a1d912109 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ParametrizedFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ParametrizedFieldMapper.java @@ -89,6 +89,9 @@ public abstract class ParametrizedFieldMapper extends FieldMapper { } ParametrizedFieldMapper.Builder builder = getMergeBuilder(); + if (builder == null) { + return (ParametrizedFieldMapper) mergeWith; + } Conflicts conflicts = new Conflicts(name()); builder.merge((FieldMapper) mergeWith, conflicts); conflicts.check(); @@ -108,11 +111,6 @@ public abstract class ParametrizedFieldMapper extends FieldMapper { // TODO remove when everything is parametrized } - @Override - public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return super.toXContent(builder, params); - } - @Override protected final void doXContentBody(XContentBuilder builder, boolean includeDefaults, Params params) throws IOException { builder.field("type", contentType()); @@ -143,6 +141,7 @@ public abstract class ParametrizedFieldMapper extends FieldMapper { private boolean acceptsNull = false; private Consumer validator = null; private Serializer serializer = XContentBuilder::field; + private Function conflictSerializer = Object::toString; private T value; private boolean isSet; @@ -186,6 +185,10 @@ public abstract class ParametrizedFieldMapper extends FieldMapper { this.value = value; } + public boolean isConfigured() { + return isSet && Objects.equals(value, defaultValue.get()) == false; + } + /** * Allows the parameter to accept a {@code null} value */ @@ -216,8 +219,9 @@ public abstract class ParametrizedFieldMapper extends FieldMapper { /** * Configure a custom serializer for this parameter */ - public Parameter setSerializer(Serializer serializer) { + public Parameter setSerializer(Serializer serializer, Function conflictSerializer) { this.serializer = serializer; + this.conflictSerializer = conflictSerializer; return this; } @@ -237,15 +241,16 @@ public abstract class ParametrizedFieldMapper extends FieldMapper { private void merge(FieldMapper toMerge, Conflicts conflicts) { T value = initializer.apply(toMerge); - if (updateable == false && isSet && Objects.equals(this.value, value) == false) { - conflicts.addConflict(name, this.value.toString(), value.toString()); + T current = getValue(); + if (updateable == false && Objects.equals(current, value) == false) { + conflicts.addConflict(name, conflictSerializer.apply(current), conflictSerializer.apply(value)); } else { setValue(value); } } private void toXContent(XContentBuilder builder, boolean includeDefaults) throws IOException { - if (includeDefaults || Objects.equals(getValue(), defaultValue.get()) == false) { + if (includeDefaults || isConfigured()) { serializer.serialize(builder, name, getValue()); } } @@ -299,6 +304,20 @@ public abstract class ParametrizedFieldMapper extends FieldMapper { (n, c, o) -> XContentMapValues.nodeStringValue(o), initializer); } + @SuppressWarnings("unchecked") + public static Parameter> stringArrayParam(String name, boolean updateable, + Function> initializer, List defaultValue) { + return new Parameter<>(name, updateable, () -> defaultValue, + (n, c, o) -> { + List values = (List) o; + List strValues = new ArrayList<>(); + for (Object item : values) { + strValues.add(item.toString()); + } + return strValues; + }, initializer); + } + /** * Defines a parameter that takes an analyzer name * @param name the parameter name @@ -316,7 +335,7 @@ public abstract class ParametrizedFieldMapper extends FieldMapper { throw new IllegalArgumentException("analyzer [" + analyzerName + "] has not been configured in mappings"); } return a; - }, initializer).setSerializer((b, n, v) -> b.field(n, v.name())); + }, initializer).setSerializer((b, n, v) -> b.field(n, v.name()), NamedAnalyzer::name); } /** diff --git a/server/src/main/java/org/elasticsearch/index/mapper/RoutingFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/RoutingFieldMapper.java index f61463129b1..f0b98094048 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/RoutingFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/RoutingFieldMapper.java @@ -26,26 +26,25 @@ import org.apache.lucene.index.Term; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.elasticsearch.common.lucene.Lucene; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.query.QueryShardContext; import java.io.IOException; import java.util.Collections; -import java.util.Iterator; -import java.util.Map; +import java.util.List; public class RoutingFieldMapper extends MetadataFieldMapper { public static final String NAME = "_routing"; public static final String CONTENT_TYPE = "_routing"; + @Override + public ParametrizedFieldMapper.Builder getMergeBuilder() { + return new Builder().init(this); + } + public static class Defaults { - public static final String NAME = "_routing"; public static final FieldType FIELD_TYPE = new FieldType(); - static { FIELD_TYPE.setIndexOptions(IndexOptions.DOCS); FIELD_TYPE.setTokenized(false); @@ -57,53 +56,33 @@ public class RoutingFieldMapper extends MetadataFieldMapper { public static final boolean REQUIRED = false; } - public static class Builder extends MetadataFieldMapper.Builder { + private static RoutingFieldMapper toType(FieldMapper in) { + return (RoutingFieldMapper) in; + } - private boolean required = Defaults.REQUIRED; + public static class Builder extends MetadataFieldMapper.Builder { - public Builder() { - super(Defaults.NAME, Defaults.FIELD_TYPE); + final Parameter required = Parameter.boolParam("required", false, m -> toType(m).required, Defaults.REQUIRED); + + protected Builder() { + super(NAME); } - public Builder required(boolean required) { - this.required = required; - return builder; + @Override + protected List> getParameters() { + return Collections.singletonList(required); } @Override public RoutingFieldMapper build(BuilderContext context) { - return new RoutingFieldMapper(fieldType, required); + return new RoutingFieldMapper(required.getValue()); } } - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - Builder builder = new Builder(); - for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = iterator.next(); - String fieldName = entry.getKey(); - Object fieldNode = entry.getValue(); - if (fieldName.equals("required")) { - builder.required(XContentMapValues.nodeBooleanValue(fieldNode, name + ".required")); - iterator.remove(); - } - } - return builder; - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - final Settings indexSettings = context.mapperService().getIndexSettings().getSettings(); - if (fieldType != null) { - return new RoutingFieldMapper(Defaults.FIELD_TYPE, Defaults.REQUIRED); - } else { - return parse(NAME, Collections.emptyMap(), context) - .build(new BuilderContext(indexSettings, new ContentPath(1))); - } - } - } + public static final TypeParser PARSER = new ConfigurableTypeParser( + c -> new RoutingFieldMapper(Defaults.REQUIRED), + c -> new Builder() + ); static final class RoutingFieldType extends StringFieldType { @@ -127,8 +106,8 @@ public class RoutingFieldMapper extends MetadataFieldMapper { private final boolean required; - private RoutingFieldMapper(FieldType fieldType, boolean required) { - super(fieldType, RoutingFieldType.INSTANCE); + private RoutingFieldMapper(boolean required) { + super(RoutingFieldType.INSTANCE); this.required = required; } @@ -152,10 +131,8 @@ public class RoutingFieldMapper extends MetadataFieldMapper { protected void parseCreateField(ParseContext context) throws IOException { String routing = context.sourceToParse().routing(); if (routing != null) { - if (fieldType.indexOptions() != IndexOptions.NONE || fieldType.stored()) { - context.doc().add(new Field(fieldType().name(), routing, fieldType)); - createFieldNamesField(context); - } + context.doc().add(new Field(fieldType().name(), routing, Defaults.FIELD_TYPE)); + createFieldNamesField(context); } } @@ -164,19 +141,4 @@ public class RoutingFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - boolean includeDefaults = params.paramAsBoolean("include_defaults", false); - - // if all are defaults, no sense to write it at all - if (!includeDefaults && required == Defaults.REQUIRED) { - return builder; - } - builder.startObject(CONTENT_TYPE); - if (includeDefaults || required != Defaults.REQUIRED) { - builder.field("required", required); - } - builder.endObject(); - return builder; - } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java index 817f74a17d3..343c9a84d25 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java @@ -20,18 +20,14 @@ package org.elasticsearch.index.mapper; import org.apache.lucene.document.Field; -import org.apache.lucene.document.FieldType; import org.apache.lucene.document.LongPoint; import org.apache.lucene.document.NumericDocValuesField; -import org.apache.lucene.index.DocValuesType; import org.apache.lucene.search.DocValuesFieldExistsQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.util.BytesRef; import org.elasticsearch.Version; import org.elasticsearch.common.Nullable; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType; import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData; @@ -42,7 +38,6 @@ import org.elasticsearch.index.seqno.SequenceNumbers; import java.io.IOException; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Objects; /** @@ -94,33 +89,12 @@ public class SeqNoFieldMapper extends MetadataFieldMapper { public static final String PRIMARY_TERM_NAME = "_primary_term"; public static final String TOMBSTONE_NAME = "_tombstone"; - public static class Defaults { - public static final String NAME = SeqNoFieldMapper.NAME; - public static final MappedFieldType MAPPED_FIELD_TYPE = new SeqNoFieldType(); - public static final FieldType FIELD_TYPE = new FieldType(); - - static { - FIELD_TYPE.setDocValuesType(DocValuesType.SORTED); - FIELD_TYPE.freeze(); - } - } - - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, ParserContext parserContext) - throws MapperParsingException { - throw new MapperParsingException(NAME + " is not configurable"); - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - final Settings indexSettings = context.mapperService().getIndexSettings().getSettings(); - return new SeqNoFieldMapper(); - } - } + public static final TypeParser PARSER = new FixedTypeParser(c -> new SeqNoFieldMapper()); static final class SeqNoFieldType extends SimpleMappedFieldType { + private static final SeqNoFieldType INSTANCE = new SeqNoFieldType(); + SeqNoFieldType() { super(NAME, true, true, TextSearchInfo.SIMPLE_MATCH_ONLY, Collections.emptyMap()); } @@ -202,7 +176,7 @@ public class SeqNoFieldMapper extends MetadataFieldMapper { } public SeqNoFieldMapper() { - super(Defaults.FIELD_TYPE, Defaults.MAPPED_FIELD_TYPE); + super(SeqNoFieldType.INSTANCE); } @Override @@ -252,9 +226,4 @@ public class SeqNoFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder; - } - } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java index bbd3f083203..34ce34c6b5d 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java @@ -30,7 +30,6 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.io.stream.BytesStreamOutput; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; @@ -43,7 +42,6 @@ import org.elasticsearch.index.query.QueryShardException; import java.io.IOException; import java.util.Arrays; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.Function; @@ -71,78 +69,36 @@ public class SourceFieldMapper extends MetadataFieldMapper { } - public static class Builder extends MetadataFieldMapper.Builder { + private static SourceFieldMapper toType(FieldMapper in) { + return (SourceFieldMapper) in; + } - private boolean enabled = Defaults.ENABLED; + public static class Builder extends MetadataFieldMapper.Builder { - private String[] includes = null; - private String[] excludes = null; + private final Parameter enabled = Parameter.boolParam("enabled", false, m -> toType(m).enabled, Defaults.ENABLED); + private final Parameter> includes + = Parameter.stringArrayParam("includes", false, m -> Arrays.asList(toType(m).includes), Collections.emptyList()); + private final Parameter> excludes + = Parameter.stringArrayParam("excludes", false, m -> Arrays.asList(toType(m).excludes), Collections.emptyList()); public Builder() { - super(Defaults.NAME, new FieldType(Defaults.FIELD_TYPE)); + super(Defaults.NAME); } - public Builder enabled(boolean enabled) { - this.enabled = enabled; - return this; - } - - public Builder includes(String[] includes) { - this.includes = includes; - return this; - } - - public Builder excludes(String[] excludes) { - this.excludes = excludes; - return this; + @Override + protected List> getParameters() { + return Arrays.asList(enabled, includes, excludes); } @Override public SourceFieldMapper build(BuilderContext context) { - return new SourceFieldMapper(enabled, includes, excludes); + return new SourceFieldMapper(enabled.getValue(), + includes.getValue().toArray(new String[0]), + excludes.getValue().toArray(new String[0])); } } - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - Builder builder = new Builder(); - - for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = iterator.next(); - String fieldName = entry.getKey(); - Object fieldNode = entry.getValue(); - if (fieldName.equals("enabled")) { - builder.enabled(XContentMapValues.nodeBooleanValue(fieldNode, name + ".enabled")); - iterator.remove(); - } else if (fieldName.equals("includes")) { - List values = (List) fieldNode; - String[] includes = new String[values.size()]; - for (int i = 0; i < includes.length; i++) { - includes[i] = values.get(i).toString(); - } - builder.includes(includes); - iterator.remove(); - } else if (fieldName.equals("excludes")) { - List values = (List) fieldNode; - String[] excludes = new String[values.size()]; - for (int i = 0; i < excludes.length; i++) { - excludes[i] = values.get(i).toString(); - } - builder.excludes(excludes); - iterator.remove(); - } - } - return builder; - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - final Settings indexSettings = context.mapperService().getIndexSettings().getSettings(); - return new SourceFieldMapper(); - } - } + public static final TypeParser PARSER = new ConfigurableTypeParser(c -> new SourceFieldMapper(), c -> new Builder()); static final class SourceFieldType extends MappedFieldType { @@ -177,32 +133,23 @@ public class SourceFieldMapper extends MetadataFieldMapper { private final String[] excludes; private SourceFieldMapper() { - this(Defaults.ENABLED, null, null); + this(Defaults.ENABLED, Strings.EMPTY_ARRAY, Strings.EMPTY_ARRAY); } private SourceFieldMapper(boolean enabled, String[] includes, String[] excludes) { - super(Defaults.FIELD_TYPE, SourceFieldType.INSTANCE); // Only stored. + super(SourceFieldType.INSTANCE); // Only stored. this.enabled = enabled; this.includes = includes; this.excludes = excludes; final boolean filtered = CollectionUtils.isEmpty(includes) == false || CollectionUtils.isEmpty(excludes) == false; - this.filter = enabled && filtered && fieldType.stored() ? XContentMapValues.filter(includes, excludes) : null; - this.complete = enabled && includes == null && excludes == null; + this.filter = enabled && filtered ? XContentMapValues.filter(includes, excludes) : null; + this.complete = enabled && CollectionUtils.isEmpty(includes) && CollectionUtils.isEmpty(excludes); } public boolean enabled() { return enabled; } - public String[] excludes() { - return this.excludes != null ? this.excludes : Strings.EMPTY_ARRAY; - - } - - public String[] includes() { - return this.includes != null ? this.includes : Strings.EMPTY_ARRAY; - } - public boolean isComplete() { return complete; } @@ -238,7 +185,7 @@ public class SourceFieldMapper extends MetadataFieldMapper { @Nullable public BytesReference applyFilters(@Nullable BytesReference originalSource, @Nullable XContentType contentType) throws IOException { - if (enabled && fieldType.stored() && originalSource != null) { + if (enabled && originalSource != null) { // Percolate and tv APIs may not set the source and that is ok, because these APIs will not index any data if (filter != null) { // we don't update the context source if we filter, we want to keep it as is... @@ -264,46 +211,7 @@ public class SourceFieldMapper extends MetadataFieldMapper { } @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - boolean includeDefaults = params.paramAsBoolean("include_defaults", false); - - // all are defaults, no need to write it at all - if (!includeDefaults && enabled == Defaults.ENABLED && includes == null && excludes == null) { - return builder; - } - builder.startObject(contentType()); - if (includeDefaults || enabled != Defaults.ENABLED) { - builder.field("enabled", enabled); - } - - if (includes != null) { - builder.array("includes", includes); - } else if (includeDefaults) { - builder.array("includes", Strings.EMPTY_ARRAY); - } - - if (excludes != null) { - builder.array("excludes", excludes); - } else if (includeDefaults) { - builder.array("excludes", Strings.EMPTY_ARRAY); - } - - builder.endObject(); - return builder; + public ParametrizedFieldMapper.Builder getMergeBuilder() { + return new Builder().init(this); } - - @Override - protected void mergeOptions(FieldMapper other, List conflicts) { - SourceFieldMapper sourceMergeWith = (SourceFieldMapper) other; - if (this.enabled != sourceMergeWith.enabled) { - conflicts.add("Cannot update enabled setting for [_source]"); - } - if (Arrays.equals(includes(), sourceMergeWith.includes()) == false) { - conflicts.add("Cannot update includes setting for [_source]"); - } - if (Arrays.equals(excludes(), sourceMergeWith.excludes()) == false) { - conflicts.add("Cannot update excludes setting for [_source]"); - } - } - } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java index 385435b5f45..3d2c85b4db6 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java @@ -39,8 +39,6 @@ import org.apache.lucene.search.WildcardQuery; import org.apache.lucene.util.BytesRef; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.lucene.search.Queries; -import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; @@ -52,7 +50,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.function.Function; @@ -64,35 +61,18 @@ public class TypeFieldMapper extends MetadataFieldMapper { public static final String CONTENT_TYPE = "_type"; + public static final TypeParser PARSER = new FixedTypeParser(c -> new TypeFieldMapper()); + public static class Defaults { - public static final String NAME = TypeFieldMapper.NAME; - public static final FieldType FIELD_TYPE = new FieldType(); - static { - FIELD_TYPE.setIndexOptions(IndexOptions.NONE); - FIELD_TYPE.setTokenized(false); - FIELD_TYPE.setStored(false); - FIELD_TYPE.setOmitNorms(true); - FIELD_TYPE.freeze(); - } + public static final FieldType NESTED_FIELD_TYPE = new FieldType(); - public static final FieldType NESTED_FIELD_TYPE = new FieldType(FIELD_TYPE); static { NESTED_FIELD_TYPE.setIndexOptions(IndexOptions.DOCS); - } - } - - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - throw new MapperParsingException(NAME + " is not configurable"); - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - final IndexSettings indexSettings = context.mapperService().getIndexSettings(); - return new TypeFieldMapper(Defaults.FIELD_TYPE); + NESTED_FIELD_TYPE.setTokenized(false); + NESTED_FIELD_TYPE.setStored(false); + NESTED_FIELD_TYPE.setOmitNorms(true); + NESTED_FIELD_TYPE.freeze(); } } @@ -275,8 +255,8 @@ public class TypeFieldMapper extends MetadataFieldMapper { } } - private TypeFieldMapper(FieldType fieldType) { - super(fieldType, new TypeFieldType()); + private TypeFieldMapper() { + super(new TypeFieldType()); } @Override @@ -305,9 +285,4 @@ public class TypeFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder; - } - } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java index 5b6ec10bafa..43242bc2b7a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java @@ -20,20 +20,15 @@ package org.elasticsearch.index.mapper; import org.apache.lucene.document.Field; -import org.apache.lucene.document.FieldType; import org.apache.lucene.document.NumericDocValuesField; -import org.apache.lucene.index.DocValuesType; -import org.apache.lucene.index.IndexOptions; import org.apache.lucene.search.DocValuesFieldExistsQuery; import org.apache.lucene.search.Query; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.mapper.ParseContext.Document; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardException; import java.io.IOException; import java.util.Collections; -import java.util.Map; /** Mapper for the _version field. */ public class VersionFieldMapper extends MetadataFieldMapper { @@ -41,31 +36,7 @@ public class VersionFieldMapper extends MetadataFieldMapper { public static final String NAME = "_version"; public static final String CONTENT_TYPE = "_version"; - public static class Defaults { - - public static final String NAME = VersionFieldMapper.NAME; - public static final FieldType FIELD_TYPE = new FieldType(); - public static final MappedFieldType MAPPED_FIELD_TYPE = new VersionFieldType(); - - static { - FIELD_TYPE.setDocValuesType(DocValuesType.NUMERIC); - FIELD_TYPE.setIndexOptions(IndexOptions.NONE); - FIELD_TYPE.freeze(); - } - } - - public static class TypeParser implements MetadataFieldMapper.TypeParser { - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - throw new MapperParsingException(NAME + " is not configurable"); - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - return new VersionFieldMapper(); - } - } + public static final TypeParser PARSER = new FixedTypeParser(c -> new VersionFieldMapper()); static final class VersionFieldType extends MappedFieldType { @@ -92,7 +63,7 @@ public class VersionFieldMapper extends MetadataFieldMapper { } private VersionFieldMapper() { - super(Defaults.FIELD_TYPE, Defaults.MAPPED_FIELD_TYPE); + super(VersionFieldType.INSTANCE); } @Override @@ -129,9 +100,4 @@ public class VersionFieldMapper extends MetadataFieldMapper { return CONTENT_TYPE; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder; - } - } diff --git a/server/src/main/java/org/elasticsearch/indices/IndicesModule.java b/server/src/main/java/org/elasticsearch/indices/IndicesModule.java index 70dd6d778d3..f5351751075 100644 --- a/server/src/main/java/org/elasticsearch/indices/IndicesModule.java +++ b/server/src/main/java/org/elasticsearch/indices/IndicesModule.java @@ -153,18 +153,18 @@ public class IndicesModule extends AbstractModule { // Use a LinkedHashMap for metadataMappers because iteration order matters builtInMetadataMappers = new LinkedHashMap<>(); // _ignored first so that we always load it, even if only _id is requested - builtInMetadataMappers.put(IgnoredFieldMapper.NAME, new IgnoredFieldMapper.TypeParser()); + builtInMetadataMappers.put(IgnoredFieldMapper.NAME, IgnoredFieldMapper.PARSER); // ID second so it will be the first (if no ignored fields) stored field to load // (so will benefit from "fields: []" early termination - builtInMetadataMappers.put(IdFieldMapper.NAME, new IdFieldMapper.TypeParser()); - builtInMetadataMappers.put(RoutingFieldMapper.NAME, new RoutingFieldMapper.TypeParser()); - builtInMetadataMappers.put(IndexFieldMapper.NAME, new IndexFieldMapper.TypeParser()); - builtInMetadataMappers.put(SourceFieldMapper.NAME, new SourceFieldMapper.TypeParser()); - builtInMetadataMappers.put(TypeFieldMapper.NAME, new TypeFieldMapper.TypeParser()); - builtInMetadataMappers.put(VersionFieldMapper.NAME, new VersionFieldMapper.TypeParser()); - builtInMetadataMappers.put(SeqNoFieldMapper.NAME, new SeqNoFieldMapper.TypeParser()); + builtInMetadataMappers.put(IdFieldMapper.NAME, IdFieldMapper.PARSER); + builtInMetadataMappers.put(RoutingFieldMapper.NAME, RoutingFieldMapper.PARSER); + builtInMetadataMappers.put(IndexFieldMapper.NAME, IndexFieldMapper.PARSER); + builtInMetadataMappers.put(SourceFieldMapper.NAME, SourceFieldMapper.PARSER); + builtInMetadataMappers.put(TypeFieldMapper.NAME, TypeFieldMapper.PARSER); + builtInMetadataMappers.put(VersionFieldMapper.NAME, VersionFieldMapper.PARSER); + builtInMetadataMappers.put(SeqNoFieldMapper.NAME, SeqNoFieldMapper.PARSER); //_field_names must be added last so that it has a chance to see all the other mappers - builtInMetadataMappers.put(FieldNamesFieldMapper.NAME, new FieldNamesFieldMapper.TypeParser()); + builtInMetadataMappers.put(FieldNamesFieldMapper.NAME, FieldNamesFieldMapper.PARSER); return Collections.unmodifiableMap(builtInMetadataMappers); } diff --git a/server/src/main/java/org/elasticsearch/indices/mapper/MapperRegistry.java b/server/src/main/java/org/elasticsearch/indices/mapper/MapperRegistry.java index 13e1c08c07d..5fa385da449 100644 --- a/server/src/main/java/org/elasticsearch/indices/mapper/MapperRegistry.java +++ b/server/src/main/java/org/elasticsearch/indices/mapper/MapperRegistry.java @@ -48,7 +48,7 @@ public final class MapperRegistry { this.metadataMapperParsers = Collections.unmodifiableMap(new LinkedHashMap<>(metadataMapperParsers)); // add the _all field mapper for indices created in 6x Map metadata6x = new LinkedHashMap<>(); - metadata6x.put(AllFieldMapper.NAME, new AllFieldMapper.TypeParser()); + metadata6x.put(AllFieldMapper.NAME, AllFieldMapper.PARSER); metadata6x.putAll(metadataMapperParsers); this.metadataMapperParsers6x = Collections.unmodifiableMap(metadata6x); this.fieldFilter = fieldFilter; diff --git a/server/src/main/java/org/elasticsearch/search/suggest/completion/context/ContextMappings.java b/server/src/main/java/org/elasticsearch/search/suggest/completion/context/ContextMappings.java index 8ff446d1c5d..7ebb62806ff 100644 --- a/server/src/main/java/org/elasticsearch/search/suggest/completion/context/ContextMappings.java +++ b/server/src/main/java/org/elasticsearch/search/suggest/completion/context/ContextMappings.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import static org.elasticsearch.search.suggest.completion.context.ContextMapping.FIELD_NAME; import static org.elasticsearch.search.suggest.completion.context.ContextMapping.FIELD_TYPE; @@ -297,4 +298,9 @@ public class ContextMappings implements ToXContent, Iterable> ContextMappings other = ((ContextMappings) obj); return contextMappings.equals(other.contextMappings); } + + @Override + public String toString() { + return contextMappings.stream().map(ContextMapping::toString).collect(Collectors.joining(",", "[", "]")); + } } diff --git a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateServiceTests.java b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateServiceTests.java index 883bc878771..5127012265a 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateServiceTests.java @@ -19,7 +19,6 @@ package org.elasticsearch.cluster.metadata; -import org.apache.lucene.document.FieldType; import org.apache.lucene.search.Query; import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; @@ -35,17 +34,18 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.xcontent.LoggingDeprecationHandler; import org.elasticsearch.common.xcontent.NamedXContentRegistry; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.env.Environment; import org.elasticsearch.index.Index; +import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MetadataFieldMapper; +import org.elasticsearch.index.mapper.ParametrizedFieldMapper; import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.TextSearchInfo; import org.elasticsearch.index.query.QueryShardContext; @@ -75,6 +75,7 @@ import java.util.stream.Collectors; import static java.util.Collections.singletonList; import static org.elasticsearch.cluster.metadata.MetadataIndexTemplateService.DEFAULT_TIMESTAMP_FIELD; import static org.elasticsearch.common.settings.Settings.builder; +import static org.elasticsearch.index.mapper.ParametrizedFieldMapper.Parameter; import static org.elasticsearch.indices.ShardLimitValidatorTests.createTestShardLimitService; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsStringIgnoringCase; @@ -1550,75 +1551,77 @@ public class MetadataIndexTemplateServiceTests extends ESSingleNodeTestCase { @Override public Map getMetadataMappers() { - return Collections.singletonMap("_data_stream_timestamp", new MetadataFieldMapper.TypeParser() { + return Collections.singletonMap("_data_stream_timestamp", new MetadataFieldMapper.ConfigurableTypeParser( + c -> new MetadataTimestampFieldMapper(false), + c -> new MetadataTimestampFieldBuilder()) + ); + } + } + private static MetadataTimestampFieldMapper toType(FieldMapper in) { + return (MetadataTimestampFieldMapper) in; + } + + public static class MetadataTimestampFieldBuilder extends MetadataFieldMapper.Builder { + + private final Parameter enabled = Parameter.boolParam("enabled", true, m -> toType(m).enabled, false); + + protected MetadataTimestampFieldBuilder() { + super("_data_stream_timestamp"); + } + + @Override + protected List> getParameters() { + return Collections.singletonList(enabled); + } + + @Override + public MetadataFieldMapper build(Mapper.BuilderContext context) { + return new MetadataTimestampFieldMapper(enabled.getValue()); + } + } + + public static class MetadataTimestampFieldMapper extends MetadataFieldMapper { + final boolean enabled; + + public MetadataTimestampFieldMapper(boolean enabled) { + super(new MappedFieldType("_data_stream_timestamp", false, false, TextSearchInfo.NONE, Collections.emptyMap()) { @Override - public MetadataFieldMapper.Builder parse(String name, - Map node, - ParserContext parserContext) throws MapperParsingException { - Boolean enabled = (Boolean) node.remove("enabled"); - return new MetadataFieldMapper.Builder(name, new FieldType()) { - @Override - public MetadataFieldMapper build(Mapper.BuilderContext context) { - return newInstance(enabled); - } - }; + public String typeName() { + return "_data_stream_timestamp"; } @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext parserContext) { - return newInstance(null); + public Query termQuery(Object value, QueryShardContext context) { + return null; } - MetadataFieldMapper newInstance(Boolean enabled) { - FieldType fieldType = new FieldType(); - fieldType.freeze(); - MappedFieldType mappedFieldType = - new MappedFieldType("_data_stream_timestamp", false, false, TextSearchInfo.NONE, Collections.emptyMap()) { - @Override - public String typeName() { - return "_data_stream_timestamp"; - } - - @Override - public Query termQuery(Object value, QueryShardContext context) { - return null; - } - - @Override - public Query existsQuery(QueryShardContext context) { - return null; - } - }; - return new MetadataFieldMapper(fieldType, mappedFieldType) { - @Override - public void preParse(ParseContext context) throws IOException { - - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { - - } - - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - if (enabled == null) { - return builder; - } - - builder.startObject(simpleName()); - builder.field("enabled", enabled); - return builder.endObject(); - } - - @Override - protected String contentType() { - return "_data_stream_timestamp"; - } - }; + @Override + public Query existsQuery(QueryShardContext context) { + return null; } }); + this.enabled = enabled; + } + + @Override + public ParametrizedFieldMapper.Builder getMergeBuilder() { + return new MetadataTimestampFieldBuilder().init(this); + } + + @Override + public void preParse(ParseContext context) { + + } + + @Override + protected void parseCreateField(ParseContext context) { + + } + + @Override + protected String contentType() { + return "_data_stream_timestamp"; } } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ExternalFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/ExternalFieldMapperTests.java index 7b40b709297..edac924388b 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ExternalFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ExternalFieldMapperTests.java @@ -69,7 +69,7 @@ public class ExternalFieldMapperTests extends ESSingleNodeTestCase { IndexService indexService = createIndex("test", settings); MapperRegistry mapperRegistry = new MapperRegistry( singletonMap(ExternalMapperPlugin.EXTERNAL, new ExternalMapper.TypeParser(ExternalMapperPlugin.EXTERNAL, "foo")), - singletonMap(ExternalMetadataMapper.CONTENT_TYPE, new ExternalMetadataMapper.TypeParser()), MapperPlugin.NOOP_FIELD_FILTER); + singletonMap(ExternalMetadataMapper.CONTENT_TYPE, ExternalMetadataMapper.PARSER), MapperPlugin.NOOP_FIELD_FILTER); Supplier queryShardContext = () -> { return indexService.newQueryShardContext(0, null, () -> { throw new UnsupportedOperationException(); }, null); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMapperPlugin.java b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMapperPlugin.java index 0824e1e0afc..b08fd41071b 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMapperPlugin.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMapperPlugin.java @@ -46,7 +46,7 @@ public class ExternalMapperPlugin extends Plugin implements MapperPlugin { @Override public Map getMetadataMappers() { - return Collections.singletonMap(ExternalMetadataMapper.CONTENT_TYPE, new ExternalMetadataMapper.TypeParser()); + return Collections.singletonMap(ExternalMetadataMapper.CONTENT_TYPE, ExternalMetadataMapper.PARSER); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java index 7e374a4087d..62507f9b38c 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java @@ -20,14 +20,12 @@ package org.elasticsearch.index.mapper; import org.apache.lucene.document.Field.Store; -import org.apache.lucene.document.FieldType; import org.apache.lucene.document.StringField; -import org.elasticsearch.common.xcontent.XContentBuilder; import java.io.IOException; import java.util.Collections; import java.util.Iterator; -import java.util.Map; +import java.util.List; public class ExternalMetadataMapper extends MetadataFieldMapper { @@ -36,7 +34,7 @@ public class ExternalMetadataMapper extends MetadataFieldMapper { static final String FIELD_VALUE = "true"; protected ExternalMetadataMapper() { - super(new FieldType(), new BooleanFieldMapper.BooleanFieldType(FIELD_NAME)); + super(new BooleanFieldMapper.BooleanFieldType(FIELD_NAME)); } @Override @@ -49,11 +47,6 @@ public class ExternalMetadataMapper extends MetadataFieldMapper { return Collections.emptyIterator(); } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - return builder.startObject(CONTENT_TYPE).endObject(); - } - @Override protected String contentType() { return CONTENT_TYPE; @@ -68,10 +61,15 @@ public class ExternalMetadataMapper extends MetadataFieldMapper { context.doc().add(new StringField(FIELD_NAME, FIELD_VALUE, Store.YES)); } - public static class Builder extends MetadataFieldMapper.Builder { + public static class Builder extends MetadataFieldMapper.Builder { protected Builder() { - super(FIELD_NAME, new FieldType()); + super(FIELD_NAME); + } + + @Override + protected List> getParameters() { + return Collections.emptyList(); } @Override @@ -81,19 +79,6 @@ public class ExternalMetadataMapper extends MetadataFieldMapper { } - public static class TypeParser implements MetadataFieldMapper.TypeParser { - - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, - ParserContext parserContext) throws MapperParsingException { - return new Builder(); - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - return new ExternalMetadataMapper(); - } - - } + public static final TypeParser PARSER = new ConfigurableTypeParser(c -> new ExternalMetadataMapper(), c -> new Builder()); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java index 2b281d310b9..6ad1a638893 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldMapperTests.java @@ -81,10 +81,10 @@ public class FieldNamesFieldMapperTests extends ESSingleNodeTestCase { FieldNamesFieldMapper fieldNamesMapper = docMapper.metadataMapper(FieldNamesFieldMapper.class); assertFalse(fieldNamesMapper.fieldType().hasDocValues()); - assertEquals(IndexOptions.DOCS, fieldNamesMapper.fieldType.indexOptions()); - assertFalse(fieldNamesMapper.fieldType.tokenized()); - assertFalse(fieldNamesMapper.fieldType.stored()); - assertTrue(fieldNamesMapper.fieldType.omitNorms()); + assertEquals(IndexOptions.DOCS, FieldNamesFieldMapper.Defaults.FIELD_TYPE.indexOptions()); + assertFalse(FieldNamesFieldMapper.Defaults.FIELD_TYPE.tokenized()); + assertFalse(FieldNamesFieldMapper.Defaults.FIELD_TYPE.stored()); + assertTrue(FieldNamesFieldMapper.Defaults.FIELD_TYPE.omitNorms()); } public void testInjectIntoDocDuringParsing() throws Exception { @@ -124,7 +124,7 @@ public class FieldNamesFieldMapperTests extends ESSingleNodeTestCase { XContentType.JSON)); assertFieldNames(set("field"), doc); - assertWarnings(FieldNamesFieldMapper.TypeParser.ENABLED_DEPRECATION_MESSAGE.replace("{}", "test")); + assertWarnings(FieldNamesFieldMapper.ENABLED_DEPRECATION_MESSAGE); } public void testDisabled() throws Exception { @@ -144,7 +144,7 @@ public class FieldNamesFieldMapperTests extends ESSingleNodeTestCase { XContentType.JSON)); assertNull(doc.rootDoc().get("_field_names")); - assertWarnings(FieldNamesFieldMapper.TypeParser.ENABLED_DEPRECATION_MESSAGE.replace("{}", "test")); + assertWarnings(FieldNamesFieldMapper.ENABLED_DEPRECATION_MESSAGE.replace("{}", "test")); } public void testMergingMappings() throws Exception { @@ -164,6 +164,6 @@ public class FieldNamesFieldMapperTests extends ESSingleNodeTestCase { DocumentMapper mapperEnabled = mapperService.merge("type", new CompressedXContent(enabledMapping), MapperService.MergeReason.MAPPING_UPDATE); assertTrue(mapperEnabled.metadataMapper(FieldNamesFieldMapper.class).fieldType().isEnabled()); - assertWarnings(FieldNamesFieldMapper.TypeParser.ENABLED_DEPRECATION_MESSAGE.replace("{}", "test")); + assertWarnings(FieldNamesFieldMapper.ENABLED_DEPRECATION_MESSAGE.replace("{}", "test")); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldTypeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldTypeTests.java index f8293d2a186..3545ee2df90 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldTypeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/FieldNamesFieldTypeTests.java @@ -38,7 +38,7 @@ public class FieldNamesFieldTypeTests extends ESTestCase { public void testTermQuery() { - FieldNamesFieldMapper.FieldNamesFieldType fieldNamesFieldType = new FieldNamesFieldMapper.FieldNamesFieldType(); + FieldNamesFieldMapper.FieldNamesFieldType fieldNamesFieldType = new FieldNamesFieldMapper.FieldNamesFieldType(true); KeywordFieldMapper.KeywordFieldType fieldType = new KeywordFieldMapper.KeywordFieldType("field_name"); Settings settings = settings(Version.CURRENT).build(); @@ -52,12 +52,12 @@ public class FieldNamesFieldTypeTests extends ESTestCase { QueryShardContext queryShardContext = new QueryShardContext(0, indexSettings, BigArrays.NON_RECYCLING_INSTANCE, null, null, mapperService, null, null, null, null, null, null, () -> 0L, null, null, () -> true, null); - fieldNamesFieldType.setEnabled(true); - Query termQuery = fieldNamesFieldType.termQuery("field_name", queryShardContext); + Query termQuery = fieldNamesFieldType.termQuery("field_name", queryShardContext); assertEquals(new TermQuery(new Term(FieldNamesFieldMapper.CONTENT_TYPE, "field_name")), termQuery); assertWarnings("terms query on the _field_names field is deprecated and will be removed, use exists query instead"); - fieldNamesFieldType.setEnabled(false); - IllegalStateException e = expectThrows(IllegalStateException.class, () -> fieldNamesFieldType.termQuery("field_name", null)); + + FieldNamesFieldMapper.FieldNamesFieldType unsearchable = new FieldNamesFieldMapper.FieldNamesFieldType(false); + IllegalStateException e = expectThrows(IllegalStateException.class, () -> unsearchable.termQuery("field_name", null)); assertEquals("Cannot run [exists] queries if the [_field_names] field is disabled", e.getMessage()); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java index ce0f4027bda..58903c310d0 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ParametrizedMapperTests.java @@ -99,12 +99,12 @@ public class ParametrizedMapperTests extends ESSingleNodeTestCase { final Parameter variable = Parameter.stringParam("variable", true, m -> toType(m).variable, "default").acceptsNull(); final Parameter wrapper - = new Parameter<>("wrapper", true, () -> new StringWrapper("default"), + = new Parameter<>("wrapper", false, () -> new StringWrapper("default"), (n, c, o) -> { if (o == null) return null; return new StringWrapper(o.toString()); }, - m -> toType(m).wrapper).setSerializer((b, n, v) -> b.field(n, v.name)); + m -> toType(m).wrapper).setSerializer((b, n, v) -> b.field(n, v.name), v -> "wrapper_" + v.name); final Parameter intValue = Parameter.intParam("int_value", true, m -> toType(m).intValue, 5) .setValidator(n -> { if (n > 50) { @@ -112,7 +112,7 @@ public class ParametrizedMapperTests extends ESSingleNodeTestCase { } }); final Parameter analyzer - = Parameter.analyzerParam("analyzer", true, m -> toType(m).analyzer, () -> Lucene.KEYWORD_ANALYZER); + = Parameter.analyzerParam("analyzer", false, m -> toType(m).analyzer, () -> Lucene.KEYWORD_ANALYZER); final Parameter searchAnalyzer = Parameter.analyzerParam("search_analyzer", true, m -> toType(m).searchAnalyzer, analyzer::getValue); final Parameter index = Parameter.boolParam("index", false, m -> toType(m).index, true); @@ -345,6 +345,12 @@ public class ParametrizedMapperTests extends ESSingleNodeTestCase { TestMapper mapper = fromMapping(mapping); assertEquals("wrapped value", mapper.wrapper.name); assertEquals("{\"field\":" + mapping + "}", Strings.toString(mapper)); + + String conflict = "{\"type\":\"test_mapper\",\"wrapper\":\"new value\",\"required\":\"value\"}"; + TestMapper toMerge = fromMapping(conflict); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> mapper.merge(toMerge)); + assertEquals("Mapper for [field] conflicts with existing mapper:\n" + + "\tCannot update parameter [wrapper] from [wrapper_wrapped value] to [wrapper_new value]", e.getMessage()); } // test validator @@ -383,6 +389,12 @@ public class ParametrizedMapperTests extends ESSingleNodeTestCase { String badAnalyzer = "{\"type\":\"test_mapper\",\"analyzer\":\"wibble\",\"required\":\"value\"}"; IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> fromMapping(badAnalyzer)); assertEquals("analyzer [wibble] has not been configured in mappings", e.getMessage()); + + TestMapper original = mapper; + TestMapper toMerge = fromMapping(mapping); + e = expectThrows(IllegalArgumentException.class, () -> original.merge(toMerge)); + assertEquals("Mapper for [field] conflicts with existing mapper:\n" + + "\tCannot update parameter [analyzer] from [default] to [_standard]", e.getMessage()); } public void testDeprecatedParameters() { diff --git a/server/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java index f7956d72085..9ebef5b7bdd 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/SourceFieldMapperTests.java @@ -97,7 +97,7 @@ public class SourceFieldMapperTests extends ESSingleNodeTestCase { public void testExcludes() throws Exception { String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") - .startObject("_source").array("excludes", new String[]{"path1*"}).endObject() + .startObject("_source").array("excludes", "path1*").endObject() .endObject().endObject()); DocumentMapper documentMapper = createIndex("test").mapperService().documentMapperParser() @@ -126,7 +126,7 @@ public class SourceFieldMapperTests extends ESSingleNodeTestCase { String mapping2 = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") .startObject("_source").field("enabled", false).endObject() .endObject().endObject()); - assertConflicts(mapping1, mapping2, parser, "Cannot update enabled setting for [_source]"); + assertConflicts(mapping1, mapping2, parser, "Cannot update parameter [enabled] from [true] to [false]"); // not changing is ok String mapping3 = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") @@ -141,13 +141,13 @@ public class SourceFieldMapperTests extends ESSingleNodeTestCase { String mapping1 = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") .startObject("_source").array("includes", "foo.*").endObject() .endObject().endObject()); - assertConflicts(defaultMapping, mapping1, parser, "Cannot update includes setting for [_source]"); - assertConflicts(mapping1, defaultMapping, parser, "Cannot update includes setting for [_source]"); + assertConflicts(defaultMapping, mapping1, parser, "Cannot update parameter [includes] from [[]] to [[foo.*]]"); + assertConflicts(mapping1, defaultMapping, parser, "Cannot update parameter [includes] from [[foo.*]] to [[]]"); String mapping2 = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") .startObject("_source").array("includes", "foo.*", "bar.*").endObject() .endObject().endObject()); - assertConflicts(mapping1, mapping2, parser, "Cannot update includes setting for [_source]"); + assertConflicts(mapping1, mapping2, parser, "Cannot update parameter [includes] from [[foo.*]] to [[foo.*, bar.*]]"); // not changing is ok assertConflicts(mapping1, mapping1, parser); @@ -159,13 +159,13 @@ public class SourceFieldMapperTests extends ESSingleNodeTestCase { String mapping1 = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") .startObject("_source").array("excludes", "foo.*").endObject() .endObject().endObject()); - assertConflicts(defaultMapping, mapping1, parser, "Cannot update excludes setting for [_source]"); - assertConflicts(mapping1, defaultMapping, parser, "Cannot update excludes setting for [_source]"); + assertConflicts(defaultMapping, mapping1, parser, "Cannot update parameter [excludes] from [[]] to [[foo.*]]"); + assertConflicts(mapping1, defaultMapping, parser, "Cannot update parameter [excludes] from [[foo.*]] to [[]]"); String mapping2 = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type") .startObject("_source").array("excludes", "foo.*", "bar.*").endObject() .endObject().endObject()); - assertConflicts(mapping1, mapping2, parser, "Cannot update excludes setting for [_source]"); + assertConflicts(mapping1, mapping2, parser, "Cannot update parameter [excludes]"); // not changing is ok assertConflicts(mapping1, mapping1, parser); diff --git a/server/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java b/server/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java index 3500753e758..93fe7e6e079 100644 --- a/server/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/QueryShardContextTests.java @@ -30,10 +30,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.AbstractLeafOrdinalsFieldData; -import org.elasticsearch.index.mapper.ContentPath; 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.TextFieldMapper; import org.elasticsearch.test.ESTestCase; @@ -117,8 +115,7 @@ public class QueryShardContextTests extends ESTestCase { final String clusterAlias = randomBoolean() ? null : "remote_cluster"; QueryShardContext context = createQueryShardContext(IndexMetadata.INDEX_UUID_NA_VALUE, clusterAlias); - Mapper.BuilderContext ctx = new Mapper.BuilderContext(context.getIndexSettings().getSettings(), new ContentPath()); - IndexFieldMapper mapper = new IndexFieldMapper.Builder().build(ctx); + IndexFieldMapper mapper = new IndexFieldMapper(); IndexFieldData forField = context.getForField(mapper.fieldType()); String expected = clusterAlias == null ? context.getIndexSettings().getIndexMetadata().getIndex().getName() diff --git a/server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java b/server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java index dce442f01ec..f7993ae7e16 100644 --- a/server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java +++ b/server/src/test/java/org/elasticsearch/index/query/QueryStringQueryBuilderTests.java @@ -1105,7 +1105,7 @@ public class QueryStringQueryBuilderTests extends AbstractQueryTestCase parse(String name, Map node, ParserContext parserContext) - throws MapperParsingException { - return null; - } - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext context) { - return null; - } - } + private static final MetadataFieldMapper.TypeParser PARSER = new MetadataFieldMapper.ConfigurableTypeParser(c -> null, c -> null); private final List fakePlugins = Arrays.asList(new MapperPlugin() { @Override @@ -82,7 +71,7 @@ public class IndicesModuleTests extends ESTestCase { } @Override public Map getMetadataMappers() { - return Collections.singletonMap("fake-metadata-mapper", new FakeMetadataMapperParser()); + return Collections.singletonMap("fake-metadata-mapper", PARSER); } }); @@ -180,7 +169,7 @@ public class IndicesModuleTests extends ESTestCase { List plugins = Arrays.asList(new MapperPlugin() { @Override public Map getMetadataMappers() { - return Collections.singletonMap(IdFieldMapper.NAME, new FakeMetadataMapperParser()); + return Collections.singletonMap(IdFieldMapper.NAME, PARSER); } }); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, @@ -192,7 +181,7 @@ public class IndicesModuleTests extends ESTestCase { MapperPlugin plugin = new MapperPlugin() { @Override public Map getMetadataMappers() { - return Collections.singletonMap("foo", new FakeMetadataMapperParser()); + return Collections.singletonMap("foo", PARSER); } }; List plugins = Arrays.asList(plugin, plugin); @@ -205,7 +194,7 @@ public class IndicesModuleTests extends ESTestCase { List plugins = Arrays.asList(new MapperPlugin() { @Override public Map getMetadataMappers() { - return Collections.singletonMap(FieldNamesFieldMapper.NAME, new FakeMetadataMapperParser()); + return Collections.singletonMap(FieldNamesFieldMapper.NAME, PARSER); } }); IllegalArgumentException e = expectThrows(IllegalArgumentException.class, diff --git a/x-pack/plugin/data-streams/src/main/java/org/elasticsearch/xpack/datastreams/DataStreamsPlugin.java b/x-pack/plugin/data-streams/src/main/java/org/elasticsearch/xpack/datastreams/DataStreamsPlugin.java index 2093c14b200..60e85de6d57 100644 --- a/x-pack/plugin/data-streams/src/main/java/org/elasticsearch/xpack/datastreams/DataStreamsPlugin.java +++ b/x-pack/plugin/data-streams/src/main/java/org/elasticsearch/xpack/datastreams/DataStreamsPlugin.java @@ -53,7 +53,7 @@ public class DataStreamsPlugin extends Plugin implements ActionPlugin, MapperPlu @Override public Map getMetadataMappers() { - return Collections.singletonMap(DataStreamTimestampFieldMapper.NAME, new DataStreamTimestampFieldMapper.TypeParser()); + return Collections.singletonMap(DataStreamTimestampFieldMapper.NAME, DataStreamTimestampFieldMapper.PARSER); } @Override diff --git a/x-pack/plugin/data-streams/src/main/java/org/elasticsearch/xpack/datastreams/mapper/DataStreamTimestampFieldMapper.java b/x-pack/plugin/data-streams/src/main/java/org/elasticsearch/xpack/datastreams/mapper/DataStreamTimestampFieldMapper.java index 5b3e1244c68..ffac7bfa0f1 100644 --- a/x-pack/plugin/data-streams/src/main/java/org/elasticsearch/xpack/datastreams/mapper/DataStreamTimestampFieldMapper.java +++ b/x-pack/plugin/data-streams/src/main/java/org/elasticsearch/xpack/datastreams/mapper/DataStreamTimestampFieldMapper.java @@ -6,23 +6,20 @@ package org.elasticsearch.xpack.datastreams.mapper; -import org.apache.lucene.document.FieldType; import org.apache.lucene.index.DocValuesType; -import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; import org.apache.lucene.search.Query; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; -import org.elasticsearch.common.xcontent.support.XContentMapValues; import org.elasticsearch.index.mapper.DateFieldMapper; import org.elasticsearch.index.mapper.MappingLookup; import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.Mapper; -import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MetadataFieldMapper; +import org.elasticsearch.index.mapper.ParametrizedFieldMapper; import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.TextSearchInfo; import org.elasticsearch.index.query.QueryShardContext; @@ -31,10 +28,8 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.util.Arrays; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Objects; import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder; @@ -43,16 +38,6 @@ public class DataStreamTimestampFieldMapper extends MetadataFieldMapper { public static final String NAME = "_data_stream_timestamp"; private static final String DEFAULT_PATH = "@timestamp"; - public static class Defaults { - - public static final FieldType TIMESTAMP_FIELD_TYPE = new FieldType(); - - static { - TIMESTAMP_FIELD_TYPE.setIndexOptions(IndexOptions.NONE); - TIMESTAMP_FIELD_TYPE.freeze(); - } - } - // For now the field shouldn't be useable in searches. // In the future it should act as an alias to the actual data stream timestamp field. public static final class TimestampFieldType extends MappedFieldType { @@ -78,58 +63,47 @@ public class DataStreamTimestampFieldMapper extends MetadataFieldMapper { } - public static class Builder extends MetadataFieldMapper.Builder { + private static DataStreamTimestampFieldMapper toType(FieldMapper in) { + return (DataStreamTimestampFieldMapper) in; + } - private boolean enabled; + public static class Builder extends MetadataFieldMapper.Builder { + + private final Parameter enabled = Parameter.boolParam("enabled", false, m -> toType(m).enabled, false); public Builder() { - super(NAME, Defaults.TIMESTAMP_FIELD_TYPE); + super(NAME); } - public void setEnabled(boolean enabled) { - this.enabled = enabled; + @Override + protected List> getParameters() { + return Collections.singletonList(enabled); } @Override public MetadataFieldMapper build(BuilderContext context) { - return new DataStreamTimestampFieldMapper(fieldType, new TimestampFieldType(), enabled); + return new DataStreamTimestampFieldMapper(new TimestampFieldType(), enabled.getValue()); } } - public static class TypeParser implements MetadataFieldMapper.TypeParser { + public static final TypeParser PARSER = new ConfigurableTypeParser( + c -> new DataStreamTimestampFieldMapper(new TimestampFieldType(), false), + c -> new Builder() + ); - @Override - public MetadataFieldMapper.Builder parse(String name, Map node, ParserContext parserContext) - throws MapperParsingException { - Builder builder = new Builder(); - for (Iterator> iterator = node.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = iterator.next(); - String fieldName = entry.getKey(); - Object fieldNode = entry.getValue(); - if (fieldName.equals("enabled")) { - builder.setEnabled(XContentMapValues.nodeBooleanValue(fieldNode, name + ".enabled")); - iterator.remove(); - } - } - return builder; - } - - @Override - public MetadataFieldMapper getDefault(MappedFieldType fieldType, ParserContext parserContext) { - return new DataStreamTimestampFieldMapper(Defaults.TIMESTAMP_FIELD_TYPE, new TimestampFieldType(), false); - } - } - - private final String path; + private final String path = DEFAULT_PATH; private final boolean enabled; - private DataStreamTimestampFieldMapper(FieldType fieldType, MappedFieldType mappedFieldType, boolean enabled) { - super(fieldType, mappedFieldType); - this.path = DEFAULT_PATH; + private DataStreamTimestampFieldMapper(MappedFieldType mappedFieldType, boolean enabled) { + super(mappedFieldType); this.enabled = enabled; } @Override + public ParametrizedFieldMapper.Builder getMergeBuilder() { + return new Builder().init(this); + } + public void doValidate(MappingLookup lookup) { if (enabled == false) { // not configured, so skip the validation @@ -227,37 +201,8 @@ public class DataStreamTimestampFieldMapper extends MetadataFieldMapper { } } - @Override - public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { - if (enabled == false) { - return builder; - } - - builder.startObject(simpleName()); - builder.field("enabled", enabled); - return builder.endObject(); - } - @Override protected String contentType() { return NAME; } - - @Override - protected boolean indexedByDefault() { - return false; - } - - @Override - protected boolean docValuesByDefault() { - return false; - } - - @Override - protected void mergeOptions(FieldMapper other, List conflicts) { - DataStreamTimestampFieldMapper otherTimestampFieldMapper = (DataStreamTimestampFieldMapper) other; - if (Objects.equals(enabled, otherTimestampFieldMapper.enabled) == false) { - conflicts.add("cannot update enabled setting for [_data_stream_timestamp]"); - } - } } diff --git a/x-pack/plugin/data-streams/src/test/java/org/elasticsearch/xpack/datastreams/mapper/DataStreamTimestampFieldMapperTests.java b/x-pack/plugin/data-streams/src/test/java/org/elasticsearch/xpack/datastreams/mapper/DataStreamTimestampFieldMapperTests.java index 82260d3f6c8..1735c8a7c2e 100644 --- a/x-pack/plugin/data-streams/src/test/java/org/elasticsearch/xpack/datastreams/mapper/DataStreamTimestampFieldMapperTests.java +++ b/x-pack/plugin/data-streams/src/test/java/org/elasticsearch/xpack/datastreams/mapper/DataStreamTimestampFieldMapperTests.java @@ -287,12 +287,12 @@ public class DataStreamTimestampFieldMapperTests extends ESSingleNodeTestCase { "{\"type\":{\"_data_stream_timestamp\":{\"enabled\":false}, \"properties\": {\"@timestamp\": {\"type\": \"date\"}}}}}"; String mapping2 = "{\"type\":{\"_data_stream_timestamp\":{\"enabled\":true}, \"properties\": {\"@timestamp2\": " + "{\"type\": \"date\"},\"@timestamp\": {\"type\": \"date\"}}}})"; - assertConflicts(mapping1, mapping2, parser, "cannot update enabled setting for [_data_stream_timestamp]"); + assertConflicts(mapping1, mapping2, parser, "Mapper for [_data_stream_timestamp]", "[enabled] from [false] to [true]"); mapping1 = "{\"type\":{\"properties\":{\"@timestamp\": {\"type\": \"date\"}}}}}"; mapping2 = "{\"type\":{\"_data_stream_timestamp\":{\"enabled\":true}, \"properties\": " + "{\"@timestamp2\": {\"type\": \"date\"},\"@timestamp\": {\"type\": \"date\"}}}})"; - assertConflicts(mapping1, mapping2, parser, "cannot update enabled setting for [_data_stream_timestamp]"); + assertConflicts(mapping1, mapping2, parser, "Mapper for [_data_stream_timestamp]", "[enabled] from [false] to [true]"); } }