如何使用MongoDB从嵌套在另一个数组中的数组中删除元素

时间:2015-11-13 20:19:37

标签: arrays mongodb

问题

假设我有一份文件如下:

doc = {
"_id" : ObjectId("56464c726879571b1dcbac79"),
"food" : {
    "fruit" : [
        "apple",
        "orange"
    ]
},
"items" : [
    {
        "item_id" : 750,
        "locations" : [
            {
                "store#" : 13,
                "num_employees" : 138
            },
            {
                "store#" : 49,
                "num_employees" : 343
            }
        ]
    },
    {
        "item_id" : 650,
        "locations" : [
            {
                "store#" : 12,
                "num_employees" : 22
            },
            {
                "store#" : 15,
                "num_employees" : 52
            }
        ]
    }
]
}


我想删除元素

    {'#store#' : 12, 'num_employees' : 22} 

仅当满足以下条件时:

  • food.fruit包含值appleorange
  • item_id的ID为650



我尝试的解决方案

我尝试了以下内容:

     db.test.update({"food.fruit" : {"$in" : ["apple", "orange"]}, "items.item_id":650},{$pull:{'items.$.locations':{'store#':12,'num_employees':22}}})


更新不起作用。有趣的是,如果查询的$ in运算符部分被删除,它就可以工作。我正在使用MongoDB v3.0.6并查阅MongoDB手册以使用 $(更新)

https://docs.mongodb.org/manual/reference/operator/update/positional/

文档包含一段感兴趣的内容:

  Nested Arrays
  The positional $ operator cannot be used for queries which traverse more than one array, such as queries that traverse arrays nested within other arrays, because the replacement for the $ placeholder is a single value

我的查询当然遍历多个数组。实际上,如果我从查询中删除'food.fruit' : {$in : ['apple']},它就会起作用。但是,这样做 不解决我的问题,因为我当然需要那个查询。我正在寻找一个最好的解决方案:

  • 不需要架构更改
  • 执行一个查询/更新语句

2 个答案:

答案 0 :(得分:1)

如果您需要匹配"food.fruit"中的多个可能值以及其他多个可能的文档(唯一有意义的情况),那么您始终可以使用JavScript逻辑替换$in$where中:

db.test.update(
    {
        "items.item_id": 650,
        "$where": function() {
            return this.food.fruit.some(function(el) {
                return ["apple","orange"].indexOf(el) != -1;
            });
        }
    },
    { "$pull": { "items.$.locations": { "store#": 12 } } },
    { "multi": true }
)

基本上应用相同的测试,虽然效率不如"food.fruit"值无法在索引中测试,但希望"items.item_id"至少是不足以使这成为一个真正的问题。

另一方面,针对MongoDB服务器版本3.1.9(开发系列)进行测试,以下工作没有问题:

db.test.update(
    { "food.fruit": { "$in": ["orange","apple"] }, "items.item_id": 650 },
    { "$pull": { "items.$.locations": { "store#": 12 } } },
    { "multi": true }
)

我还建议,如果您打算在查询中包含_id,那么无论如何您只匹配单个文档,因此您只需要在您想要的数组上提供匹配{{ 3}}来自:

db.test.update(
    { "_id": 123, "items.item_id": 650 },
    { "$pull": { "items.$.locations": { "store#": 12 } } }
)

这非常简单并且没有冲突,除非您确实需要以确保实际存在所需的"food.fruits"值才能应用更新。在这种情况下,请遵循前面的例子。

答案 1 :(得分:0)

马特,

我使用了您的示例数据,以下查询有效:

db.pTest.update(
    {"food.fruit" : "apple","items.item_id":650},
    {$pull:{'items.$.locations':{'store#':12,'num_employees':22}}}
)

不需要$in(除非您为数组输入多个值),也不需要$elemMatch。此外,对于两级深度数组,您可以使用{$pull: {"someArray.$.someNestedArray": {"key": "value to delete"}}}

您从文档中引用的内容。

  

嵌套数组     位置$运算符不能用于遍历多个数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$ placeholder的替换是单个值。

转换为,意味着您无法使用$ Positional运算符两次。

示例数据:

{
    "_id" : ObjectId("56464c726879571b1dcbac79"),
    "food" : {
        "fruit" : [
            "apple",
            "orange"
        ]
    },
    "items" : [
        {
            "item_id" : 650,
            "locations" : [
                {
                    "store#" : 12,
                    "num_employees" : 22
                },
                {
                    "store#" : 15,
                    "num_employees" : 52
                }
            ]
        }
    ]
}

结果:

{
    "_id" : ObjectId("56464c726879571b1dcbac79"),
    "food" : {
        "fruit" : [
            "apple",
            "orange"
        ]
    },
    "items" : [
        {
            "item_id" : 650,
            "locations" : [
                {
                    "store#" : 15,
                    "num_employees" : 52
                }
            ]
        }
    ]
}