Przeglądaj źródła

Don't run include_in_parent when in copy_to context (#87123)

We changed how copy_to is implemented in #79922, which moved
the handling of dots in field names into a specialised parser. Unfortunately,
while doing this we added a bug whereby every time a copy_to directive
is processed for a nested field, the nested field's include_in_parent logic
would be run, meaning that the parent would end up with multiple copies
of the nested child's fields.

This commit fixes this by only running include_in_parent when the parser
is not in a copy_to context. It also fixes another bug that meant the parent
document would contain multiple copies of the ID field.

Fixes #87036
Alan Woodward 3 lat temu
rodzic
commit
4e1f72552c

+ 6 - 0
docs/changelog/87123.yaml

@@ -0,0 +1,6 @@
+pr: 87123
+summary: Don't run `include_in_parent` when in `copy_to` context
+area: Mapping
+type: bug
+issues:
+ - 87036

+ 10 - 3
server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java

@@ -278,7 +278,7 @@ public final class DocumentParser {
         innerParseObject(context, mapper);
         // restore the enable path flag
         if (mapper.isNested()) {
-            nested(context, (NestedObjectMapper) mapper);
+            copyNestedFields(context, (NestedObjectMapper) mapper);
         }
     }
 
@@ -344,7 +344,14 @@ public final class DocumentParser {
         );
     }
 
-    private static void nested(DocumentParserContext context, NestedObjectMapper nested) {
+    private static void copyNestedFields(DocumentParserContext context, NestedObjectMapper nested) {
+        if (context.isWithinCopyTo()) {
+            // Only process the nested document after we've finished parsing the actual
+            // doc; we can't copy_to outside of the current nested context, so if we are
+            // in a copy_to context then we're adding data within the doc and we haven't
+            // finished parsing yet.
+            return;
+        }
         LuceneDocument nestedDoc = context.doc();
         LuceneDocument parentDoc = nestedDoc.getParent();
         Version indexVersion = context.indexSettings().getIndexVersionCreated();
@@ -363,7 +370,7 @@ public final class DocumentParser {
     private static void addFields(Version indexCreatedVersion, LuceneDocument nestedDoc, LuceneDocument rootDoc) {
         String nestedPathFieldName = NestedPathFieldMapper.name(indexCreatedVersion);
         for (IndexableField field : nestedDoc.getFields()) {
-            if (field.name().equals(nestedPathFieldName) == false) {
+            if (field.name().equals(nestedPathFieldName) == false && field.name().equals(IdFieldMapper.NAME) == false) {
                 rootDoc.add(field);
             }
         }

+ 48 - 0
server/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java

@@ -744,4 +744,52 @@ public class CopyToMapperTests extends MapperServiceTestCase {
             );
         }
     }
+
+    public void testCopyToMultipleNested() throws IOException {
+
+        // Don't copy values beyond a single step
+
+        DocumentMapper documentMapper = createDocumentMapper("""
+            { "_doc" : { "properties" : {
+                "_all" : { "type" : "text" },
+                "du" : {
+                    "type" : "nested",
+                    "include_in_root" : "true",
+                    "properties" : {
+                        "_all" : { "type" : "text" },
+                        "bc" : {
+                            "type" : "nested",
+                            "include_in_parent" : "true",
+                            "properties" : {
+                                "_all" : { "type" : "text" },
+                                "bc4" : {
+                                    "type" : "nested",
+                                    "include_in_parent" : "true",
+                                    "properties" : {
+                                        "_all" : { "type" : "text" },
+                                        "area" : {
+                                            "type" : "text",
+                                            "copy_to" : [
+                                                "_all",
+                                                "du._all",
+                                                "du.bc._all",
+                                                "du.bc.bc4._all"
+                                            ]
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }}}""");
+
+        ParsedDocument doc = documentMapper.parse(source("""
+            { "du" : { "bc" : [ { "bc4": { "area" : "foo" } }, { "bc4" : { "area" : "bar" } } ] } }
+            """));
+
+        assertEquals(1, doc.rootDoc().getFields("_id").length);
+        assertEquals(2, doc.rootDoc().getFields("du._all").length);
+
+    }
 }