Skip to content

Commit

Permalink
RootObjectMapper to clone the runtime fields map (elastic#65378)
Browse files Browse the repository at this point in the history
ObjectMappers clone themselves as part of their merge method, and also as part of dynamic mapping updates. As part of its clone, RootObjectMapper should clone its map of runtime fields, otherwise changes to the cloned root around runtime fields are reflected in the original root, which is not desirable.
  • Loading branch information
javanna committed Nov 24, 2020
1 parent 679bc3c commit 6144d9e
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,13 @@ private boolean processField(RootObjectMapper.Builder builder, String fieldName,
this.numericDetection = numericDetection;
}

@Override
protected ObjectMapper clone() {
ObjectMapper clone = super.clone();
((RootObjectMapper) clone).runtimeFieldTypes = new HashMap<>(this.runtimeFieldTypes);
return clone;
}

@Override
public ObjectMapper mappingUpdate(Mapper mapper) {
RootObjectMapper update = (RootObjectMapper) super.mappingUpdate(mapper);
Expand All @@ -250,7 +257,8 @@ public ObjectMapper mappingUpdate(Mapper mapper) {
update.dynamicDateTimeFormatters = new Explicit<>(Defaults.DYNAMIC_DATE_TIME_FORMATTERS, false);
update.dateDetection = new Explicit<>(Defaults.DATE_DETECTION, false);
update.numericDetection = new Explicit<>(Defaults.NUMERIC_DETECTION, false);
update.runtimeFieldTypes = new HashMap<>();
//also no need to carry the already defined runtime fields, only new ones need to be added
update.runtimeFieldTypes.clear();
return update;
}

Expand Down Expand Up @@ -355,7 +363,7 @@ protected void doMerge(ObjectMapper mergeWith, MergeReason reason) {
this.dynamicTemplates = mergeWithObject.dynamicTemplates;
}
}

assert this.runtimeFieldTypes != mergeWithObject.runtimeFieldTypes;
this.runtimeFieldTypes.putAll(mergeWithObject.runtimeFieldTypes);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,48 @@ public void testRuntimeSection() throws IOException {
assertEquals(mapping, mapperService.documentMapper().mappingSource().toString());
}

public void testRuntimeSectionRejectedUpdate() throws IOException {
MapperService mapperService;
{
XContentBuilder builder = XContentFactory.jsonBuilder().startObject().startObject("_doc");
builder.startObject("runtime");
builder.startObject("field").field("type", "test").endObject();
builder.endObject();
builder.startObject("properties");
builder.startObject("concrete").field("type", "keyword").endObject();
builder.endObject();
builder.endObject().endObject();
mapperService = createMapperService(builder);
assertEquals(Strings.toString(builder), mapperService.documentMapper().mappingSource().toString());
MappedFieldType concrete = mapperService.fieldType("concrete");
assertThat(concrete, instanceOf(KeywordFieldMapper.KeywordFieldType.class));
MappedFieldType field = mapperService.fieldType("field");
assertThat(field, instanceOf(RuntimeField.class));
}
{
XContentBuilder builder = XContentFactory.jsonBuilder().startObject().startObject("_doc");
builder.startObject("runtime");
builder.startObject("another_field").field("type", "test").endObject();
builder.endObject();
builder.startObject("properties");
//try changing the type of the existing concrete field, so that merge fails
builder.startObject("concrete").field("type", "text").endObject();
builder.endObject();
builder.endObject().endObject();

expectThrows(IllegalArgumentException.class, () -> merge(mapperService, builder));

//make sure that the whole rejected update, including changes to runtime fields, has not been applied
MappedFieldType concrete = mapperService.fieldType("concrete");
assertThat(concrete, instanceOf(KeywordFieldMapper.KeywordFieldType.class));
MappedFieldType field = mapperService.fieldType("field");
assertThat(field, instanceOf(RuntimeField.class));
assertNull(mapperService.fieldType("another_field"));
assertEquals("{\"_doc\":{\"runtime\":{\"field\":{\"type\":\"test\"}},\"properties\":{\"concrete\":{\"type\":\"keyword\"}}}}",
Strings.toString(mapperService.documentMapper().mapping().root));
}
}

public void testRuntimeSectionMerge() throws IOException {
MapperService mapperService;
{
Expand Down

0 comments on commit 6144d9e

Please sign in to comment.