使用 MongoDB 更新嵌套数组 [英] Updating a Nested Array with MongoDB

查看:39
本文介绍了使用 MongoDB 更新嵌套数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to update a value in the nested array but can't get it to work.

我的对象是这样的

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

我需要向answeredBy"推送一个值数组.

I need to push a value to "answeredBy" array.

在下面的示例中,我尝试推送成功".字符串到answeredBy"123 _id"数组对象,但它不起作用.

In the below example, I tried pushing "success" string to the "answeredBy" array of the "123 _id" object but it does not work.

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);

我找到了这个链接,但它的回答只说我应该使用像结构而不是数组.这不适用于我的情况.我真的需要我的对象嵌套在数组中

I found this link, but its answer only says I should use object like structure instead of array's. This cannot be applied in my situation. I really need my object to be nested in arrays

如果你能在这里帮助我就好了.我已经花了几个小时来弄清楚这一点.

It would be great if you can help me out here. I've been spending hours to figure this out.

先谢谢你!

推荐答案

一般范围和说明

您在这里所做的事情有一些问题.首先是您的查询条件.您指的是几个不需要的 _id 值,其中至少有一个不在顶层.

General Scope and Explanation

There are a few things wrong with what you are doing here. Firstly your query conditions. You are referring to several _id values where you should not need to, and at least one of which is not on the top level.

为了进入嵌套"value 并假设 _id 值是唯一的并且不会出现在任何其他文档中,您的查询表单应该是这样的:

In order to get into a "nested" value and also presuming that _id value is unique and would not appear in any other document, you query form should be like this:

Model.update(
    { "array1.array2._id": "123" },
    { "$push": { "array1.0.array2.$.answeredBy": "success" } },
    function(err,numAffected) {
       // something with the result in here
    }
);

现在这确实可行,但实际上它只是侥幸,因为它有充分的理由不适合您.

Now that would actually work, but really it is only a fluke that it does as there are very good reasons why it should not work for you.

重要的阅读在 positional 的官方文档中嵌套数组"主题下的 $ 运算符.这是什么意思:

The important reading is in the official documentation for the positional $ operator under the subject of "Nested Arrays". What this says is:

位置$运算符不能用于遍历多个数组的查询,例如遍历嵌套在其他数组中的数组的查询,因为$占位符的替换是单个值

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

具体来说,这意味着将在位置占位符中匹配并返回的元素是第一个匹配数组的索引值.这意味着在您的情况下,顶部"上的匹配索引不可用.级别数组.

Specifically what that means is the element that will be matched and returned in the positional placeholder is the value of the index from the first matching array. This means in your case the matching index on the "top" level array.

因此,如果您查看如图所示的查询符号,我们已经硬编码"了顶级数组中的 first(或 0 索引)位置,而array2"中的匹配元素恰好位于也是零索引条目.

So if you look at the query notation as shown, we have "hardcoded" the first ( or 0 index ) position in the top level array, and it just so happens that the matching element within "array2" is also the zero index entry.

为了证明这一点,您可以将匹配的 _id 值更改为124";结果将$push一个新条目到带有_id123"的元素上.因为它们都在array1"的零索引条目中这就是返回给占位符的值.

To demonstrate this you can change the matching _id value to "124" and the result will $push an new entry onto the element with _id "123" as they are both in the zero index entry of "array1" and that is the value returned to the placeholder.

这就是嵌套数组的普遍问题.您可以删除其中一个级别,您仍然可以 $将 推送到顶部"中的正确元素数组,但仍然会有多个级别.

So that is the general problem with nesting arrays. You could remove one of the levels and you would still be able to $push to the correct element in your "top" array, but there would still be multiple levels.

尽量避免嵌套数组,因为您会遇到如图所示的更新问题.

Try to avoid nesting arrays as you will run into update problems as is shown.

一般情况是扁平化"你想"的事情是级别"并实际使论文成为属性"在最后的细节项目上.例如,扁平化"问题中的结构形式应类似于:

The general case is to "flatten" the things you "think" are "levels" and actually make theses "attributes" on the final detail items. For example, the "flattened" form of the structure in the question should be something like:

 {
   "answers": [
     { "by": "success", "type2": "123", "type1": "12" }
   ]
 }

或者甚至当接受内部数组是$push,从未更新:

Or even when accepting the inner array is $push only, and never updated:

 {
   "array": [
     { "type1": "12", "type2": "123", "answeredBy": ["success"] },
     { "type1": "12", "type2": "124", "answeredBy": [] }
   ]
 }

这两者都适用于 位置$运算符

Which both lend themselves to atomic updates within the scope of the positional $ operator

从 MongoDB 3.6 开始,有新功能可用于处理嵌套数组.这使用 位置过滤 $[] 语法以便在更新语句中通过 arrayFilters 匹配特定元素并应用不同的条件:

From MongoDB 3.6 there are new features available to work with nested arrays. This uses the positional filtered $[<identifier>] syntax in order to match the specific elements and apply different conditions through arrayFilters in the update statement:

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"="noreferrer">.update() 甚至.updateOne(),.updateMany(),.findOneAndUpdate().bulkWrite() 方法指定与更新语句中给定的标识符匹配的条件.任何符合给定条件的元素都将被更新.

The "arrayFilters" as passed to the options for .update() or even .updateOne(), .updateMany(), .findOneAndUpdate() or .bulkWrite() method specifies the conditions to match on the identifier given in the update statement. Any elements that match the condition given will be updated.

因为结构是嵌套"的,所以我们实际上使用了多个过滤器";如用数组"指定的那样过滤器定义如图所示.标记的标识符"用于匹配 位置过滤 $[;] 语句的更新块中实际使用的语法.在这种情况下,innerouter 是用于嵌套链指定的每个条件的标识符.

Because the structure is "nested", we actually use "multiple filters" as is specified with an "array" of filter definitions as shown. The marked "identifier" is used in matching against the positional filtered $[<identifier>] syntax actually used in the update block of the statement. In this case inner and outer are the identifiers used for each condition as specified with the nested chain.

这个新的扩展使得嵌套数组内容的更新成为可能,但是对于查询"的实用性并没有真正的帮助此类数据,因此同样的警告也适用于之前的解释.

This new expansion makes the update of nested array content possible, but it does not really help with the practicality of "querying" such data, so the same caveats apply as explained earlier.

你通常真的很刻薄"表达为属性",即使你的大脑最初认为嵌套",它通常只是对你如何相信先前的相关部分"的反应.一起来.实际上,您确实需要更多的非规范化.

You typically really "mean" to express as "attributes", even if your brain initially thinks "nesting", it's just usually a reaction to how you believe the "previous relational parts" come together. In reality you really need more denormalization.

另请参阅如何在 mongodb 中更新多个数组元素,因为这些新的更新操作符实际上匹配并更新了multiple"数组元素"而不仅仅是第一个,这是位置更新的前一个动作.

Also see How to Update Multiple Array Elements in mongodb, since these new update operators actually match and update "multiple array elements" rather than just the first, which has been the previous action of positional updates.

注意有点讽刺,因为这是在选项"中指定的..update() 和类似方法的参数,语法通常与所有最新发布的驱动程序版本兼容.

NOTE Somewhat ironically, since this is specified in the "options" argument for .update() and like methods, the syntax is generally compatible with all recent release driver versions.

然而,mongo shell 并非如此,因为在那里实现方法的方式(具有讽刺意味的是向后兼容")arrayFilters 参数不被识别并由解析选项的内部方法移除以提供向后兼容性";使用先前的 MongoDB 服务器版本和遗留".update() API 调用语法.

However this is not true of the mongo shell, since the way the method is implemented there ( "ironically for backward compatibility" ) the arrayFilters argument is not recognized and removed by an internal method that parses the options in order to deliver "backward compatibility" with prior MongoDB server versions and a "legacy" .update() API call syntax.

因此,如果您想在 mongo shell 或其他基于 shell"的环境中使用该命令,产品(特别是 Robo 3T),您需要来自开发分支或生产版本 3.6 或更高版本的最新版本.

So if you want to use the command in the mongo shell or other "shell based" products ( notably Robo 3T ) you need a latest version from either the development branch or production release as of 3.6 or greater.

另见positional all $[] 也更新多个数组元素"但不应用于指定的条件,而是应用于数组中的所有元素,其中这是所需的操作.

See also positional all $[] which also updates "multiple array elements" but without applying to specified conditions and applies to all elements in the array where that is the desired action.

这篇关于使用 MongoDB 更新嵌套数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆