按多个条件从嵌套数组中删除对象

时间:2018-05-06 06:59:06

标签: mongodb mongodb-query

我在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中所有feild为null的json对象? 我看了thisthis,但让我感到困惑......

  

修改

我写了这个查询:

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

此查询将完全删除DistributionData数组中具有null键对象的分布内的对象:

结果

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

1 个答案:

答案 0 :(得分:3)

你可以$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一起使用。

如果数据有&#34;多个&#34;匹配在&#34;外部&#34; "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选项定义了一个条件,通过该条件我们匹配&#34;外部&#34;中的条目。数组,这样实际上可以应用于匹配的所有内容。

或者事实上,因为$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")
                        ]
                }
        ]
}

&#34;查询&#34;条件全部使用$elemMatch进行文档选择。这实际上是positional $ operator所需要的,以便获得&#34;位置索引&#34;用于&#34;第一场比赛&#34;。虽然这实际上不是&#34;要求&#34;对于positional filtered $[<identifier>]positional all $[]运算符,它仍然有用,因此您甚至不会考虑更新的文档,这些文档与$pull$pull的更新条件不匹配arrayFilters选项。

至于$elemMatch本身,这里的条件实际上适用于&#34;每个&#34;数组元素,所以在该操作中不需要positional all $[],因为我们已经在查看&#34;元素&#34;水平。

第三个示例显示$pull运算符可以简单地使用positional filtered $[<identifier>]条件来考虑每个&#34;内部&#34;数组元素,只适用于所有&#34;外部&#34;数组元素。所以$elemMatch表达式的实际点是&#34;只有&#34;处理那些&#34;外部&#34;数组元素实际匹配&#34;内部&#34;条件。因此,为什么我们在考虑中使用How to Update Multiple Array Elements in mongodb来匹配每个&#34;内部&#34;数组元素。

如果你实际上至少没有MongoDB 3.6,那么你正在使用第一个表格并且可能会重复这个表格,直到更新最终没有返回任何更多的修改文件,表明没有剩余的元素符合条件。

关于&#34;替代品&#34;有更详细的文章。作为{{3}}处的方法,但只要您的数据适合初始情况或您实际上有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")
                    ]
            }
    ]
}

这基本上复制了一些条目&#34;外部&#34;和&#34;内部&#34;显示语句如何删除所有null值。

  

注意 arrayFilters在&#34;选项&#34;中指定对于.update()和类似方法的参数,语法通常与所有最新的发行版驱动程序版本兼容,甚至与MongoDB 3.6发布之前的版本兼容。

     

然而,mongo shell不是这样,因为在那里实现方法的方式(&#34;具有讽刺意味的是为了向后兼容&#34;)arrayFilters参数无法识别和删除通过内部方法解析选项以实现向后兼容性&#34;使用先前的MongoDB服务器版本和&#34;遗产&#34; .update() API调用语法。

     

因此,如果您想在mongo shell或其他基于shell的&#34;中使用该命令。产品(特别是Robo 3T)您需要3.6或更高版本的开发分支或生产版本的最新版本。

     

Robo 3T显然仍然基于MongoDB 3.4 shell。因此,即使连接到功能强大的MongoDB 3.6实例,这些选项也不会从此程序传递到服务器。建议仅保留shell和支持的产品,但有一些其他产品没有相同的限制。