diff --git a/docs/changelog/106077.yaml b/docs/changelog/106077.yaml new file mode 100644 index 0000000000000..eb987cd9617f8 --- /dev/null +++ b/docs/changelog/106077.yaml @@ -0,0 +1,7 @@ +pr: 106077 +summary: Fix merging component templates with a mix of dotted and nested object mapper + definitions +area: Mapping +type: bug +issues: + - 105482 diff --git a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/RankFeatureMetaFieldMapperTests.java b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/RankFeatureMetaFieldMapperTests.java index b9ca544e7532d..9f559c8f55858 100644 --- a/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/RankFeatureMetaFieldMapperTests.java +++ b/modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/RankFeatureMetaFieldMapperTests.java @@ -49,7 +49,11 @@ public void testBasics() throws Exception { .endObject() ); - Mapping parsedMapping = createMapperService(mapping).parseMapping("type", new CompressedXContent(mapping)); + Mapping parsedMapping = createMapperService(mapping).parseMapping( + "type", + MapperService.MergeReason.MAPPING_UPDATE, + new CompressedXContent(mapping) + ); assertEquals(mapping, parsedMapping.toCompressedXContent().toString()); assertNotNull(parsedMapping.getMetadataMapperByClass(RankFeatureMetaFieldMapper.class)); } diff --git a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java index 46b9e365fd0ea..4adc7f9b5ba27 100644 --- a/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java +++ b/modules/percolator/src/test/java/org/elasticsearch/percolator/PercolatorFieldMapperTests.java @@ -52,6 +52,7 @@ import org.elasticsearch.index.mapper.LuceneDocument; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.mapper.ParsedDocument; import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.TestDocumentParserContext; @@ -206,7 +207,7 @@ public void init() throws Exception { .endObject() .endObject() ); - mapperService.merge("doc", new CompressedXContent(mapper), MapperService.MergeReason.MAPPING_UPDATE); + mapperService.merge("doc", new CompressedXContent(mapper), MergeReason.MAPPING_UPDATE); } private void addQueryFieldMappings() throws Exception { @@ -223,7 +224,7 @@ private void addQueryFieldMappings() throws Exception { .endObject() .endObject() ); - mapperService.merge("doc", new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE); + mapperService.merge("doc", new CompressedXContent(percolatorMapper), MergeReason.MAPPING_UPDATE); fieldType = (PercolatorFieldMapper.PercolatorFieldType) mapperService.fieldType(fieldName); } @@ -699,7 +700,7 @@ public void testAllowNoAdditionalSettings() throws Exception { MapperParsingException e = expectThrows( MapperParsingException.class, () -> indexServiceWithoutSettings.mapperService() - .merge("doc", new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE) + .merge("doc", new CompressedXContent(percolatorMapper), MergeReason.MAPPING_UPDATE) ); assertThat(e.getMessage(), containsString("Mapping definition for [" + fieldName + "] has unsupported parameters: [index : no]")); } @@ -722,7 +723,7 @@ public void testMultiplePercolatorFields() throws Exception { .endObject() .endObject() ); - mapperService.merge(typeName, new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE); + mapperService.merge(typeName, new CompressedXContent(percolatorMapper), MergeReason.MAPPING_UPDATE); QueryBuilder queryBuilder = matchQuery("field", "value"); ParsedDocument doc = mapperService.documentMapper() @@ -763,7 +764,7 @@ public void testNestedPercolatorField() throws Exception { .endObject() .endObject() ); - mapperService.merge(typeName, new CompressedXContent(percolatorMapper), MapperService.MergeReason.MAPPING_UPDATE); + mapperService.merge(typeName, new CompressedXContent(percolatorMapper), MergeReason.MAPPING_UPDATE); QueryBuilder queryBuilder = matchQuery("field", "value"); ParsedDocument doc = mapperService.documentMapper() @@ -912,7 +913,7 @@ public void testEmptyName() throws Exception { ); MapperParsingException e = expectThrows( MapperParsingException.class, - () -> mapperService.parseMapping("type1", new CompressedXContent(mapping)) + () -> mapperService.parseMapping("type1", MergeReason.MAPPING_UPDATE, new CompressedXContent(mapping)) ); assertThat(e.getMessage(), containsString("field name cannot be an empty string")); } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataMappingService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataMappingService.java index 7e2c0849a6fad..3ca206eaddb28 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataMappingService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetadataMappingService.java @@ -133,6 +133,7 @@ private static ClusterState applyRequest( final CompressedXContent mappingUpdateSource = request.source(); final Metadata metadata = currentState.metadata(); final List updateList = new ArrayList<>(); + MergeReason reason = request.autoUpdate() ? MergeReason.MAPPING_AUTO_UPDATE : MergeReason.MAPPING_UPDATE; for (Index index : request.indices()) { MapperService mapperService = indexMapperServices.get(index); // IMPORTANT: always get the metadata from the state since it get's batched @@ -147,13 +148,8 @@ private static ClusterState applyRequest( updateList.add(indexMetadata); // try and parse it (no need to add it here) so we can bail early in case of parsing exception // first, simulate: just call merge and ignore the result - Mapping mapping = mapperService.parseMapping(MapperService.SINGLE_MAPPING_NAME, mappingUpdateSource); - MapperService.mergeMappings( - mapperService.documentMapper(), - mapping, - request.autoUpdate() ? MergeReason.MAPPING_AUTO_UPDATE : MergeReason.MAPPING_UPDATE, - mapperService.getIndexSettings() - ); + Mapping mapping = mapperService.parseMapping(MapperService.SINGLE_MAPPING_NAME, reason, mappingUpdateSource); + MapperService.mergeMappings(mapperService.documentMapper(), mapping, reason, mapperService.getIndexSettings()); } Metadata.Builder builder = Metadata.builder(metadata); boolean updated = false; @@ -169,11 +165,7 @@ private static ClusterState applyRequest( if (existingMapper != null) { existingSource = existingMapper.mappingSource(); } - DocumentMapper mergedMapper = mapperService.merge( - MapperService.SINGLE_MAPPING_NAME, - mappingUpdateSource, - request.autoUpdate() ? MergeReason.MAPPING_AUTO_UPDATE : MergeReason.MAPPING_UPDATE - ); + DocumentMapper mergedMapper = mapperService.merge(MapperService.SINGLE_MAPPING_NAME, mappingUpdateSource, reason); CompressedXContent updatedSource = mergedMapper.mappingSource(); if (existingSource != null) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParserContext.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParserContext.java index 66c5de61bcd92..6fb0b3f917842 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParserContext.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParserContext.java @@ -14,6 +14,7 @@ import org.elasticsearch.common.time.DateFormatter; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.analysis.IndexAnalyzers; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.xcontent.FilterXContentParserWrapper; import org.elasticsearch.xcontent.FlatteningXContentParser; import org.elasticsearch.xcontent.XContentParser; @@ -613,7 +614,7 @@ public final MapperBuilderContext createDynamicMapperBuilderContext() { if (objectMapper instanceof PassThroughObjectMapper passThroughObjectMapper) { containsDimensions = passThroughObjectMapper.containsDimensions(); } - return new MapperBuilderContext(p, mappingLookup().isSourceSynthetic(), false, containsDimensions); + return new MapperBuilderContext(p, mappingLookup().isSourceSynthetic(), false, containsDimensions, MergeReason.MAPPING_UPDATE); } public abstract XContentParser parser(); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MapperBuilderContext.java b/server/src/main/java/org/elasticsearch/index/mapper/MapperBuilderContext.java index 4154c936bab52..84b3b285c4e01 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MapperBuilderContext.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MapperBuilderContext.java @@ -9,6 +9,7 @@ package org.elasticsearch.index.mapper; import org.elasticsearch.common.Strings; +import org.elasticsearch.index.mapper.MapperService.MergeReason; /** * Holds context for building Mapper objects from their Builders @@ -19,23 +20,35 @@ public class MapperBuilderContext { * The root context, to be used when building a tree of mappers */ public static MapperBuilderContext root(boolean isSourceSynthetic, boolean isDataStream) { - return new MapperBuilderContext(null, isSourceSynthetic, isDataStream, false); + return root(isSourceSynthetic, isDataStream, MergeReason.MAPPING_UPDATE); + } + + public static MapperBuilderContext root(boolean isSourceSynthetic, boolean isDataStream, MergeReason mergeReason) { + return new MapperBuilderContext(null, isSourceSynthetic, isDataStream, false, mergeReason); } private final String path; private final boolean isSourceSynthetic; private final boolean isDataStream; private final boolean parentObjectContainsDimensions; + private final MergeReason mergeReason; MapperBuilderContext(String path) { - this(path, false, false, false); + this(path, false, false, false, MergeReason.MAPPING_UPDATE); } - MapperBuilderContext(String path, boolean isSourceSynthetic, boolean isDataStream, boolean parentObjectContainsDimensions) { + MapperBuilderContext( + String path, + boolean isSourceSynthetic, + boolean isDataStream, + boolean parentObjectContainsDimensions, + MergeReason mergeReason + ) { this.path = path; this.isSourceSynthetic = isSourceSynthetic; this.isDataStream = isDataStream; this.parentObjectContainsDimensions = parentObjectContainsDimensions; + this.mergeReason = mergeReason; } /** @@ -44,7 +57,7 @@ public static MapperBuilderContext root(boolean isSourceSynthetic, boolean isDat * @return a new MapperBuilderContext with this context as its parent */ public MapperBuilderContext createChildContext(String name) { - return new MapperBuilderContext(buildFullName(name), isSourceSynthetic, isDataStream, parentObjectContainsDimensions); + return new MapperBuilderContext(buildFullName(name), isSourceSynthetic, isDataStream, parentObjectContainsDimensions, mergeReason); } /** @@ -78,4 +91,11 @@ public boolean parentObjectContainsDimensions() { return parentObjectContainsDimensions; } + /** + * The merge reason to use when merging mappers while building the mapper. + * See also {@link ObjectMapper.Builder#buildMappers(MapperBuilderContext)}. + */ + public MergeReason getMergeReason() { + return mergeReason; + } } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MapperMergeContext.java b/server/src/main/java/org/elasticsearch/index/mapper/MapperMergeContext.java index 0af182f315559..dc08a3a199082 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MapperMergeContext.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MapperMergeContext.java @@ -8,6 +8,8 @@ package org.elasticsearch.index.mapper; +import org.elasticsearch.index.mapper.MapperService.MergeReason; + /** * Holds context used when merging mappings. * As the merge process also involves building merged {@link Mapper.Builder}s, @@ -23,11 +25,18 @@ private MapperMergeContext(MapperBuilderContext mapperBuilderContext, NewFieldsB this.newFieldsBudget = newFieldsBudget; } + static MapperMergeContext root(boolean isSourceSynthetic, boolean isDataStream, long newFieldsBudget) { + return root(isSourceSynthetic, isDataStream, MergeReason.MAPPING_UPDATE, newFieldsBudget); + } + /** * The root context, to be used when merging a tree of mappers */ - public static MapperMergeContext root(boolean isSourceSynthetic, boolean isDataStream, long newFieldsBudget) { - return new MapperMergeContext(MapperBuilderContext.root(isSourceSynthetic, isDataStream), NewFieldsBudget.of(newFieldsBudget)); + public static MapperMergeContext root(boolean isSourceSynthetic, boolean isDataStream, MergeReason mergeReason, long newFieldsBudget) { + return new MapperMergeContext( + MapperBuilderContext.root(isSourceSynthetic, isDataStream, mergeReason), + NewFieldsBudget.of(newFieldsBudget) + ); } /** diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MapperService.java b/server/src/main/java/org/elasticsearch/index/mapper/MapperService.java index 4646936b8891f..f91c4f176c6da 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MapperService.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MapperService.java @@ -310,7 +310,7 @@ public void updateMapping(final IndexMetadata currentIndexMetadata, final IndexM if (newMappingMetadata != null) { String type = newMappingMetadata.type(); CompressedXContent incomingMappingSource = newMappingMetadata.source(); - Mapping incomingMapping = parseMapping(type, incomingMappingSource); + Mapping incomingMapping = parseMapping(type, MergeReason.MAPPING_UPDATE, incomingMappingSource); DocumentMapper previousMapper; synchronized (this) { previousMapper = this.mapper; @@ -366,7 +366,7 @@ boolean assertNoUpdateRequired(final IndexMetadata newIndexMetadata) { // that the incoming mappings are the same as the current ones: we need to // parse the incoming mappings into a DocumentMapper and check that its // serialization is the same as the existing mapper - Mapping newMapping = parseMapping(mapping.type(), mapping.source()); + Mapping newMapping = parseMapping(mapping.type(), MergeReason.MAPPING_UPDATE, mapping.source()); final CompressedXContent currentSource = this.mapper.mappingSource(); final CompressedXContent newSource = newMapping.toCompressedXContent(); if (Objects.equals(currentSource, newSource) == false @@ -533,7 +533,7 @@ public DocumentMapper merge(String type, CompressedXContent mappingSource, Merge } private synchronized DocumentMapper doMerge(String type, MergeReason reason, Map mappingSourceAsMap) { - Mapping incomingMapping = parseMapping(type, mappingSourceAsMap); + Mapping incomingMapping = parseMapping(type, reason, mappingSourceAsMap); Mapping mapping = mergeMappings(this.mapper, incomingMapping, reason, this.indexSettings); // TODO: In many cases the source here is equal to mappingSource so we need not serialize again. // We should identify these cases reliably and save expensive serialization here @@ -542,7 +542,7 @@ private synchronized DocumentMapper doMerge(String type, MergeReason reason, Map return newMapper; } this.mapper = newMapper; - assert assertSerialization(newMapper); + assert assertSerialization(newMapper, reason); return newMapper; } @@ -552,9 +552,9 @@ private DocumentMapper newDocumentMapper(Mapping mapping, MergeReason reason, Co return newMapper; } - public Mapping parseMapping(String mappingType, CompressedXContent mappingSource) { + public Mapping parseMapping(String mappingType, MergeReason reason, CompressedXContent mappingSource) { try { - return mappingParser.parse(mappingType, mappingSource); + return mappingParser.parse(mappingType, reason, mappingSource); } catch (Exception e) { throw new MapperParsingException("Failed to parse mapping: {}", e, e.getMessage()); } @@ -564,12 +564,13 @@ public Mapping parseMapping(String mappingType, CompressedXContent mappingSource * A method to parse mapping from a source in a map form. * * @param mappingType the mapping type + * @param reason the merge reason to use when merging mappers while building the mapper * @param mappingSource mapping source already converted to a map form, but not yet processed otherwise * @return a parsed mapping */ - public Mapping parseMapping(String mappingType, Map mappingSource) { + public Mapping parseMapping(String mappingType, MergeReason reason, Map mappingSource) { try { - return mappingParser.parse(mappingType, mappingSource); + return mappingParser.parse(mappingType, reason, mappingSource); } catch (Exception e) { throw new MapperParsingException("Failed to parse mapping: {}", e, e.getMessage()); } @@ -619,10 +620,10 @@ static Mapping mergeMappings(DocumentMapper currentMapper, Mapping incomingMappi return newMapping; } - private boolean assertSerialization(DocumentMapper mapper) { + private boolean assertSerialization(DocumentMapper mapper, MergeReason reason) { // capture the source now, it may change due to concurrent parsing final CompressedXContent mappingSource = mapper.mappingSource(); - Mapping newMapping = parseMapping(mapper.type(), mappingSource); + Mapping newMapping = parseMapping(mapper.type(), reason, mappingSource); if (newMapping.toCompressedXContent().equals(mappingSource) == false) { throw new AssertionError( "Mapping serialization result is different from source. \n--> Source [" 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 903e4e5da5b29..b5de3971fa091 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/Mapping.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/Mapping.java @@ -137,8 +137,8 @@ public SourceLoader.SyntheticFieldLoader syntheticFieldLoader() { * @return the resulting merged mapping. */ Mapping merge(Mapping mergeWith, MergeReason reason, long newFieldsBudget) { - MapperMergeContext mergeContext = MapperMergeContext.root(isSourceSynthetic(), false, newFieldsBudget); - RootObjectMapper mergedRoot = root.merge(mergeWith.root, reason, mergeContext); + MapperMergeContext mergeContext = MapperMergeContext.root(isSourceSynthetic(), false, reason, newFieldsBudget); + RootObjectMapper mergedRoot = root.merge(mergeWith.root, mergeContext); // When merging metadata fields as part of applying an index template, new field definitions // completely overwrite existing ones instead of being merged. This behavior matches how we @@ -176,11 +176,11 @@ Mapping merge(Mapping mergeWith, MergeReason reason, long newFieldsBudget) { * @param fieldsBudget the maximum number of fields this mapping may have */ public Mapping withFieldsBudget(long fieldsBudget) { - MapperMergeContext mergeContext = MapperMergeContext.root(isSourceSynthetic(), false, fieldsBudget); + MapperMergeContext mergeContext = MapperMergeContext.root(isSourceSynthetic(), false, MergeReason.MAPPING_RECOVERY, fieldsBudget); // get a copy of the root mapper, without any fields RootObjectMapper shallowRoot = root.withoutMappers(); // calling merge on the shallow root to ensure we're only adding as many fields as allowed by the fields budget - return new Mapping(shallowRoot.merge(root, MergeReason.MAPPING_RECOVERY, mergeContext), metadataMappers, meta); + return new Mapping(shallowRoot.merge(root, mergeContext), metadataMappers, meta); } @Override diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MappingParser.java b/server/src/main/java/org/elasticsearch/index/mapper/MappingParser.java index 8b30915ca4d3c..86d8c1686858c 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MappingParser.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MappingParser.java @@ -12,6 +12,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexMode; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.xcontent.XContentType; import java.util.Collections; @@ -79,20 +80,25 @@ static Map convertToMap(CompressedXContent source) { } Mapping parse(@Nullable String type, CompressedXContent source) throws MapperParsingException { + return parse(type, MergeReason.MAPPING_UPDATE, source); + } + + Mapping parse(@Nullable String type, MergeReason reason, CompressedXContent source) throws MapperParsingException { Map mapping = convertToMap(source); - return parse(type, mapping); + return parse(type, reason, mapping); } /** * A method to parse mapping from a source in a map form. * * @param type the mapping type + * @param reason the merge reason to use when merging mappers while building the mapper * @param mappingSource mapping source already converted to a map form, but not yet processed otherwise * @return a parsed mapping * @throws MapperParsingException in case of parsing error */ @SuppressWarnings("unchecked") - Mapping parse(@Nullable String type, Map mappingSource) throws MapperParsingException { + Mapping parse(@Nullable String type, MergeReason reason, Map mappingSource) throws MapperParsingException { if (mappingSource.isEmpty()) { if (type == null) { throw new MapperParsingException("malformed mapping, no type name found"); @@ -178,7 +184,7 @@ Mapping parse(@Nullable String type, Map mappingSource) throws M } return new Mapping( - rootObjectMapper.build(MapperBuilderContext.root(isSourceSynthetic, isDataStream)), + rootObjectMapper.build(MapperBuilderContext.root(isSourceSynthetic, isDataStream, reason)), metadataMappers.values().toArray(new MetadataFieldMapper[0]), meta ); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java index a654819811621..6eb63679b15d1 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java @@ -62,8 +62,12 @@ public NestedObjectMapper build(MapperBuilderContext context) { this.includeInRoot = Explicit.IMPLICIT_FALSE; } } - NestedMapperBuilderContext nestedContext = new NestedMapperBuilderContext(context.buildFullName(name), parentIncludedInRoot); - final String fullPath = context.buildFullName(name); + NestedMapperBuilderContext nestedContext = new NestedMapperBuilderContext( + context.buildFullName(name()), + parentIncludedInRoot, + context.getMergeReason() + ); + final String fullPath = context.buildFullName(name()); final String nestedTypePath; if (indexCreatedVersion.before(IndexVersions.V_8_0_0)) { nestedTypePath = "__" + fullPath; @@ -117,14 +121,14 @@ private static class NestedMapperBuilderContext extends MapperBuilderContext { final boolean parentIncludedInRoot; - NestedMapperBuilderContext(String path, boolean parentIncludedInRoot) { - super(path); + NestedMapperBuilderContext(String path, boolean parentIncludedInRoot, MapperService.MergeReason mergeReason) { + super(path, false, false, false, mergeReason); this.parentIncludedInRoot = parentIncludedInRoot; } @Override public MapperBuilderContext createChildContext(String name) { - return new NestedMapperBuilderContext(buildFullName(name), parentIncludedInRoot); + return new NestedMapperBuilderContext(buildFullName(name), parentIncludedInRoot, getMergeReason()); } } @@ -222,16 +226,14 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } @Override - public ObjectMapper merge(Mapper mergeWith, MapperService.MergeReason reason, MapperMergeContext parentMergeContext) { + public ObjectMapper merge(Mapper mergeWith, MapperMergeContext parentMergeContext) { if ((mergeWith instanceof NestedObjectMapper) == false) { MapperErrors.throwNestedMappingConflictError(mergeWith.name()); } NestedObjectMapper mergeWithObject = (NestedObjectMapper) mergeWith; - return merge(mergeWithObject, reason, parentMergeContext); - } - ObjectMapper merge(NestedObjectMapper mergeWithObject, MapperService.MergeReason reason, MapperMergeContext parentMergeContext) { - var mergeResult = MergeResult.build(this, mergeWithObject, reason, parentMergeContext); + final MapperService.MergeReason reason = parentMergeContext.getMapperBuilderContext().getMergeReason(); + var mergeResult = MergeResult.build(this, mergeWithObject, parentMergeContext); Explicit incInParent = this.includeInParent; Explicit incInRoot = this.includeInRoot; if (reason == MapperService.MergeReason.INDEX_TEMPLATE) { @@ -280,7 +282,11 @@ protected MapperMergeContext createChildContext(MapperMergeContext mapperMergeCo parentIncludedInRoot |= this.includeInParent.value(); } return mapperMergeContext.createChildContext( - new NestedMapperBuilderContext(mapperBuilderContext.buildFullName(name), parentIncludedInRoot) + new NestedMapperBuilderContext( + mapperBuilderContext.buildFullName(name), + parentIncludedInRoot, + mapperBuilderContext.getMergeReason() + ) ); } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java index 7a807f767611b..97a65847cc80a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java @@ -449,11 +449,6 @@ public final boolean subobjects() { return subobjects.value(); } - @Override - public ObjectMapper merge(Mapper mergeWith, MapperMergeContext mapperMergeContext) { - return merge(mergeWith, MergeReason.MAPPING_UPDATE, mapperMergeContext); - } - @Override public void validate(MappingLookup mappers) { for (Mapper mapper : this.mappers.values()) { @@ -465,7 +460,8 @@ protected MapperMergeContext createChildContext(MapperMergeContext mapperMergeCo return mapperMergeContext.createChildContext(name); } - public ObjectMapper merge(Mapper mergeWith, MergeReason reason, MapperMergeContext parentMergeContext) { + @Override + public ObjectMapper merge(Mapper mergeWith, MapperMergeContext parentMergeContext) { if (mergeWith instanceof ObjectMapper == false) { MapperErrors.throwObjectMappingConflictError(mergeWith.name()); } @@ -473,11 +469,7 @@ public ObjectMapper merge(Mapper mergeWith, MergeReason reason, MapperMergeConte // TODO stop NestedObjectMapper extending ObjectMapper? MapperErrors.throwNestedMappingConflictError(mergeWith.name()); } - return merge((ObjectMapper) mergeWith, reason, parentMergeContext); - } - - ObjectMapper merge(ObjectMapper mergeWith, MergeReason reason, MapperMergeContext parentMergeContext) { - var mergeResult = MergeResult.build(this, mergeWith, reason, parentMergeContext); + var mergeResult = MergeResult.build(this, (ObjectMapper) mergeWith, parentMergeContext); return new ObjectMapper( simpleName(), fullPath, @@ -494,13 +486,9 @@ protected record MergeResult( ObjectMapper.Dynamic dynamic, Map mappers ) { - static MergeResult build( - ObjectMapper existing, - ObjectMapper mergeWithObject, - MergeReason reason, - MapperMergeContext parentMergeContext - ) { + static MergeResult build(ObjectMapper existing, ObjectMapper mergeWithObject, MapperMergeContext parentMergeContext) { final Explicit enabled; + final MergeReason reason = parentMergeContext.getMapperBuilderContext().getMergeReason(); if (mergeWithObject.enabled.explicit()) { if (reason == MergeReason.INDEX_TEMPLATE) { enabled = mergeWithObject.enabled; @@ -527,7 +515,7 @@ static MergeResult build( subObjects = existing.subobjects; } MapperMergeContext objectMergeContext = existing.createChildContext(parentMergeContext, existing.simpleName()); - Map mergedMappers = buildMergedMappers(existing, mergeWithObject, reason, objectMergeContext); + Map mergedMappers = buildMergedMappers(existing, mergeWithObject, objectMergeContext, subObjects.value()); return new MergeResult( enabled, subObjects, @@ -539,8 +527,8 @@ static MergeResult build( private static Map buildMergedMappers( ObjectMapper existing, ObjectMapper mergeWithObject, - MergeReason reason, - MapperMergeContext objectMergeContext + MapperMergeContext objectMergeContext, + boolean subobjects ) { Iterator iterator = mergeWithObject.iterator(); if (iterator.hasNext() == false) { @@ -555,10 +543,10 @@ private static Map buildMergedMappers( if (objectMergeContext.decrementFieldBudgetIfPossible(mergeWithMapper.getTotalFieldsCount())) { merged = mergeWithMapper; } else if (mergeWithMapper instanceof ObjectMapper om) { - merged = truncateObjectMapper(reason, objectMergeContext, om); + merged = truncateObjectMapper(objectMergeContext, om); } } else if (mergeIntoMapper instanceof ObjectMapper objectMapper) { - merged = objectMapper.merge(mergeWithMapper, reason, objectMergeContext); + merged = objectMapper.merge(mergeWithMapper, objectMergeContext); } else { assert mergeIntoMapper instanceof FieldMapper || mergeIntoMapper instanceof FieldAliasMapper; if (mergeWithMapper instanceof NestedObjectMapper) { @@ -569,7 +557,7 @@ private static Map buildMergedMappers( // If we're merging template mappings when creating an index, then a field definition always // replaces an existing one. - if (reason == MergeReason.INDEX_TEMPLATE) { + if (objectMergeContext.getMapperBuilderContext().getMergeReason() == MergeReason.INDEX_TEMPLATE) { merged = mergeWithMapper; } else { merged = mergeIntoMapper.merge(mergeWithMapper, objectMergeContext); @@ -582,13 +570,13 @@ private static Map buildMergedMappers( return Map.copyOf(mergedMappers); } - private static ObjectMapper truncateObjectMapper(MergeReason reason, MapperMergeContext context, ObjectMapper objectMapper) { + private static ObjectMapper truncateObjectMapper(MapperMergeContext context, ObjectMapper objectMapper) { // there's not enough capacity for the whole object mapper, // so we're just trying to add the shallow object, without it's sub-fields ObjectMapper shallowObjectMapper = objectMapper.withoutMappers(); if (context.decrementFieldBudgetIfPossible(shallowObjectMapper.getTotalFieldsCount())) { // now trying to add the sub-fields one by one via a merge, until we hit the limit - return shallowObjectMapper.merge(objectMapper, reason, context); + return shallowObjectMapper.merge(objectMapper, context); } return null; } diff --git a/server/src/main/java/org/elasticsearch/index/mapper/PassThroughObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/PassThroughObjectMapper.java index b49c9328fcc79..ef598bbe2059e 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/PassThroughObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/PassThroughObjectMapper.java @@ -10,7 +10,6 @@ import org.elasticsearch.common.Explicit; import org.elasticsearch.index.IndexVersion; -import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.xcontent.XContentBuilder; import java.io.IOException; @@ -100,9 +99,14 @@ public PassThroughObjectMapper.Builder newBuilder(IndexVersion indexVersionCreat return builder; } - public PassThroughObjectMapper merge(ObjectMapper mergeWith, MergeReason reason, MapperMergeContext parentBuilderContext) { - final var mergeResult = MergeResult.build(this, mergeWith, reason, parentBuilderContext); + @Override + public PassThroughObjectMapper merge(Mapper mergeWith, MapperMergeContext parentBuilderContext) { + if (mergeWith instanceof PassThroughObjectMapper == false) { + MapperErrors.throwObjectMappingConflictError(mergeWith.name()); + } + PassThroughObjectMapper mergeWithObject = (PassThroughObjectMapper) mergeWith; + final var mergeResult = MergeResult.build(this, mergeWithObject, parentBuilderContext); final Explicit containsDimensions = (mergeWithObject.timeSeriesDimensionSubFields.explicit()) ? mergeWithObject.timeSeriesDimensionSubFields diff --git a/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java index 21cf05ac9a244..4541d16a7115f 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java @@ -345,15 +345,13 @@ protected MapperMergeContext createChildContext(MapperMergeContext mapperMergeCo } @Override - public RootObjectMapper merge(Mapper mergeWith, MergeReason reason, MapperMergeContext parentMergeContext) { + public RootObjectMapper merge(Mapper mergeWith, MapperMergeContext parentMergeContext) { if (mergeWith instanceof RootObjectMapper == false) { MapperErrors.throwObjectMappingConflictError(mergeWith.name()); } - return merge((RootObjectMapper) mergeWith, reason, parentMergeContext); - } - RootObjectMapper merge(RootObjectMapper mergeWithObject, MergeReason reason, MapperMergeContext parentMergeContext) { - final var mergeResult = MergeResult.build(this, mergeWithObject, reason, parentMergeContext); + RootObjectMapper mergeWithObject = (RootObjectMapper) mergeWith; + final var mergeResult = MergeResult.build(this, mergeWithObject, parentMergeContext); final Explicit numericDetection; if (mergeWithObject.numericDetection.explicit()) { numericDetection = mergeWithObject.numericDetection; @@ -377,7 +375,7 @@ RootObjectMapper merge(RootObjectMapper mergeWithObject, MergeReason reason, Map final Explicit dynamicTemplates; if (mergeWithObject.dynamicTemplates.explicit()) { - if (reason == MergeReason.INDEX_TEMPLATE) { + if (parentMergeContext.getMapperBuilderContext().getMergeReason() == MergeReason.INDEX_TEMPLATE) { Map templatesByKey = new LinkedHashMap<>(); for (DynamicTemplate template : this.dynamicTemplates.value()) { templatesByKey.put(template.name(), template); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DocumentMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DocumentMapperTests.java index 144bfa3e8701e..486b33d9b155a 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DocumentMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DocumentMapperTests.java @@ -464,7 +464,11 @@ public void testDeeplyNestedMapping() throws Exception { threads[threadId] = new Thread(() -> { try { latch.await(); - mapperService.parseMapping("_doc", new CompressedXContent(Strings.toString(builders[threadId]))); + mapperService.parseMapping( + "_doc", + MergeReason.MAPPING_UPDATE, + new CompressedXContent(Strings.toString(builders[threadId])) + ); } catch (Exception e) { throw new AssertionError(e); } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/MapperBuilderContextTests.java b/server/src/test/java/org/elasticsearch/index/mapper/MapperBuilderContextTests.java new file mode 100644 index 0000000000000..8c9197b0f3173 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/index/mapper/MapperBuilderContextTests.java @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.index.mapper; + +import org.elasticsearch.test.ESTestCase; + +public class MapperBuilderContextTests extends ESTestCase { + + public void testRoot() { + MapperBuilderContext root = MapperBuilderContext.root(false, false); + assertFalse(root.isSourceSynthetic()); + assertFalse(root.isDataStream()); + assertEquals(MapperService.MergeReason.MAPPING_UPDATE, root.getMergeReason()); + } + + public void testRootWithMergeReason() { + MapperService.MergeReason mergeReason = randomFrom(MapperService.MergeReason.values()); + MapperBuilderContext root = MapperBuilderContext.root(false, false, mergeReason); + assertFalse(root.isSourceSynthetic()); + assertFalse(root.isDataStream()); + assertEquals(mergeReason, root.getMergeReason()); + } + +} diff --git a/server/src/test/java/org/elasticsearch/index/mapper/MapperMergeContextTests.java b/server/src/test/java/org/elasticsearch/index/mapper/MapperMergeContextTests.java index 9c38487dbdf7b..77d3259ea1091 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/MapperMergeContextTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/MapperMergeContextTests.java @@ -29,4 +29,10 @@ public void testAddFieldIfPossibleUnlimited() { assertTrue(context.decrementFieldBudgetIfPossible(Integer.MAX_VALUE)); } + public void testMergeReasons() { + MapperService.MergeReason mergeReason = randomFrom(MapperService.MergeReason.values()); + MapperMergeContext context = MapperMergeContext.root(false, false, mergeReason, Integer.MAX_VALUE); + assertEquals(mergeReason, context.getMapperBuilderContext().getMergeReason()); + } + } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java b/server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java index 68e7bd6f24664..985b5448d0002 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java @@ -1467,4 +1467,102 @@ public void testMergeUntilLimitCapacityOnlyForParent() throws IOException { assertNull(mapper.mappers().getMapper("parent.child")); } + public void testMergeDottedAndNestedNotation() throws IOException { + CompressedXContent mapping1 = new CompressedXContent(""" + { + "properties": { + "parent.child": { + "type": "keyword" + } + } + }"""); + + CompressedXContent mapping2 = new CompressedXContent(""" + { + "properties": { + "parent" : { + "properties" : { + "child" : { + "type" : "integer" + } + } + } + } + }"""); + + assertMergeEquals(List.of(mapping1, mapping2), """ + { + "_doc" : { + "properties" : { + "parent" : { + "properties" : { + "child" : { + "type" : "integer" + } + } + } + } + } + }"""); + + assertMergeEquals(List.of(mapping2, mapping1), """ + { + "_doc" : { + "properties" : { + "parent" : { + "properties" : { + "child" : { + "type" : "keyword" + } + } + } + } + } + }"""); + } + + public void testDottedAndNestedNotationInSameMapping() throws IOException { + CompressedXContent mapping = new CompressedXContent(""" + { + "properties": { + "parent.child": { + "type": "keyword" + }, + "parent" : { + "properties" : { + "child" : { + "type" : "integer" + } + } + } + } + }"""); + + assertMergeEquals(List.of(mapping), """ + { + "_doc" : { + "properties" : { + "parent" : { + "properties" : { + "child" : { + "type" : "integer" + } + } + } + } + } + }"""); + } + + private void assertMergeEquals(List mappingSources, String expected) throws IOException { + final MapperService mapperServiceBulk = createMapperService(mapping(b -> {})); + // simulates multiple component templates being merged in a composable index template + mapperServiceBulk.merge("_doc", mappingSources, MergeReason.INDEX_TEMPLATE); + assertEquals(expected, Strings.toString(mapperServiceBulk.documentMapper().mapping(), true, true)); + + MapperService mapperServiceSequential = createMapperService(mapping(b -> {})); + // simulates a series of mapping updates + mappingSources.forEach(m -> mapperServiceSequential.merge("_doc", m, MergeReason.INDEX_TEMPLATE)); + assertEquals(expected, Strings.toString(mapperServiceSequential.documentMapper().mapping(), true, true)); + } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java index 61d62c1e41969..25e4ccdf4d3a9 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java @@ -1515,8 +1515,7 @@ public void testMergeNested() { NestedObjectMapper result = (NestedObjectMapper) firstMapper.merge( secondMapper, - MapperService.MergeReason.INDEX_TEMPLATE, - MapperMergeContext.root(false, false, Long.MAX_VALUE) + MapperMergeContext.root(false, false, MapperService.MergeReason.INDEX_TEMPLATE, Long.MAX_VALUE) ); assertFalse(result.isIncludeInParent()); assertTrue(result.isIncludeInRoot()); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java b/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java index 005b14886d059..06d08cebba308 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java @@ -75,10 +75,7 @@ public void testMergeDisabledField() { new ObjectMapper.Builder("disabled", Explicit.IMPLICIT_TRUE) ).build(MapperBuilderContext.root(false, false)); - RootObjectMapper merged = (RootObjectMapper) rootObjectMapper.merge( - mergeWith, - MapperMergeContext.root(false, false, Long.MAX_VALUE) - ); + RootObjectMapper merged = rootObjectMapper.merge(mergeWith, MapperMergeContext.root(false, false, Long.MAX_VALUE)); assertFalse(((ObjectMapper) merged.getMapper("disabled")).isEnabled()); } @@ -93,8 +90,7 @@ public void testMergeEnabled() { ObjectMapper result = rootObjectMapper.merge( mergeWith, - MapperService.MergeReason.INDEX_TEMPLATE, - MapperMergeContext.root(false, false, Long.MAX_VALUE) + MapperMergeContext.root(false, false, MapperService.MergeReason.INDEX_TEMPLATE, Long.MAX_VALUE) ); assertTrue(result.isEnabled()); } @@ -115,8 +111,7 @@ public void testMergeEnabledForRootMapper() { ObjectMapper result = firstMapper.merge( secondMapper, - MapperService.MergeReason.INDEX_TEMPLATE, - MapperMergeContext.root(false, false, Long.MAX_VALUE) + MapperMergeContext.root(false, false, MapperService.MergeReason.INDEX_TEMPLATE, Long.MAX_VALUE) ); assertFalse(result.isEnabled()); } @@ -131,10 +126,7 @@ public void testMergeDisabledRootMapper() { Collections.singletonMap("test", new TestRuntimeField("test", "long")) ).build(MapperBuilderContext.root(false, false)); - RootObjectMapper merged = (RootObjectMapper) rootObjectMapper.merge( - mergeWith, - MapperMergeContext.root(false, false, Long.MAX_VALUE) - ); + RootObjectMapper merged = rootObjectMapper.merge(mergeWith, MapperMergeContext.root(false, false, Long.MAX_VALUE)); assertFalse(merged.isEnabled()); assertEquals(1, merged.runtimeFields().size()); assertEquals("test", merged.runtimeFields().iterator().next().name()); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperTests.java index 29e5f8540734b..47975f7158aae 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperTests.java @@ -125,6 +125,7 @@ public void testMerge() throws IOException { assertNull(mapper.mapping().getRoot().dynamic()); Mapping mergeWith = mapperService.parseMapping( "_doc", + MergeReason.MAPPING_UPDATE, new CompressedXContent(BytesReference.bytes(topMapping(b -> b.field("dynamic", "strict")))) ); Mapping merged = mapper.mapping().merge(mergeWith, reason, Long.MAX_VALUE); @@ -466,10 +467,14 @@ public void testSubobjectsCannotBeUpdated() throws IOException { MapperService mapperService = createMapperService(fieldMapping(b -> b.field("type", "object"))); DocumentMapper mapper = mapperService.documentMapper(); assertNull(mapper.mapping().getRoot().dynamic()); - Mapping mergeWith = mapperService.parseMapping("_doc", new CompressedXContent(BytesReference.bytes(fieldMapping(b -> { - b.field("type", "object"); - b.field("subobjects", "false"); - })))); + Mapping mergeWith = mapperService.parseMapping( + "_doc", + MergeReason.MAPPING_UPDATE, + new CompressedXContent(BytesReference.bytes(fieldMapping(b -> { + b.field("type", "object"); + b.field("subobjects", "false"); + }))) + ); MapperException exception = expectThrows( MapperException.class, () -> mapper.mapping().merge(mergeWith, MergeReason.MAPPING_UPDATE, Long.MAX_VALUE) @@ -481,9 +486,13 @@ public void testSubobjectsCannotBeUpdatedOnRoot() throws IOException { MapperService mapperService = createMapperService(topMapping(b -> b.field("subobjects", false))); DocumentMapper mapper = mapperService.documentMapper(); assertNull(mapper.mapping().getRoot().dynamic()); - Mapping mergeWith = mapperService.parseMapping("_doc", new CompressedXContent(BytesReference.bytes(topMapping(b -> { - b.field("subobjects", true); - })))); + Mapping mergeWith = mapperService.parseMapping( + "_doc", + MergeReason.MAPPING_UPDATE, + new CompressedXContent(BytesReference.bytes(topMapping(b -> { + b.field("subobjects", true); + }))) + ); MapperException exception = expectThrows( MapperException.class, () -> mapper.mapping().merge(mergeWith, MergeReason.MAPPING_UPDATE, Long.MAX_VALUE) diff --git a/server/src/test/java/org/elasticsearch/index/similarity/SimilarityTests.java b/server/src/test/java/org/elasticsearch/index/similarity/SimilarityTests.java index a52fd7e608d24..9b686417badfc 100644 --- a/server/src/test/java/org/elasticsearch/index/similarity/SimilarityTests.java +++ b/server/src/test/java/org/elasticsearch/index/similarity/SimilarityTests.java @@ -27,6 +27,7 @@ import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperParsingException; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.lucene.similarity.LegacyBM25Similarity; import org.elasticsearch.plugins.Plugin; import org.elasticsearch.test.ESSingleNodeTestCase; @@ -254,7 +255,7 @@ public void testResolveSimilaritiesFromMapping_Unknown() throws IOException { IndexService indexService = createIndex("foo"); MapperParsingException e = expectThrows( MapperParsingException.class, - () -> indexService.mapperService().parseMapping("type", new CompressedXContent(mapping)) + () -> indexService.mapperService().parseMapping("type", MergeReason.MAPPING_UPDATE, new CompressedXContent(mapping)) ); assertThat(e.getMessage(), equalTo("Failed to parse mapping: Unknown Similarity type [unknown_similarity] for field [field1]")); } diff --git a/test/framework/src/main/java/org/elasticsearch/index/mapper/MetadataMapperTestCase.java b/test/framework/src/main/java/org/elasticsearch/index/mapper/MetadataMapperTestCase.java index 77391aadaa554..1b00ba3e9fd09 100644 --- a/test/framework/src/main/java/org/elasticsearch/index/mapper/MetadataMapperTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/index/mapper/MetadataMapperTestCase.java @@ -12,6 +12,7 @@ import org.elasticsearch.core.CheckedConsumer; import org.elasticsearch.index.IndexVersion; import org.elasticsearch.index.IndexVersions; +import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.test.index.IndexVersionUtils; import org.elasticsearch.xcontent.XContentBuilder; @@ -120,7 +121,7 @@ public final void testUnsupportedParametersAreRejected() throws IOException { + "}"; MapperParsingException exception = expectThrows( MapperParsingException.class, - () -> mapperService.parseMapping("_doc", new CompressedXContent(mappingAsString)) + () -> mapperService.parseMapping("_doc", MergeReason.MAPPING_UPDATE, new CompressedXContent(mappingAsString)) ); assertEquals( "Failed to parse mapping: unknown parameter [anything] on metadata field [" + fieldName() + "]", @@ -136,7 +137,7 @@ public final void testFixedMetaFieldsAreNotConfigurable() throws IOException { String mappingAsString = "{\n" + " \"_doc\" : {\n" + " \"" + fieldName() + "\" : {\n" + " }\n" + " }\n" + "}"; MapperParsingException exception = expectThrows( MapperParsingException.class, - () -> mapperService.parseMapping("_doc", new CompressedXContent(mappingAsString)) + () -> mapperService.parseMapping("_doc", MergeReason.MAPPING_UPDATE, new CompressedXContent(mappingAsString)) ); assertEquals("Failed to parse mapping: " + fieldName() + " is not configurable", exception.getMessage()); } @@ -161,7 +162,7 @@ public void testTypeAndFriendsAreAcceptedBefore_8_6_0() throws IOException { + " }\n" + " }\n" + "}"; - assertNotNull(mapperService.parseMapping("_doc", new CompressedXContent(mappingAsString))); + assertNotNull(mapperService.parseMapping("_doc", MergeReason.MAPPING_UPDATE, new CompressedXContent(mappingAsString))); } } @@ -184,7 +185,7 @@ public void testTypeAndFriendsAreDeprecatedFrom_8_6_0() throws IOException { + " }\n" + " }\n" + "}"; - assertNotNull(mapperService.parseMapping("_doc", new CompressedXContent(mappingAsString))); + assertNotNull(mapperService.parseMapping("_doc", MergeReason.MAPPING_UPDATE, new CompressedXContent(mappingAsString))); assertWarnings("Parameter [" + param + "] has no effect on metadata field [" + fieldName() + "] and will be removed in future"); } }