在 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
,用于保留其他字段的验证目的。
使用以下文档,我们能够完成我们需要做的事情,这实际上是“重新生成”存储祖先数组的文档,如下所示:
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
,用于保留其他字段的验证目的。