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

4

问题

假设我有以下文档:

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手册,以了解$(update)的使用:

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']},它是可行的。然而,这并不能解决我的问题,因为我需要那个查询条件。我正在寻找一种解决方案,最好满足以下条件:

  • 不需要更改模式
  • 可以在一个查询/更新语句中执行

当我使用3.0.5尝试时,您正在尝试的更新可以正常工作。值得注意的是,在这里您不需要使用$in,因为'food.fruit': 'apple'可以完成相同的操作,但是对我来说,无论哪种方式更新都可以正常工作。 - JohnnyHK
@JohnnyHK,使用$in运算符可以解决问题吗?我需要编辑我的示例,因为我不仅要查找“apple”。示例:{$in : ['apple', 'orange']}。使用您的建议,我可以使用'food.fruit':'apple','food.fruit':'orange',这应该等同于使用$in - matt
糟糕,实际上这样行不通。我试图查看food.fruit是否有appleorange。只需要至少有其中一个。 - matt
使用$in确实可以正常工作,因为您所引用的文档中提到的限制是指尝试在嵌套数组中(即数组的第一层之外)使用$ - JohnnyHK
是的,当我写“这不起作用”时,我的意思是查询{'food.fruit':'apple','food.fruit':'orange'}{'$in':['apple','orange']}不等同。 - matt
2个回答

1
如果您需要匹配多个可能的值,因此需要匹配多个文档(唯一有意义的情况),则可以在$where中使用JavaScript逻辑替换$in。请注意不要解释,只需翻译,保留HTML标签。
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,那么你只需要匹配单个文档,因此你只需要在你想要$pull的数组上提供匹配即可。
db.test.update(
    { "_id": 123, "items.item_id": 650 },
    { "$pull": { "items.$.locations": { "store#": 12 } } }
)

这很简单,不会产生冲突,除非您确实需要确保所需的 "food.fruits" 值实际存在才能应用更新。在这种情况下,请遵循前面的示例。

谢谢Seven。我刚刚更新了我的问题。我现在才意识到,我遇到的问题无法通过我提供的信息进行复现。我现在会阅读你的答案。 - matt
@matt 由于你一直使用相同的模板回复,所以很明显你没有阅读人们实际回答的任何内容。你没有改变问题中任何细节,这对回答没有任何影响,也不应该有。答案是根据“提问的方式”提供的上下文而给出的。如果你有不同的问题,请提出另一个问题。这是按照提问的方式回答问题。 - Blakes Seven
在获得灵感的回答后,我阅读了他/她的回答,进行了更深入的调查,并意识到我需要编辑我的问题以包含更多结构来复现我的问题。那么在Stack Overflow上,我应该开始一个新的问题吗?还是只需编辑当前的问题即可?似乎编辑当前的问题是合理的。现在我将阅读你的回答。 - matt
@matt 问题是什么,它怎么可能是新的?你的数据结构要么就是你所呈现的样子,要么就不是。这个答案向你展示了如何从提供的结构中删除所需元素,因此它回答了那个问题。如果你的结构实际上不同或需要删除的内容不同,那么你就有一个新问题,不应该修改这个问题。 - Blakes Seven

0

Matt,

我使用了你提供的样本数据,以下查询有效:

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

这是您从文档中引用的内容。

嵌套数组 由于$占位符的替换是单个值,因此无法对遍历超过一个数组的查询(例如遍历其他数组中嵌套的数组的查询)使用位置$运算符。

这意味着您不能两次使用$ 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
                }
            ]
        }
    ]
}

谢谢您的回答。我已经更新了我的问题,现在应该有可重现的问题了。抱歉。由于无法在公共论坛上发布数据,我试图模仿我正在处理的实际文档的结构。 - matt

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