猫鼬:在嵌套数组中查找和更新 [英] Mongoose: Find and Update in nested array

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

问题描述

所以,我有这个模式

购买模式:

const purchasesSchema = new Schema({
  date: String,
  status: String,
  product: { type: Schema.Types.ObjectId, ref: 'Product' }
})

产品架构:

const productSchema = new Schema({
  product_name: String,
  transaction: [{
    price: Number,
    purchase: { type: Schema.Types.ObjectId, ref: 'Purchases' }
  }]

该算法在进行购买之前必须先创建产品名称,然后在购买中通过bond transaction.purchase创建产品交易.

The algorithm, before making a purchase must create a product name, then create product transaction in purchase with bond transaction.purchase.

购买文件样本:

[{
  "_id": "5ac0b7cab7924a1710398c9e",
  "date": "01/04/2018",
  "status": "Not Paid",
  "product": {
    "_id": "5ac0b7b1b7924a1710398c9a",
    "product_name": "Milk",
    "transactions": [
      {
      "_id": "5ac0b7c9b7924a1710398c9b",
      "price": 5000,
      }
    ],
  }
}]

在交易中具有购买ID绑定的预期文档

Expected document with purchase id binding in transactions

[{
  "_id": "5ac0b7cab7924a1710398c9e",
  "date": "01/04/2018",
  "status": "Not Paid",
  "product": {
    "_id": "5ac0b7b1b7924a1710398c9a",
    "product_name": "Milk",
    "transactions": [
      {
      "_id": "5ac0b7c9b7924a1710398c9b",
      "price": 5000,
      "purchase": "the id"
      }
    ],
  }
}]

到目前为止,我已经尝试过了,以防万一我想将交易推送到已经是产品名称的地方:

So far i had tried, in case i want to push transaction to already product name:

    const newTransaction = {
        product_price: req.body.product_price
      }
      Product.findOneAndUpdate({ _id: req.body.product_id }, { $push: { transaction: newTransaction } }, { new: true }, (err, data) => {
        const TRANSACTION_ID = data.transaction[data.transaction.length -1]._id

        const newPurchase = new Purchase({
          date: moment(req.body.date).format('DD/MM/YYYY'),
          status: 'Not Paid',
          product: req.body.product_id,
        })
        newPurchase.save((err, data) => {
          if (err) throw err
          const PURCHASES_ID = data._id
          Product.findOneAndUpdate({ 'transactions._id': TRANSCATION_ID }, { $set: { transactions: { purchase: PURCHASES_ID } } }, (err, data) => { if (err) return handleError(err) })

如何在嵌套数组产品架构中将购买ID"作为"transactions.purchase"推送.谢谢.

The problem how to push Purchase id as 'transactions.purchase' in the nested array product schema. Thank you.

推荐答案

从此答案中解决 https://stackoverflow.com/a /23577266/5834822

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

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.

为了获得一个嵌套"值,并且还假定_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
    }
);

现在这实际上可以工作,但实际上它只是a幸,因为有充分的理由说明它不适合您.

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.

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

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

具体来说,这意味着将被匹配并在位置占位符中返回的元素是 first 匹配数组中的索引值.在您的情况下,这意味着在顶级"数组上的匹配索引.

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 index)位置,并且恰好发生了其中的匹配元素"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在具有_id"123"的元素上添加一个新条目,因为它们都在"的零索引条目中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.

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

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起,有一些新功能可用于嵌套数组.这按顺序使用位置过滤的$[<identifier>] 语法以匹配特定元素并通过update语句中的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"传递给 甚至 .updateOne()

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中更新多个数组元素,因为这些新的更新操作符实际上会匹配并更新多个数组"元素",而不仅仅是第一,这是位置更新的先前动作.

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()和类似方法的"options"参数中指定的,因此该语法通常与所有最新发行版驱动程序兼容.

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 $[] ,它也会更新多个数组元素",但不适用于指定条件,并且适用于需要执行操作的数组中的所有元素.

这篇关于猫鼬:在嵌套数组中查找和更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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