您可以$pull“外部数组”中的“第一个匹配项”,只需执行以下操作即可删除“所有内部元素”:
db.Events.updateMany(
{
"Distributions.DistributionData": {
"$elemMatch": {
"Key": null,
"Value": null,
"Children": null
}
}
},
{
"$pull": {
"Distributions.$.DistributionData": {
"Key": null,
"Value": null,
"Children": null
}
}
}
)
如果您在 "Distributions" 数组中只有一个条目,或者至少其中一个条目具有匹配条件的子数组条目,那很好。这就是 positional $ operator 与所有版本的 MongoDB 一起工作的方式。
如果数据在“外部”"Distributions" 数组中有“多个”匹配项,那么如果您有 MongoDB 3.6,则可以应用 positional filtered $[<identifier>] operator 来修改所有匹配的条目:
db.Events.updateMany(
{
"Distributions.DistributionData": {
"$elemMatch": {
"Key": null,
"Value": null,
"Children": null
}
}
},
{
"$pull": {
"Distributions.$[element].DistributionData": {
"Key": null,
"Value": null,
"Children": null
}
}
},
{
"arrayFilters": [
{ "element.DistributionData": {
"$elemMatch": {
"Key": null,
"Value": null,
"Children": null
}
}}
]
}
)
在这种情况下,arrayFilters 选项定义了一个条件,我们通过该条件匹配“外部”数组中的条目,以便它实际上可以应用于匹配的所有内容。
或者实际上,由于$pull 本身本质上具有这些条件,那么您可以在这种情况下交替使用positional all $[] 运算符:
db.Event.updateMany(
{
"Distributions.DistributionData": {
"$elemMatch": {
"Key": null,
"Value": null,
"Children": null
}
}
},
{
"$pull": {
"Distributions.$[].DistributionData": {
"Key": null,
"Value": null,
"Children": null
}
}
}
)
任何一种情况都会通过删除带有所有 null 键的内部项目来更改问题中的文档:
{
"_id" : UUID("cf397865-c000-4f51-8959-1aae84769706"),
"CreationDateTime" : ISODate("2016-05-06T05:09:14.589Z"),
"WKT" : "",
"Distributions" : [
{
"_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
"DeliveryType" : 1,
"DistributionData" : [
{
"Key" : "Topic",
"Value" : "Topics",
"Children" : null
},
{
"Key" : "Message",
"Value" : "test",
"Children" : null
}
],
"Schedules" : [
ISODate("2016-05-06T05:09:56.988Z")
]
}
]
}
“查询”条件都使用$elemMatch 进行文档选择。这实际上是positional $ operator 所必需的,以便获得用于“第一次匹配”的“位置索引”。虽然这实际上不是 positional filtered $[<identifier>] 或 positional all $[] 运算符的“要求”,但它仍然很有用,因此您甚至不必考虑更新与 $pull 的后续更新条件不匹配的文档或arrayFilters 选项。
至于$pull 本身,这里的条件实际上适用于“每个”数组元素,因此在该操作中不需要$elemMatch,因为我们已经在查看“元素”级别。
第三个示例表明positional all $[] 运算符可以简单地使用那些考虑到每个“内部”数组元素的$pull 条件,并且只适用于所有“外部”数组元素。所以positional filtered $[<identifier>] 表达式的实际意义是“仅”处理那些实际匹配“内部”条件的“外部”数组元素。因此我们在考虑匹配每个“内部”数组元素时使用$elemMatch。
如果您实际上至少没有 MongoDB 3.6,那么您正在使用第一种形式,并且可能会重复该形式,直到更新最终不再返回修改后的文档,表明没有更多符合条件的元素。
在How to Update Multiple Array Elements in mongodb 上有关于“替代方案”作为方法的更详细的文章,但只要您的数据适合初始情况或者您确实有可用的 MongoDB 3.6,那么这是正确的方法.
如果您想查看 MongoDB 3.6 新语法的完整效果。这是我用来验证更新语句的问题中对文档的更改:
{
"_id" : UUID("cf397865-c000-4f51-8959-1aae84769706"),
"CreationDateTime" : ISODate("2016-05-06T05:09:14.589Z"),
"WKT" : "",
"Distributions" : [
{
"_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
"DeliveryType" : 1,
"DistributionData" : [
{
"Key" : "Topic",
"Value" : "Topics",
"Children" : null
},
{
"Key" : null,
"Value" : null,
"Children" : null
},
{
"Key" : "Message",
"Value" : "test",
"Children" : null
},
{
"Key" : null,
"Value" : null,
"Children" : null
}
],
"Schedules" : [
ISODate("2016-05-06T05:09:56.988Z")
]
},
{
"_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
"DeliveryType" : 1,
"DistributionData" : [
{
"Key" : "Topic",
"Value" : "Topics",
"Children" : null
},
{
"Key" : null,
"Value" : null,
"Children" : null
},
{
"Key" : "Message",
"Value" : "test",
"Children" : null
},
{
"Key" : null,
"Value" : null,
"Children" : null
}
],
"Schedules" : [
ISODate("2016-05-06T05:09:56.988Z")
]
}
]
}
这基本上重复了一些“外部”和“内部”条目,以显示该语句如何删除所有 null 值。
注意 arrayFilters 在 .update() 和类似方法的“选项”参数中指定,语法通常与所有最新版本的驱动程序版本兼容,甚至与 MongoDB 版本之前的版本兼容3.6.
但是,mongo shell 并非如此,因为该方法在那里实现的方式(“具有讽刺意味的是,为了向后兼容”)arrayFilters 参数无法被解析选项的内部方法识别和删除为了提供与先前 MongoDB 服务器版本的“向后兼容性”和“遗留”.update() API 调用语法。
因此,如果您想在 mongo shell 或其他“基于 shell”的产品(尤其是 Robo 3T)中使用该命令,您需要来自开发分支或生产版本 3.6 或更高版本的最新版本。
值得注意的是,Robo 3T 仍然基于 MongoDB 3.4 shell。因此,即使连接到功能强大的 MongoDB 3.6 实例,这些选项也不会从该程序传递到服务器。建议仅使用外壳和受支持的产品,尽管还有一些其他产品没有相同的限制。