Pārlūkot izejas kodu

Call fixRedundantIncludes on dynamic mapping updates (#74903)

Dynamic mappings updates containing nested mappings were not fixing
their redundant include settings before being posted back to the put
mappings service; this meant that a dynamic mapping that had both
'include_in_parent' and 'include_in_root' set to 'true' could cause an
error if it was returned from multiple shards, because the first mapping
would be updated to have 'include_in_root' set to 'false' when merged,
and merging this with the second mapping with 'include_in_root' set to
'true' would cause a conflict.

This commit ensures that fixRedundantIncludes is called on dynamic
mappings before they are posted back to the mapping service.

Fixes #74899
Alan Woodward 4 gadi atpakaļ
vecāks
revīzija
b83c4fb46d

+ 1 - 0
server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java

@@ -263,6 +263,7 @@ public final class DocumentParser {
         RootObjectMapper root;
         if (dynamicMappers.isEmpty() == false) {
             root = createDynamicUpdate(mappingLookup, dynamicMappers);
+            root.fixRedundantIncludes();
         } else {
             root = mappingLookup.getMapping().getRoot().copyAndReset();
         }

+ 68 - 0
server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java

@@ -412,6 +412,44 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
         assertThat(fields.size(), equalTo(new HashSet<>(fields).size()));
     }
 
+    public void testRecursiveIncludeInParent() throws IOException {
+
+        // if we have a nested hierarchy, and all nested mappers have 'include_in_parent'
+        // set to 'true', then values from the grandchild nodes should be copied all the
+        // way up the hierarchy and into the root document, even if 'include_in_root' has
+        // explicitly been set to 'false'.
+
+        MapperService mapperService = createMapperService(mapping(b -> {
+            b.startObject("nested1");
+            b.field("type", "nested");
+            b.field("include_in_parent", true);
+            b.field("include_in_root", false);
+            b.startObject("properties");
+            b.startObject("nested1_id").field("type", "keyword").endObject();
+            b.startObject("nested2");
+            b.field("type", "nested");
+            b.field("include_in_parent", true);
+            b.field("include_in_root", false);
+            b.startObject("properties");
+            b.startObject("nested2_id").field("type", "keyword").endObject();
+            b.endObject();
+            b.endObject();
+            b.endObject();
+            b.endObject();
+        }));
+
+        ParsedDocument doc = mapperService.documentMapper().parse(source(b -> {
+            b.startObject("nested1");
+            b.field("nested1_id", "1");
+            b.startObject("nested2");
+            b.field("nested2_id", "2");
+            b.endObject();
+            b.endObject();
+        }));
+
+        assertNotNull(doc.rootDoc().getField("nested1.nested2.nested2_id"));
+    }
+
     /**
      * Same as {@link NestedObjectMapperTests#testMultipleLevelsIncludeRoot1()} but tests for the
      * case where the transitive {@code include_in_parent} and redundant {@code include_in_root}
@@ -801,4 +839,34 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
         })));
         assertEquals("the [include_in_root] parameter can't be updated on a nested object mapping", e2.getMessage());
     }
+
+    public void testMergeNestedMappingsFromDynamicUpdate() throws IOException {
+
+        // Check that dynamic mappings have redundant includes removed
+
+        MapperService mapperService = createMapperService(topMapping(b -> {
+            b.startArray("dynamic_templates");
+            b.startObject();
+            b.startObject("object_fields");
+            b.field("match_mapping_type", "object");
+            b.startObject("mapping");
+            b.field("type", "nested");
+            b.field("include_in_parent", true);
+            b.field("include_in_root", true);
+            b.endObject();
+            b.field("match", "*");
+            b.endObject();
+            b.endObject();
+            b.endArray();
+        }));
+
+        ParsedDocument doc = mapperService.documentMapper().parse(source(b -> b.startObject("object").endObject()));
+
+        merge(mapperService, Strings.toString(doc.dynamicMappingsUpdate()));
+        merge(mapperService, Strings.toString(doc.dynamicMappingsUpdate()));
+
+        assertThat(
+            Strings.toString(mapperService.documentMapper().mapping()),
+            containsString("\"properties\":{\"object\":{\"type\":\"nested\",\"include_in_parent\":true}}"));
+    }
 }