【问题标题】:Remove Object from Nested Array by Multiple Criteria按多个条件从嵌套数组中删除对象
【发布时间】:2018-10-16 05:57:04
【问题描述】:

我在 mongoDB 中有这个架构文档:

{
    "_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
                }
            ],
            "Schedules": [
                ISODate("2016-05-06T05:09:56.988Z")
            ]
        }
    ],
}

我想清理我的数据库。所以我决定删除 DistributionData 中的空对象。如何删除所有三个属性都具有空值的对象:

 {
  "Key": null,
  "Value": null,
  "Children": null
 }.

我写了这个查询:

db.Events.update(
    {},
    {$pull:
        {
            results: {
                $elemMatch: {
                    "Distributions[0].DistributionData" : {$in:[null]}
                }
            }
        }
     },
     { multi: true }
)

当我执行这个查询时,什么也没有发生!我知道$elemMatch 是错误的.. 现在我如何删除DistributionData中所有字段为空的json对象? 我读了this 和this 但让我很困惑......

编辑

我写了这个查询:

db.Events.update(
    {},
    {$pull:
        {
            Distributions : {
                DistributionData:{
                $elemMatch: {
                    "Key" : null
                }
              }
            }
        }
     },
     { multi: true }
)

此查询将完全删除 Distributions 中 DistributionData 数组有一个带有空键的对象的对象:

结果:

{
"_id": UUID("cf397865-c000-4f51-8959-1aae84769706"),
"CreationDateTime": ISODate("2016-05-06T05:09:14.589Z"),
"WKT" : "",
"Distributions" : [],...

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案1】:

    您可以$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 实例,这些选项也不会从该程序传递到服务器。建议仅使用外壳和受支持的产品,尽管还有一些其他产品没有相同的限制。

    【讨论】:

    • 谢谢老兄,我会尽快宣布你..@neillunn
    • @sayreskabir 很高兴它有帮助。如果您仍然需要更多解释为什么这有效,请添加更多信息。
    • 我首先检查了您的建议,因为我在“发行版”中有一个条目。但是您的描述是完整的,我知道,如果我在 Distributions 中有多个数组,您的代码将完美运行。
    猜你喜欢
    • 2021-04-19
    • 1970-01-01
    • 2019-04-30
    • 2020-12-07
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多