我正在尝试更新嵌套数组中的值,但无法使其正常工作。
我的对象是这样的
{ "_id": { "$oid": "1" }, "array1": [ { "_id": "12", "array2": [ { "_id": "123", "answeredBy": [], }, { "_id": "124", "answeredBy": [], } ], } ] }
我需要将值推送到“ answeredBy”数组。
在下面的示例中,我尝试将“成功”字符串推到“ 123 _id”对象的“ answeredBy”数组中,但是它不起作用。
callback = function(err,value){ if(err){ res.send(err); }else{ res.send(value); } }; conditions = { "_id": 1, "array1._id": 12, "array2._id": 123 }; updates = { $push: { "array2.$.answeredBy": "success" } }; options = { upsert: true }; Model.update(conditions, updates, options, callback);
我找到了此链接,但其答案仅表示我应该使用类似结构的对象而不是数组。这不适用于我的情况。我真的需要将我的对象嵌套在数组中
如果您能在这里帮助我,那就太好了。我已经花了几个小时来解决这个问题。
先感谢您!
您在这里所做的事情有些错误。首先,您的查询条件。您指的是几个_id不需要的值,其中至少一个不在顶层。
_id
为了获得“嵌套的”值并假定该_id值是唯一的并且不会出现在任何其他文档中,您查询的表单应如下所示:
Model.update( { "array1.array2._id": "123" }, { "$push": { "array1.0.array2.$.answeredBy": "success" } }, function(err,numAffected) { // something with the result in here } );
现在可以实际使用,但实际上只是a幸,因为有很好的理由说明它不适合您。
重要的阅读内容在位置$嵌套运算符的官方文档中“嵌套数组”的主题下。这是什么意思:
$
位置$运算符不能用于遍历一个以上数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$占位符的替换是单个值
具体来说,这意味着将被匹配并在位置占位符中返回的元素是来自第 一个 匹配数组的索引值。在您的情况下,这意味着“顶级”数组上的匹配索引。
因此,如果您查看所示的查询表示法,我们已经“硬编码”了顶层数组中的 第一个 (或0索引)位置,并且碰巧“ array2”中的匹配元素也是零索引条目。
为了说明这一点,您可以将匹配_id值更改为“ 124”,结果将$push在元素_id“ 123” 上添加一个新条目,因为它们都在“ array1”的零索引条目中,并且这是返回给占位符的值。
$push
这就是嵌套数组的普遍问题。您可以删除其中一个级别,仍然可以$push在“顶部”数组中使用正确的元素,但是仍然会有多个级别。
尝试避免嵌套数组,因为您将遇到如图所示的更新问题。
通常的情况是“平化”您“认为”的事物是“级别”,并实际上将这些归因于最终细节项目。例如,问题中结构的“扁平化”形式应类似于:
{ "answers": [ { "by": "success", "type2": "123", "type1": "12" } ] }
甚至仅在接受内部数组时$push,并且永不更新时:
{ "array": [ { "type1": "12", "type2": "123", "answeredBy": ["success"] }, { "type1": "12", "type2": "124", "answeredBy": [] } ] }
两者都适合在位置运算符范围内进行原子更新$
从MongoDB3.6开始,有一些新功能可用于嵌套数组。这使用位置过滤的$[<identifier>]语法以匹配特定元素并arrayFilters在update语句中应用不同的条件:
$[<identifier>]
arrayFilters
Model.update( { "_id": 1, "array1": { "$elemMatch": { "_id": "12","array2._id": "123" } } }, { "$push": { "array1.$[outer].array2.$[inner].answeredBy": "success" } }, { "arrayFilters": [{ "outer._id": "12" },{ "inner._id": "123" }] } )
该"arrayFilters"传递给了选项.update(),甚至 .updateOne(),.updateMany(),.findOneAndUpdate()或.bulkWrite()方法指定的条件匹配的更新语句中给出的标识符。符合给定条件的任何元素都将被更新。
"arrayFilters"
.update()
.updateOne()
.updateMany()
.findOneAndUpdate()
.bulkWrite()
因为结构是“嵌套的”,所以我们实际上使用“多个过滤器”,如通过过滤器定义的“数组”指定的那样,如图所示。标记的“标识符”用于与语句的更新块中实际使用的位置过滤$[<identifier>]语法进行匹配。在这种情况下inner,outer是嵌套链所指定的每个条件的标识符。
inner
outer
这个新的扩展使嵌套数组内容的更新成为可能,但是它对“查询”此类数据的实用性并没有真正的帮助,因此如前所述适用相同的警告。
即使您的大脑最初认为是“嵌套”,您也通常会真正地“表示”为“属性”,这通常只是对您认为“先前的关系部分”融合在一起的一种反应。实际上,您确实需要更多的非规范化。
另请参阅如何在mongodb中更新多个数组元素,因为这些新的更新操作符实际上匹配并更新“多个数组元素”,而不仅仅是第 一个 (位置更新的先前动作)。
注意 有些讽刺意味的是,由于这是在.update()和类似方法的“ options”参数中指定的,因此该语法通常与所有最新发行版驱动程序兼容。 但是,对于mongoShell而言,情况并非如此,因为在那里实现该方法的方式(“具有讽刺意味的是向后兼容”),arrayFilters无法通过内部方法来解析和删除参数,该内部方法会解析这些选项,以便与先前版本实现“向后兼容性” MongoDB服务器版本和“旧版” .update()API调用语法。 因此,如果要在mongoShell或其他“基于Shell的”产品(尤其是Robo 3T)中使用该命令,则需要从开发分支或生产版本开始的3.6或更高版本。
注意 有些讽刺意味的是,由于这是在.update()和类似方法的“ options”参数中指定的,因此该语法通常与所有最新发行版驱动程序兼容。
但是,对于mongoShell而言,情况并非如此,因为在那里实现该方法的方式(“具有讽刺意味的是向后兼容”),arrayFilters无法通过内部方法来解析和删除参数,该内部方法会解析这些选项,以便与先前版本实现“向后兼容性” MongoDB服务器版本和“旧版” .update()API调用语法。
mongo
因此,如果要在mongoShell或其他“基于Shell的”产品(尤其是Robo 3T)中使用该命令,则需要从开发分支或生产版本开始的3.6或更高版本。
另请参见positional all$[]哪些还会更新“多个数组元素”,但不应用于指定条件,而是应用于数组中 所有 需要执行操作的元素。
positional all$[]