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