如何从 MongoDB 文档中的双重嵌套数组中删除元素。

Posted

技术标签:

【中文标题】如何从 MongoDB 文档中的双重嵌套数组中删除元素。【英文标题】:How to remove an element from a doubly-nested array in a MongoDB document. 【发布时间】:2021-02-26 20:26:40 【问题描述】:

我的文档结构类似于以下内容:


"_id" : "777",
"someKey" : "someValue",
"someArray" : [
    
        "name" : "name1",
        "someNestedArray" : [
            
                "name" : "value"
            ,
            
                "name" : "delete me"
            
        ]
    
  ]

我想删除值为“delete me”的嵌套数组元素。

我知道我可以使用嵌套的 $elemMatch 表达式找到与此描述匹配的文档。删除相关元素的查询语法是什么?

【问题讨论】:

【参考方案1】:

要删除有问题的项目,您实际上将使用更新。更具体地说,您将使用 $pull 命令进行更新,该命令将从数组中删除该项目。

db.temp.update(
   _id : "777" ,
  $pull : "someArray.0.someNestedArray" : "name":"delete me"
)

这里发生了一点“魔法”。使用.0表示我们知道我们正在修改someArray的第0项。使用"name":"delete me" 表示我们知道我们计划删除的确切数据。

如果您将数据加载到客户端然后执行更新,此过程就可以正常工作。如果您想要执行执行这些操作的“通用”查询,则此过程效果较差。

我认为最简单的方法是简单地认识到更新子文档数组通常需要您在某个时候在内存中拥有原始文档。


针对下面的第一条评论,您可能可以通过稍微改变数据结构来帮助您的情况

"someObjects" : 
  "name1":  
        "someNestedArray" : [
            
                "name" : "value"
            ,
            
                "name" : "delete me"
            
        ]
    

现在你可以$pull : "someObjects.name1.someNestedArray" : ...

这是您的结构的问题。 MongoDB 对操作“子数组”没有很好的支持。您的结构有一个对象数组,而这些对象包含更多对象的数组。

如果你有以下结构,你将很难使用像$pull这样的东西:

array [
   subarray : array [] ,
   subarray : array [] ,
]

如果你的结构看起来像那样并且你想更新subarray你有两个选择:

    更改您的结构,以便您可以利用 $pull。 不要使用$pull。将整个对象加载到客户端并使用findAndModify

【讨论】:

如果我们不知道 someArray 中项目的索引怎么办?我应该在我最初的问题中指定这个条件,我很抱歉。 您指定 someObjects.name1 的方式似乎不起作用? 我正在努力解决这个问题。 MongoDB 不支持 2nd level deep $ [positional operator] 进行查找和更新。 @UdayHiwarale 对于两级深度,您可以使用_id: 777, someArray.name: "name1", $pull: "someArray.$.someNestedArray": "name": "delete me"。但是如果你有更深的嵌套数组,它就不起作用了,因为 MongoDB 只支持一个$。官方开发者表示,2.9 版可能会提供多 $ 支持。【参考方案2】:

MongoDB 3.6 添加了$[] 运算符,有助于更新包含嵌入文档的数组。所以问题可以通过以下方式解决:

db.test.update(
   _id : "777" ,
  $pull : "someArray.$[].someNestedArray" : "name":"delete me"
)

【讨论】:

【参考方案3】:

正如@Melkor 所评论的那样(可能本身就是一个答案),

如果你不知道索引使用:

    _id: TheMainID, "theArray._id": TheArrayID, $pull: 
    "theArray.$.theNestedArray": _id: theNestedArrayID

【讨论】:

【参考方案4】:

从 MongoDB 3.6 开始,您可以使用 arrayFilters 来执行此操作:

db.test.update(
   _id: "777" , 
   $pull:  "someArray.$[elem].someNestedArray":  name: "delete me"   ,
   arrayFilters: [ "elem.name": "name1"] 
)

另见https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/index.html#update-all-documents-that-match-arrayfilters-in-an-array

【讨论】:

【参考方案5】:

其他例子和用法可能是这样的:



    "company": 
        "location": 
            "postalCode": "12345",
            "Address": "Address1",
            "city": "Frankfurt",
            "state": "Hessen",
            "country": "Germany"
        ,
        "establishmentDate": "2019-04-29T14:12:37.206Z",
        "companyId": "1",
        "ceo": "XYZ"
    ,
    "items": [
            "name": "itemA",
            "unit": "kg",
            "price": "10"
        ,
        
            "name": "itemB",
            "unit": "ltr",
            "price": "20"
        

    ]

    DELETE : Mongodb 查询删除 ItemB:
db.getCollection('test').update(   
    "company.companyId":"1","company.location.city":"Frankfurt",
    $pull : "items" : "name":"itemB"
)
    FIND:查找 itemB 的查询:
db.getCollection('test').find(
    "company.companyId":"1","company.location.city":"Frankfurt","items.name":"itemB",
     "items.$": 1 
)

3.UPDATE : 更新 itemB 的查询:

db.getCollection('test').update
(
 "company.companyId":"1","company.location.city":"Frankfurt","items.name":"itemB",
  $set:  "items.$[].price" : 90 , 
    multi: true );

【讨论】:

如果可能的话,你应该使用尽可能少的查询......但这是最安全的方法之一。

以上是关于如何从 MongoDB 文档中的双重嵌套数组中删除元素。的主要内容,如果未能解决你的问题,请参考以下文章

从嵌套数组mongodb中删除元素

从嵌套数组mongodb中删除元素

如何更新(删除)嵌套数组中的文档

如何在 MONGODB 的嵌套文档数组中提取文档

MongoDB,从数组中的对象中删除嵌套项

mongoose:使用$ pull删除嵌入式文档数组中的值(MongoDB 3.4版本)