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 347d018cb72c9..54977637e26ab 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 @@ -65,17 +65,6 @@ private RankFeatureMetaFieldMapper() { super(RankFeatureMetaFieldType.INSTANCE); } - @Override - public void preParse(ParseContext context) {} - - @Override - protected void parseCreateField(ParseContext context) { - throw new AssertionError("Should never be called"); - } - - @Override - public void postParse(ParseContext context) {} - @Override protected String contentType() { return CONTENT_TYPE; diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RankFeatureMetaFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RankFeatureMetaFieldMapperTests.java index e11a08597dc24..36e55012da849 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RankFeatureMetaFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RankFeatureMetaFieldMapperTests.java @@ -69,6 +69,7 @@ public void testDocumentParsingFailsOnMetaField() throws Exception { BytesReference bytes = BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(rfMetaField, 0).endObject()); MapperParsingException e = expectThrows(MapperParsingException.class, () -> mapper.parse(new SourceToParse("test", "1", bytes, XContentType.JSON))); - assertTrue(e.getMessage().contains("Field ["+ rfMetaField + "] is a metadata field and cannot be added inside a document.")); + assertTrue( + e.getCause().getMessage().contains("Field ["+ rfMetaField + "] is a metadata field and cannot be added inside a document.")); } } 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 9a5bd5591c757..86a1123287294 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 @@ -79,23 +79,9 @@ public boolean enabled() { return this.enabled.value(); } - @Override - public void preParse(ParseContext context) { - } - @Override public void postParse(ParseContext context) throws IOException { // we post parse it so we get the size stored, possibly compressed (source will be preParse) - super.parse(context); - } - - @Override - public void parse(ParseContext context) { - // nothing to do here, we call the parent in postParse - } - - @Override - protected void parseCreateField(ParseContext context) { if (enabled.value() == false) { return; } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index 0d198e854aca6..1fa8268eccb4b 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -391,10 +391,7 @@ private static void innerParseObject(ParseContext context, ObjectMapper mapper, if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); paths = splitAndValidatePath(currentFieldName); - if (context.mapperService().isMetadataField(context.path().pathAsText(currentFieldName))) { - throw new MapperParsingException("Field [" + currentFieldName + "] is a metadata field and cannot be added inside" - + " a document. Use the index API request parameters."); - } else if (containsDisabledObjectMapper(mapper, paths)) { + if (containsDisabledObjectMapper(mapper, paths)) { parser.nextToken(); parser.skipChildren(); } @@ -482,7 +479,7 @@ private static void parseObject(final ParseContext context, ObjectMapper mapper, String[] paths) throws IOException { assert currentFieldName != null; - Mapper objectMapper = getMapper(mapper, currentFieldName, paths); + Mapper objectMapper = getMapper(context, mapper, currentFieldName, paths); if (objectMapper != null) { context.path().add(currentFieldName); parseObjectOrField(context, objectMapper); @@ -519,7 +516,7 @@ private static void parseArray(ParseContext context, ObjectMapper parentMapper, String[] paths) throws IOException { String arrayFieldName = lastFieldName; - Mapper mapper = getMapper(parentMapper, lastFieldName, paths); + Mapper mapper = getMapper(context, parentMapper, lastFieldName, paths); if (mapper != null) { // There is a concrete mapper for this field already. Need to check if the mapper // expects an array, if so we pass the context straight to the mapper and if not @@ -596,7 +593,7 @@ private static void parseValue(final ParseContext context, ObjectMapper parentMa throw new MapperParsingException("object mapping [" + parentMapper.name() + "] trying to serialize a value with" + " no field associated with it, current value [" + context.parser().textOrNull() + "]"); } - Mapper mapper = getMapper(parentMapper, currentFieldName, paths); + Mapper mapper = getMapper(context, parentMapper, currentFieldName, paths); if (mapper != null) { parseObjectOrField(context, mapper); } else { @@ -613,7 +610,7 @@ private static void parseValue(final ParseContext context, ObjectMapper parentMa private static void parseNullValue(ParseContext context, ObjectMapper parentMapper, String lastFieldName, String[] paths) throws IOException { // we can only handle null values if we have mappings for them - Mapper mapper = getMapper(parentMapper, lastFieldName, paths); + Mapper mapper = getMapper(context, parentMapper, lastFieldName, paths); if (mapper != null) { // TODO: passing null to an object seems bogus? parseObjectOrField(context, mapper); @@ -881,9 +878,16 @@ private static ObjectMapper.Dynamic dynamicOrDefault(ObjectMapper parentMapper, } // looks up a child mapper, but takes into account field names that expand to objects - private static Mapper getMapper(ObjectMapper objectMapper, String fieldName, String[] subfields) { + private static Mapper getMapper(final ParseContext context, ObjectMapper objectMapper, String fieldName, String[] subfields) { + String fieldPath = context.path().pathAsText(fieldName); + // Check if mapper is a metadata mapper first + Mapper mapper = context.docMapper().mapping().getMetadataMapper(fieldPath); + if (mapper != null) { + return mapper; + } + for (int i = 0; i < subfields.length - 1; ++i) { - Mapper mapper = objectMapper.getMapper(subfields[i]); + mapper = objectMapper.getMapper(subfields[i]); if (mapper == null || (mapper instanceof ObjectMapper) == false) { return null; } 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 92380951b3fc8..0e99b1602da58 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/FieldNamesFieldMapper.java @@ -27,7 +27,6 @@ import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.index.query.QueryShardContext; -import java.io.IOException; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -160,19 +159,6 @@ public FieldNamesFieldType fieldType() { return (FieldNamesFieldType) super.fieldType(); } - @Override - public void preParse(ParseContext context) { - } - - @Override - public void postParse(ParseContext context) { - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { - // Adding values to the _field_names field is handled by the mappers for each field type - } - static Iterable extractFieldNames(final String fullPath) { return new Iterable() { @Override 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 3c08d3cdb4ead..300688ebc2f3d 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java @@ -257,11 +257,6 @@ private IdFieldMapper() { @Override public void preParse(ParseContext context) throws IOException { - super.parse(context); - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { BytesRef id = Uid.encodeId(context.sourceToParse().id()); context.doc().add(new Field(NAME, id, Defaults.FIELD_TYPE)); } 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 766d80fecbada..c1e0568a4db03 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IgnoredFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IgnoredFieldMapper.java @@ -82,22 +82,8 @@ private IgnoredFieldMapper() { super(IgnoredFieldType.INSTANCE); } - @Override - public void preParse(ParseContext context) throws IOException { - } - @Override public void postParse(ParseContext context) throws IOException { - super.parse(context); - } - - @Override - public void parse(ParseContext context) throws IOException { - // done in post-parse - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { for (String field : context.getIgnoredFields()) { context.doc().add(new Field(NAME, field, Defaults.FIELD_TYPE)); } 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 20cb3c8153eab..dcfa7e7e1c27c 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java @@ -27,7 +27,6 @@ import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; import org.elasticsearch.search.lookup.SearchLookup; -import java.io.IOException; import java.util.Collections; import java.util.function.Supplier; @@ -73,12 +72,6 @@ public IndexFieldMapper() { super(IndexFieldType.INSTANCE); } - @Override - public void preParse(ParseContext context) throws IOException {} - - @Override - protected void parseCreateField(ParseContext context) throws IOException {} - @Override protected String contentType() { return CONTENT_TYPE; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/Mapping.java b/server/src/main/java/org/elasticsearch/index/mapper/Mapping.java index cbce56d66fa22..b36d240be0b73 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/Mapping.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/Mapping.java @@ -48,6 +48,7 @@ public final class Mapping implements ToXContentFragment { final RootObjectMapper root; final MetadataFieldMapper[] metadataMappers; final Map, MetadataFieldMapper> metadataMappersMap; + final Map metadataMappersByName; final Map meta; public Mapping(Version indexCreated, RootObjectMapper rootObjectMapper, @@ -55,8 +56,10 @@ public Mapping(Version indexCreated, RootObjectMapper rootObjectMapper, this.indexCreated = indexCreated; this.metadataMappers = metadataMappers; Map, MetadataFieldMapper> metadataMappersMap = new HashMap<>(); + Map metadataMappersByName = new HashMap<>(); for (MetadataFieldMapper metadataMapper : metadataMappers) { metadataMappersMap.put(metadataMapper.getClass(), metadataMapper); + metadataMappersByName.put(metadataMapper.name(), metadataMapper); } this.root = rootObjectMapper; // keep root mappers sorted for consistent serialization @@ -67,6 +70,7 @@ public int compare(Mapper o1, Mapper o2) { } }); this.metadataMappersMap = unmodifiableMap(metadataMappersMap); + this.metadataMappersByName = unmodifiableMap(metadataMappersByName); this.meta = meta; } @@ -136,6 +140,10 @@ public Mapping merge(Mapping mergeWith, MergeReason reason) { return new Mapping(indexCreated, mergedRoot, mergedMetadataMappers.values().toArray(new MetadataFieldMapper[0]), mergedMeta); } + public MetadataFieldMapper getMetadataMapper(String mapperName) { + return metadataMappersByName.get(mapperName); + } + @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { root.toXContent(builder, params, new ToXContent() { 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 0c8febc19ed62..1a56b92d7e417 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MetadataFieldMapper.java @@ -153,10 +153,18 @@ public final XContentBuilder toXContent(XContentBuilder builder, Params params) return builder.endObject(); } + @Override + protected void parseCreateField(ParseContext context) throws IOException { + throw new MapperParsingException("Field [" + name() + "] is a metadata field and cannot be added inside" + + " a document. Use the index API request parameters."); + } + /** * Called before {@link FieldMapper#parse(ParseContext)} on the {@link RootObjectMapper}. */ - public abstract void preParse(ParseContext context) throws IOException; + public void preParse(ParseContext context) throws IOException { + // do nothing + } /** * Called after {@link FieldMapper#parse(ParseContext)} on the {@link RootObjectMapper}. @@ -169,5 +177,4 @@ public void postParse(ParseContext context) throws IOException { public ValueFetcher valueFetcher(MapperService mapperService, SearchLookup lookup, String format) { throw new UnsupportedOperationException("Cannot fetch values for internal field [" + name() + "]."); } - } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NestedPathFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NestedPathFieldMapper.java index ee79aae39d8f5..d9f300a1f7b1f 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NestedPathFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NestedPathFieldMapper.java @@ -31,7 +31,6 @@ import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.query.QueryShardContext; -import java.io.IOException; import java.util.Collections; public class NestedPathFieldMapper extends MetadataFieldMapper { @@ -93,24 +92,9 @@ private NestedPathFieldMapper(Settings settings) { super(new NestedPathFieldType(settings)); } - @Override - public void preParse(ParseContext context) throws IOException { - super.parse(context); - } - - @Override - public void parse(ParseContext context) throws IOException { - // we parse in pre parse - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { - } - @Override protected String contentType() { return 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 82f7ee1fff57f..4aeac3ea337b3 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/RoutingFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/RoutingFieldMapper.java @@ -117,18 +117,6 @@ public boolean required() { @Override public void preParse(ParseContext context) throws IOException { - super.parse(context); - } - - @Override - public void parse(ParseContext context) throws IOException { - // no need ot parse here, we either get the routing in the sourceToParse - // or we don't have routing, if we get it in sourceToParse, we process it in preParse - // which will always be called - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { String routing = context.sourceToParse().routing(); if (routing != null) { context.doc().add(new Field(fieldType().name(), routing, Defaults.FIELD_TYPE)); 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 a1137879c78c6..59cdee09a576a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java @@ -182,11 +182,6 @@ public SeqNoFieldMapper() { @Override public void preParse(ParseContext context) throws IOException { - super.parse(context); - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { // see InternalEngine.innerIndex to see where the real version value is set // also see ParsedDocument.updateSeqID (called by innerIndex) SequenceIDFields seqID = SequenceIDFields.emptySeqID(); @@ -196,11 +191,6 @@ protected void parseCreateField(ParseContext context) throws IOException { context.doc().add(seqID.primaryTerm); } - @Override - public void parse(ParseContext context) throws IOException { - // fields are added in parseCreateField - } - @Override public void postParse(ParseContext context) throws IOException { // In the case of nested docs, let's fill nested docs with the original 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 8e6e20c6cfad0..b06bf9f0c48a1 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SourceFieldMapper.java @@ -156,16 +156,6 @@ public boolean isComplete() { @Override public void preParse(ParseContext context) throws IOException { - super.parse(context); - } - - @Override - public void parse(ParseContext context) throws IOException { - // nothing to do here, we will call it in pre parse - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { BytesReference originalSource = context.sourceToParse().source(); XContentType contentType = context.sourceToParse().getXContentType(); final BytesReference adaptedSource = applyFilters(originalSource, contentType); 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 6ee984fa5c2da..49638b26a9685 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java @@ -168,21 +168,6 @@ private TypeFieldMapper() { super(new TypeFieldType()); } - @Override - public void preParse(ParseContext context) throws IOException { - super.parse(context); - } - - @Override - public void parse(ParseContext context) throws IOException { - // we parse in pre parse - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { - - } - @Override protected String contentType() { return CONTENT_TYPE; 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 43242bc2b7a1c..bb4a711736f27 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/VersionFieldMapper.java @@ -68,22 +68,12 @@ private VersionFieldMapper() { @Override public void preParse(ParseContext context) throws IOException { - super.parse(context); - } - - @Override - protected void parseCreateField(ParseContext context) throws IOException { // see InternalEngine.updateVersion to see where the real version value is set final Field version = new NumericDocValuesField(NAME, -1L); context.version(version); context.doc().add(version); } - @Override - public void parse(ParseContext context) throws IOException { - // _version added in preparse - } - @Override public void postParse(ParseContext context) throws IOException { // In the case of nested docs, let's fill nested docs with version=1 so that Lucene doesn't write a Bitset for documents 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 eeec74bc3a583..dc8c995194924 100644 --- a/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateServiceTests.java +++ b/server/src/test/java/org/elasticsearch/cluster/metadata/MetadataIndexTemplateServiceTests.java @@ -47,7 +47,6 @@ 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; import org.elasticsearch.indices.IndexTemplateMissingException; @@ -1554,16 +1553,6 @@ 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/DocumentParserTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java index 3940a9ae572a6..c37537fbfecd2 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java @@ -31,15 +31,18 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.mapper.ParseContext.Document; +import org.elasticsearch.plugins.Plugin; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; +import static java.util.Collections.singletonList; import static org.elasticsearch.test.StreamsUtils.copyToBytesFromClasspath; import static org.elasticsearch.test.StreamsUtils.copyToStringFromClasspath; import static org.hamcrest.Matchers.containsString; @@ -50,6 +53,11 @@ public class DocumentParserTests extends MapperServiceTestCase { + @Override + protected Collection getPlugins() { + return singletonList(new MockMetadataMapperPlugin()); + } + public void testFieldDisabled() throws Exception { DocumentMapper mapper = createDocumentMapper(mapping(b -> { b.startObject("foo").field("enabled", false).endObject(); @@ -931,12 +939,36 @@ public void testDocumentContainsMetadataField() throws Exception { DocumentMapper mapper = createDocumentMapper(mapping(b -> {})); MapperParsingException e = expectThrows(MapperParsingException.class, () -> mapper.parse(source(b -> b.field("_field_names", 0)))); - assertTrue(e.getMessage(), - e.getMessage().contains("Field [_field_names] is a metadata field and cannot be added inside a document.")); + assertTrue(e.getCause().getMessage(), + e.getCause().getMessage().contains("Field [_field_names] is a metadata field and cannot be added inside a document.")); mapper.parse(source(b -> b.field("foo._field_names", 0))); // parses without error } + public void testDocumentContainsAllowedMetadataField() throws Exception { + DocumentMapper mapper = createDocumentMapper(mapping(b -> {})); + { + // A metadata field that parses a value fails to parse a null value + MapperParsingException e = expectThrows(MapperParsingException.class, () -> + mapper.parse(source(b -> b.nullField(MockMetadataMapperPlugin.MockMetadataMapper.CONTENT_TYPE)))); + assertTrue(e.getMessage(), e.getMessage().contains("failed to parse field [_mock_metadata]")); + } + { + // A metadata field that parses a value fails to parse an object + MapperParsingException e = expectThrows(MapperParsingException.class, () -> + mapper.parse(source(b -> b.field(MockMetadataMapperPlugin.MockMetadataMapper.CONTENT_TYPE) + .startObject().field("sub-field", "true").endObject()))); + assertTrue(e.getMessage(), e.getMessage().contains("failed to parse field [_mock_metadata]")); + } + { + ParsedDocument doc = mapper.parse(source(b -> + b.field(MockMetadataMapperPlugin.MockMetadataMapper.CONTENT_TYPE, "mock-metadata-field-value") + )); + IndexableField field = doc.rootDoc().getField(MockMetadataMapperPlugin.MockMetadataMapper.CONTENT_TYPE); + assertEquals("mock-metadata-field-value", field.stringValue()); + } + } + public void testSimpleMapper() throws Exception { DocumentMapper docMapper = createDocumentMapper(mapping(b -> { b.startObject("name"); 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 62507f9b38c0e..32919b16179a6 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ExternalMetadataMapper.java @@ -37,11 +37,6 @@ protected ExternalMetadataMapper() { super(new BooleanFieldMapper.BooleanFieldType(FIELD_NAME)); } - @Override - protected void parseCreateField(ParseContext context) throws IOException { - // handled in post parse - } - @Override public Iterator iterator() { return Collections.emptyIterator(); @@ -52,10 +47,6 @@ protected String contentType() { return CONTENT_TYPE; } - @Override - public void preParse(ParseContext context) throws IOException { - } - @Override public void postParse(ParseContext context) throws IOException { context.doc().add(new StringField(FIELD_NAME, FIELD_VALUE, Store.YES)); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/IdFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/IdFieldMapperTests.java index 4b425c378c1a6..fbf593df5fc5b 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/IdFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/IdFieldMapperTests.java @@ -59,7 +59,8 @@ public void testIncludeInObjectNotAllowed() throws Exception { .startObject().field("_id", "1").endObject()), XContentType.JSON)); fail("Expected failure to parse metadata field"); } catch (MapperParsingException e) { - assertTrue(e.getMessage(), e.getMessage().contains("Field [_id] is a metadata field and cannot be added inside a document")); + assertTrue(e.getCause().getMessage(), + e.getCause().getMessage().contains("Field [_id] is a metadata field and cannot be added inside a document")); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/MockMetadataMapperPlugin.java b/server/src/test/java/org/elasticsearch/index/mapper/MockMetadataMapperPlugin.java new file mode 100644 index 0000000000000..db2ddf8efb5d2 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/index/mapper/MockMetadataMapperPlugin.java @@ -0,0 +1,98 @@ +/* + * 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; + +import org.apache.lucene.document.Field; +import org.apache.lucene.document.StringField; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.plugins.MapperPlugin; +import org.elasticsearch.plugins.Plugin; + +import java.io.IOException; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Mapper plugin providing a mock metadata field mapper implementation that supports setting its value + * through the document source. + */ +public class MockMetadataMapperPlugin extends Plugin implements MapperPlugin { + + /** + * A mock metadata field mapper that supports being set from the document source. + */ + public static class MockMetadataMapper extends MetadataFieldMapper { + + static final String CONTENT_TYPE = "_mock_metadata"; + static final String FIELD_NAME = "_mock_metadata"; + + protected MockMetadataMapper() { + super(new KeywordFieldMapper.KeywordFieldType(FIELD_NAME)); + } + + @Override + protected void parseCreateField(ParseContext context) throws IOException { + if (context.parser().currentToken() == XContentParser.Token.VALUE_STRING) { + context.doc().add(new StringField(FIELD_NAME, context.parser().text(), Field.Store.YES)); + } else { + throw new IllegalArgumentException("Field [" + fieldType().name() + "] must be a string."); + } + } + + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @Override + protected String contentType() { + return CONTENT_TYPE; + } + + public static class Builder extends MetadataFieldMapper.Builder { + + protected Builder() { + super(FIELD_NAME); + } + + @Override + protected List> getParameters() { + return Collections.emptyList(); + } + + @Override + public MockMetadataMapper build(BuilderContext context) { + return new MockMetadataMapper(); + } + } + + public static final TypeParser PARSER = new ConfigurableTypeParser( + c -> new MockMetadataMapper(), + c -> new MockMetadataMapper.Builder()) { + }; + } + + @Override + public Map getMetadataMappers() { + return Collections.singletonMap(MockMetadataMapper.CONTENT_TYPE, MockMetadataMapper.PARSER); + } +} diff --git a/server/src/test/java/org/elasticsearch/index/mapper/RoutingFieldMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/RoutingFieldMapperTests.java index d4e6f685aeeb2..cf1404875382c 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/RoutingFieldMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/RoutingFieldMapperTests.java @@ -58,7 +58,7 @@ public void testIncludeInObjectNotAllowed() throws Exception { .startObject().field("_routing", "foo").endObject()),XContentType.JSON)); fail("Expected failure to parse metadata field"); } catch (MapperParsingException e) { - assertThat(e.getMessage(), e.getMessage(), + assertThat(e.getCause().getMessage(), e.getCause().getMessage(), containsString("Field [_routing] is a metadata field and cannot be added inside a document")); } } 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 1dd2bc1f97c3d..042c84adbd3bd 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 @@ -171,15 +171,6 @@ public void doValidate(MappingLookup lookup) { } } - @Override - public void preParse(ParseContext context) throws IOException {} - - @Override - protected void parseCreateField(ParseContext context) throws IOException { - // Meta field doesn't create any fields, so this shouldn't happen. - throw new IllegalStateException(NAME + " field mapper cannot create fields"); - } - @Override public void postParse(ParseContext context) throws IOException { if (enabled == false) {