我有一个看起来像这样的文件:
{
field: 'value',
field2: 'value',
scan: [
[
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
],
[
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
],
[
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
{
dontDeleteMe: 'keepMe',
arrayToDelete: [0,1,2]
},
],
]
}
我们只想删除嵌套在“扫描”列表列表中的字典中的arrayToDelete的任何实例。
当前,我一直在使用
update({}, {$unset: {"scans.0.0.arrayToDelete":1}}, {multi: true})
删除数组。但是,这只会删除您想像的第一个(0.0)
。
是否可以遍历扫描数组和嵌套数组以删除“ arrayToDelete”,并保留所有其他字段?
谢谢
答案 0 :(得分:2)
所以我在评论中问了一个问题,但是您似乎已经走开了,所以我想我只是回答了我看到的三种可能的情况。
首先,我不确定嵌套数组中显示的元素是否是数组中的仅 元素,或者实际上是arrayToDelete
是这些元素中出现的仅 字段。所以基本上我需要抽象一点并包括这种情况:
{
field: 'value',
field2: 'value',
scan: [
[
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
[
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
[
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
]
}
这将使用$pull
运算符,因为这完全是删除数组元素。您可以在现代的MongoDB中使用以下语句执行此操作:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
)
这样会更改所有匹配的文档:
{
"_id" : ObjectId("5ca1c36d9e31550a618011e2"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
]
]
}
因此,包含该字段的每个元素都将被删除。
您在这里使用$unset
。这与您正在执行的“硬索引” 版本略有不同:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[].$[].arrayToDelete": "" } }
)
将所有匹配的文档更改为:
{
"_id" : ObjectId("5ca1c4c49e31550a618011e3"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"anotherField" : "a"
},
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
}
],
[
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
}
],
[
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
}
]
]
}
所以一切仍然存在,但是从每个内部数组文档中仅删除了已标识的字段。
实际上只是使用$set
并清除之前的所有内容的简单情况:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$set": { "scan": [] } }
)
应该可以很好地预期结果:
{
"_id" : ObjectId("5ca1c5c59e31550a618011e4"),
"field" : "value",
"field2" : "value",
"scan" : [ ]
}
您应该首先看到的是查询谓词。通常,这是一个很好的主意,以确保您不匹配甚至“尝试” 以使文档上的更新条件得到满足,这些文档甚至不包含要更新的模式的数据。嵌套数组充其量是困难,在实际情况中,您应该避免使用它们,因为您通常“真正的意思” 实际上是用单个数组表示的,而其他属性则表示您“想” 嵌套实际上是为您服务的。
但是仅仅因为它们是 hard 并不意味着不可能。只是您需要了解$elemMatch
:
db.colelction.find(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
}}
)
这是基本的find()
示例,它根据 outer 数组的$elemMatch
条件进行匹配,并使用另一个$elemMatch
来匹配内部阵列。即使这个“出现” 是一个奇异的谓词。像这样:
"scan.arrayToDelete": { "$exists": true }
只是行不通。都不会:
"scan..arrayToDelete": { "$exists": true }
使用“双点” ..
,因为这基本上是无效的。
这是与需要处理的“文档”相匹配的“ <查询>谓词”,其余内容则用于确定“需要更新的部分”。
在案例1 中,为了从内部数组中$pull
开始,我们首先需要能够识别外部元素数组包含要更新的数据。这就是"scan.$[a]"
使用positional filtered $[<identifier>]
运算符所做的事情。
该运算符基本上将数组中匹配的 indices (所以其中的许多)转置到另一个谓词它是在update
样式命令的第三部分中与arrayFilters
部分一起定义的。本节从命名标识符的角度基本上定义了要满足的条件。
在这种情况下,“标识符”被命名为a
,这是arrayFilters
条目中使用的前缀:
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
在上下文中与实际的 update语句部分相同:
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
然后从"a"
作为外部数组元素的标识符(从"scan"
开始向内)的角度出发,然后采用与原始查询相同的条件谓词,但来自第一 $elemMatch
语句的“内” 。因此,从已经“向内看” 每个外部元素的内容的角度,您基本上可以将其视为“查询中的查询”。
通过相同的标记,$pull
的作用与“查询中的查询” 相似,因为从数组元素的角度来看,它也是自变量。因此,仅存在arrayToDelete
字段,而不是:
// This would be wrong! and do nothing :(
{
"$pull": {
"scan.$[a]": { "$elemMatch": { "arrayToDelete": { "$exists": true } } }
}
}
但这都是$pull
所特有的,其他情况也有所不同:
第2种情况将查看您只想$unset
命名字段的位置。您只需为字段命名就很容易了,对吧?完全不正确,因为以下内容显然不符合我们先前所知道的:
{ "$unset": { "scan.arrayToDelete": "" } } // Not right :(
当然,要注意所有内容的数组索引只是一个痛苦:
{ "$unset": {
"scan.0.0.arrayToDelete": "",
"scan.0.1.arrayToDelete": "",
"scan.0.2.arrayToDelete": "",
"scan.0.3.arrayToDelete": "", // My fingers are tired :-<
} }
这是positional all $[]
运算符的原因。这比positional filtered $[<identifier>]
的“蛮力” 多一点,因为与其匹配arrayFilters
中提供的另一个谓词适用于该“索引”处数组内容中的 一切 。实际上,这实际上是一种说出“所有索引” 而不用像上面显示的 horrible 例那样键入每个字符的方法。
因此,并非所有情况都适用,但它肯定非常适合$unset
,因为它具有非常具体的路径命名,这当然不重要如果该路径与数组的每个元素都不匹配。
您可以仍然使用arrayFilters
和positional filtered $[<identifier>]
,但是这太过分了。此外,演示另一种方法也无妨。
但是,当然值得理解 该语句的外观,所以:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[a].$[b].arrayToDelete": "" } },
{
"arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } },
{ "b.arrayToDelete": { "$exists": true } },
]
}
)
请注意,"b.arrayToDelete"
可能一开始并不是您所期望的,但是考虑到"scan.$[a].$[b]
中的位置,从b
开始确实应该有元素名称如图所示,通过“点符号”。实际上,在两种情况下。同样,$unset
仍然只适用于命名字段,因此确实不需要选择标准。
和案例3 。嗯,这很简单,如果您删除内容后不需要 将其他任何内容保留在数组中(即$pull
,与之匹配的字段仅是 或其中的$unset
),然后就不用弄乱其他任何东西,而只需擦除数组。
如果您要考虑这一点,这是一个重要的区别,以澄清带有命名字段的文档是否嵌套数组中的 only 元素,实际上命名的键是文档中存在的仅 内容。
原因是,如此处所示,在这些条件下使用$pull
,您将获得:
{
"_id" : ObjectId("5ca321909e31550a618011e6"),
"field" : "value",
"field2" : "value",
"scan" : [
[ ],
[ ],
[ ]
]
}
或使用$unset
:
{
"_id" : ObjectId("5ca322bc9e31550a618011e7"),
"field" : "value",
"field2" : "value",
"scan" : [
[{ }, { }, { }, { }],
[{ }, { }, { }, { }],
[{ }, { }, { }, { }]
]
}
这两者显然是不希望的。因此,如果arrayToDelete
字段完全是其中的 内容,这就是您的理由,那么删除所有< / em>只是将数组替换为空数组。甚至$unset
整个文档属性。
但是请注意,所有这些“奇特的事物” (当然是$set
除外)都要求您必须至少具有MongoDB 3.6可用为了使用此功能。
如果您仍在运行比该版本更旧的MongoDB(从撰写之日起,您实际上不应该这样做,因为从此日期起仅5个月您的官方支持就用光了),然后在{ {3}}实际上是给你的。
答案 1 :(得分:0)
我已尝试使用给定的示例文档及其工作原理,您必须使用$[]
来实现:
db.collectionName.(update({},{$unset: {
"scan.$[].$[].arrayToDelete": 1
}})