如何从MongoDB文档的双重嵌套数组中移除一个元素

65

我有一个类似以下结构的文档:

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

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

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

5个回答

63

要删除问题中的项,实际上您将使用更新。更具体地说,您将使用$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,你有两个选择:

  1. 改变你的结构使得你可以使用$pull
  2. 不使用$pull。将整个对象加载到客户端中,并使用findAndModify

8
如果我们不知道someArray中某一项的索引怎么办?我应该在最初的问题中说明这个条件,抱歉。 - rshepherd
3
你指定的方式 someObjects.name1 似乎不起作用? - Lion789
2
我正在为这个问题苦苦挣扎。MongoDB不支持在查找和更新中使用第二级深度的$ [位置操作符]。 - Uday Hiwarale
19
对于两级深度,您可以使用{_id: 777, someArray.name: "name1"}, {$pull: {"someArray.$.someNestedArray": {"name": "delete me"}}}。但是如果您有更深层次的嵌套数组,则无法使用此方法,因为MongoDB仅支持一个$符号。根据官方开发者的说法,多个$符号的支持可能在2.9版本中可用。 - Melkor

37
MongoDB 3.6添加了$[]运算符,可简化更新包含嵌入式文档的数组的操作。因此,问题可以通过以下方式解决:
db.test.update(
  { _id : "777" },
  {$pull : {"someArray.$[].someNestedArray" : {"name":"delete me"}}}
)

14

正如@Melkor所评论的(可能应该作为一个单独的回答),

如果你不知道索引,请使用:

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

8

4

另一个例子和用法可能是这样的:

{

    "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"
        }

    ]
}
  1. 使用Mongodb查询删除ItemB的语句:
db.getCollection('test').update(   
    {"company.companyId":"1","company.location.city":"Frankfurt"},
    {$pull : {"items" : {"name":"itemB"}}}
)

寻找:寻找与itemB相关的查询。
db.getCollection('test').find(
    {"company.companyId":"1","company.location.city":"Frankfurt","items.name":"itemB"},
    { "items.$": 1 }
)

3.更新:针对itemB的更新查询:

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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接