我试图更新嵌套数组中的值,但无法使其工作.

我的目标是这样的

 {
    "_id": {
        "$oid": "1"
    },
    "array1": [
        {
            "_id": "12",
            "array2": [
                  {
                      "_id": "123",
                      "answeredBy": [],   // need to push "success" 
                  },
                  {
                      "_id": "124",
                      "answeredBy": [],
                  }
             ],
         }
     ]
 }

我需要将一个值推送到"answeredBy"array.

在下面的示例中,我try 将"success"字符串推送到"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);

我找到了这个link,但它的答案只是说我应该使用类似对象的 struct ,而不是array.这不能应用于我的情况.我真的需要我的对象嵌套在数组中

如果你能帮我,那就太好了.我花了好几个小时才弄明白.

提前谢谢!

推荐答案

一般范围和解释

你在这里做的事情有一些问题.首先是查询条件.你指的是一些你不需要的_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>]语法进行匹配.在这种情况下,innerouter是用于嵌套链指定的每个条件的标识符.

这种新的扩展使嵌套数组内容的更新成为可能,但它实际上无助于"查询"此类数据的实用性,因此,前面解释的警告同样适用.

你通常真正的"意思"是用"属性"来表达,即使你的大脑最初认为"嵌套",它通常只是对你认为"之前的关系部分"是如何结合在一起的一种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个元素,这是所需的操作.

Node.js相关问答推荐

如何使用NodeJS处理来自请求的单个或多个文件?

如何在.npmrc中添加 comments ?

如何在MEVN堆栈中结合创建和更新表单流程?

如何呈现ejs.renderFile中包含的子模板?

如何使用 Node.js 连接到 Cloud SQL?

当 Got 包因错误 JSON 崩溃时如何获取响应文本?

在 MacOS Ventura 上使用 NVM 安装 node ?

module.exports=require('other') 和临时变量有什么区别?

处理 UTC 日期和future

Mongodb 从文档中获取聚合结果中的特定属性

带有事件网格的 Azure 函数在没有 ngrok 的情况下在本地运行

获取数组的至少一个元素包含子字符串的文档

使用 .pipe(res) 向客户端发送音频不允许您搜索?

GridFS 大文件下载使 Node.js 服务器崩溃. MongoServerError:排序超出了字节的内存限制

Web3.js 脚本在监听 CreatedPairs 时退出

如何限制 cron 表单将消息推送到 RabbitMQ?

Puppeteer 错误:未下载 Chromium 修订版

构建 vue cli 后如何运行生产站点

为什么我会收到错误:解决方法指定过多?

NPM:为什么要安装这个包?