一般范围和解释
你在这里做的事情有一些问题.首先是查询条件.你指的是一些你不需要的_id
个值,其中至少有一个不在顶层.
为了进入一个"嵌套"值,并假定_id
值是唯一的,不会出现在任何其他文档中,您的查询表单应该如下所示:
Model.update(
{ "array1.array2._id": "123" },
{ "$push": { "array1.0.array2.$.answeredBy": "success" } },
function(err,numAffected) {
// something with the result in here
}
);
现在,这确实会起作用,但实际上这只是一个侥幸,因为有很好的理由说明它不应该对你起作用.
重要的阅读内容在positional $
操作员的官方文档中,主题是"嵌套数组".这说明:
positional$运算符不能用于遍历多个数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$占位符的替换项是单个值
具体来说,这意味着将在位置占位符中匹配并返回的元素是first匹配数组中的索引值.这意味着在您的例子中,匹配索引位于"顶层"数组中.
因此,如果你看一下如图所示的查询符号,我们已经"硬编码"了顶层数组中的first(或0索引)位置,"array2"中的匹配元素也是零索引项.
为了演示这一点,您可以将匹配的_id
值更改为"124",结果将在元素中添加一个新条目,该条目为_id
"123",因为它们都位于"array1"的零索引条目中,并且这是返回给占位符的值.
这就是嵌套数组的一般问题.您可以删除其中一个级别,但仍然可以将$push
个级别添加到"top"数组中的正确元素,但仍然会有多个级别.
尽量避免嵌套数组,因为您将遇到更新问题,如图所示.
一般情况下,将你"认为"是"层次"的东西"展平",并在最后的细节项目上实际制作这些"属性".例如,问题中 struct 的"扁平"形式应该是:
{
"answers": [
{ "by": "success", "type2": "123", "type1": "12" }
]
}
或者即使接受的内部数组仅为$push
,且从未更新:
{
"array": [
{ "type1": "12", "type2": "123", "answeredBy": ["success"] },
{ "type1": "12", "type2": "124", "answeredBy": [] }
]
}
这两者都适用于positional $
operator%范围内的原子更新
MongoDB 3.6及以上版本
MongoDB 3.6中有一些新功能可用于嵌套array.这将使用positional filtered $[<identifier>]
语法来匹配特定元素,并在update语句中通过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()
个甚至更多的选项
由于 struct 是"嵌套的",我们实际上使用了"多个过滤器",如图所示的过滤器定义的"数组"所示.标记的"标识符"用于与语句更新块中实际使用的positional filtered $[<identifier>]
语法进行匹配.在这种情况下,inner
和outer
是用于嵌套链指定的每个条件的标识符.
这种新的扩展使嵌套数组内容的更新成为可能,但它实际上无助于"查询"此类数据的实用性,因此,前面解释的警告同样适用.
你通常真正的"意思"是用"属性"来表达,即使你的大脑最初认为"嵌套",它通常只是对你认为"之前的关系部分"是如何结合在一起的一种react .实际上,你真的需要更多的非规范化.
另请参见How to Update Multiple Array Elements in mongodb,因为这些新的更新操作符实际上匹配并更新"多个数组元素",而不仅仅是first,这是位置更新的前一个操作.
NOTE有点讽刺的是,由于这是在.update()
和类似方法的"options"参数中指定的,因此该语法通常与所有最新版本的驱动程序版本兼容.
然而,对于mongo
shell来说,情况并非如此,因为该方法在那里的实现方式("具有讽刺意味的是,对于向后兼容性"),arrayFilters
参数没有被一个内部方法识别和删除,该方法通过解析选项来提供与之前MongoDB服务器版本和"遗留".update()
API调用语法的"向后兼容性".
因此,如果您想在mongo
shell或其他"基于shell的"产品(尤其是Robo 3T)中使用该命令,您需要从3.6或更高版本的开发分支或生产版本获得最新版本.
另请参见positional all $[]
,它也会更新"多个数组元素",但不适用于指定的条件,并适用于数组中的all个元素,这是所需的操作.