在 MongoDB 中使用 $indexOfArray 和 $slice 更新嵌套数组

Updating a nested array using $indexOfArray and $slice in MongoDB

使用以下文档,我们能够完成我们需要做的事情,这实际上是“重新生成”存储祖先数组的文档,如下所示:

db.getCollection("myarrays").insertOne({
    "myid": "A",
    "ancestors": [
         {
             "_id": "parent1",
             "type": "type1"
         },
         {
             "_id": "parent2",
             "type": "type1"
         },
         {
             "_id": "parent3",
             "type": "type1"
         }       
    ]
})
db.getCollection("myarrays").aggregate(
    [
        { 
            "$match" : { 
                "ancestors._id" : "parent2"
            }
        }, 
        { 
            "$project" : { 
                "ancestors" : { 
                    "$concatArrays" : [
                        [
                            { 
                                "_id" : "parent4", 
                                "type" : "type1"
                            }, 
                            { 
                                "_id" : "parent5", 
                                "type" : "type1"
                            }
                        ], 
                        { 
                            "$slice" : [
                                "$ancestors", 
                                { 
                                    "$add" : [
                                        { 
                                            "$indexOfArray" : [
                                                "$ancestors._id", 
                                                "parent2"
                                            ]
                                        }, 
                                        1.0
                                    ]
                                }, 
                                { 
                                    "$size" : [
                                        "$ancestors"
                                    ]
                                }
                            ]
                        }
                    ]
                }
            }
        }
    ], 
    { 
        "allowDiskUse" : false
    }
);

这有效地找到了任何作为“parent2”的孙子的文档,并将它们重新设置为“parent5”(它本身是“parent4”的子项)的父项。生成的文档如下所示:

{ 
    "_id" : ObjectId("61e9860d97f54a3cebb47e3d"), 
    "ancestors" : [
        {
            "_id" : "parent4", 
            "type" : "type1"
        }, 
        {
            "_id" : "parent5", 
            "type" : "type1"
        }, 
        {
            "_id" : "parent3", 
            "type" : "type1"
        }
    ]
}

效果很好。但是,在 ancestors 嵌套在另一个数组中的情况下,我正在努力做完全相同的事情:

db.getCollection("myarraysnested").insertOne(
    {
        "references": [
            { 
                "myid": "A",
                "ancestors": [
                     {
                         "_id": "parent1",
                         "type": "type1"
                     },
                     {
                         "_id": "parent2",
                         "type": "type1"
                     },
                     {
                         "_id": "parent3",
                         "type": "type1"
                     }       
                ]
            },
            { 
                "myid": "B",
                "ancestors": [
                     {
                         "_id": "parent4",
                         "type": "type1"
                     },
                     {
                         "_id": "parent5",
                         "type": "type1"
                     },
                     {
                         "_id": "parent3",
                         "type": "type1"
                     }       
                ]
            },
            { 
                "myid": "C",
                "ancestors": [
                     {
                         "_id": "parent0",
                         "type": "type1"
                     },
                     {
                         "_id": "parent1",
                         "type": "type1"
                     },
                     {
                         "_id": "parent2",
                         "type": "type1"
                     },
                     {
                         "_id": "parent6",
                         "type": "type2"
                     }       
                ]
            }
            
        ]
    }
)

期望的结果是匹配 ancestors 数组的 3 个 references 元素中的 2 个包含 _i = to "parent2" 的元素 ancestors 数组已更新,看起来与上面的非嵌套示例看起来一样,导致以下输出:

{ 
    "_id" : ObjectId("61e9860d97f54a3cebb47e3c"), 
    "references" : [
        {
            "myid" : "A", 
            "ancestors" : [
                {
                    "_id" : "parent4", 
                    "type" : "type1"
                }, 
                {
                    "_id" : "parent5", 
                    "type" : "type1"
                }, 
                {
                    "_id" : "parent3", 
                    "type" : "type1"
                }
            ]
        }, 
        {
            "myid" : "B", 
            "ancestors" : [
                {
                    "_id" : "parent4", 
                    "type" : "type1"
                }, 
                {
                    "_id" : "parent5", 
                    "type" : "type1"
                }, 
                {
                    "_id" : "parent3", 
                    "type" : "type1"
                }
            ]
        }, 
        {
            "myid" : "C", 
            "ancestors" : [
                {
                    "_id" : "parent4", 
                    "type" : "type1"
                }, 
                {
                    "_id" : "parent5", 
                    "type" : "type1"
                }, 
                {
                    "_id" : "parent6", 
                    "type" : "type2"
                }
            ]
        }
    ]
}

我已经尝试了几次使用 $filter$map 的迭代,但至少不能完全正确,找到嵌套数组中匹配的元素的索引.如果可能的话,我宁愿避免 $unwind,但如果不可能,那也可以。

注意:管道 is/will 用作更新语句的一部分以实际更改文档,因此更新管道样式的任何限制都适用。

您可以先 $unwind references 以便更轻松地处理和重用您的工作逻辑。使用 $cond 有条件地对具有 parent2 的元素执行您的工作逻辑。最后,$group 重新组合展开的文档。使用 $merge 将聚合结果更新回集合。

db.myarrays.aggregate([
  {
    "$match": {
      "references.ancestors._id": "parent2"
    }
  },
  {
    "$unwind": "$references"
  },
  {
    "$addFields": {
      "references.ancestors": {
        "$cond": {
          "if": {
            "$in": [
              "parent2",
              "$references.ancestors._id"
            ]
          },
          "then": {
            "$concatArrays": [
              [
                {
                  "_id": "parent4",
                  "type": "type1"
                },
                {
                  "_id": "parent5",
                  "type": "type1"
                }
              ],
              {
                "$slice": [
                  "$references.ancestors",
                  {
                    "$add": [
                      {
                        "$indexOfArray": [
                          "$references.ancestors._id",
                          "parent2"
                        ]
                      },
                      1.0
                    ]
                  },
                  {
                    "$size": [
                      "$references.ancestors"
                    ]
                  }
                ]
              }
            ]
          },
          "else": "$references.ancestors"
        }
      }
    }
  },
  {
    "$group": {
      "_id": "$_id",
      "references": {
        "$push": "$references"
      }
    }
  },
  {
    "$merge": {
      "into": "myarrays",
      "on": "_id",
      "whenMatched": "merge"
    }
  }
])

这里是Mongo playground供您参考。请注意 Mongo playground 仅显示结果集,而不是更新的文档。下面是我的本地 MongoDB 运行 结果的屏幕截图。您应该会看到有一个额外的字段 field,其值为 to be kept,用于保留其他字段的验证目的。