使用嵌套嵌套数组中的值进行MongoDB更新(聚合管道)

时间:2020-07-14 15:57:27

标签: node.js arrays mongodb aggregation-framework

我正在尝试基于嵌套的嵌套数组中的值更新文档中的字段(totalPrice)(addOns>从x个数组中获取匹配数组>在匹配数组中获取int [总是在pos 2])。

这是一个在addOns中嵌套2个数组的文档示例:

{
   "_id": ObjectID('.....'),
   "addOns": [
                ["appleId", "Apples", 2],
                ["bananaID", "Bananas", 1]
             ],
   "totalPrice": 5.7
}

比方说,香蕉的价格上涨了20美分,因此我需要查找其附件中包含香蕉的所有文档,然后根据所购买的香蕉的数量来增加这些文档的totalPrice。我计划使用一个聚合管道使用一个updateMany()查询,该管道应该大致类似于以下示例。 ???应该是购买的香蕉数量,但是我不确定如何去获取该价值。到目前为止,我已经考虑过使用$ unwind,$ filter和$ arrayElemAt,但是不确定如何将它们一起使用。不胜感激!

db.collection('orders').updateMany(
     { $elemMatch: { $elemMatch: { $in: ['bananaID'] } } },
     [
          { $set: {'totalPrice': { $add: ['$totalPrice', { $multiply: [{$toDecimal: '0.2'}, ???] } ] } } }
     ]

我不确定我的mongo版本是什么,但我确实知道我可以使用聚合管道,因为我还有其他的updateMany()调用也使用该管道而没有任何问题,因此应该是(version> = 4.2)。

** 编辑:考虑到???,过滤步骤似乎在文档更新时有些起作用,但该值为null而不是更新的价格。不知道为什么

  1. 过滤'$ addOns'数组以返回匹配的嵌套数组。
  2. 对于条件,请使用$ eq检查[0]('$$ this.0')处的元素是否与字符串'bananaID'相匹配,以及是否确实返回此嵌套数组。
  3. 使用$ arrayElemAt和位置[0]获取从步骤1返回的数组。
  4. 在第3步的数组上再次使用$ arrayElemAt,其位置为positon [2],因为它是数量元素的索引
{ $arrayElemAt: [{ $arrayElemAt: [ { $filter: { input: "$addOns", as:'this', cond: { $eq: ['$$this.0', 'bananaID'] } } } , 0 ] }, 2 ] }

1 个答案:

答案 0 :(得分:0)

设法自己解决-尽管仅在不介意Joe在问题评论中指出的updateMany()风险的情况下才使用它。它与我最初在** Edit 中共享的内容非常相似,不同之处在于您无法使用$$this.0访问数组中的元素。

将此内容插入???是并且它将起作用,在此代码块下方是解释:

{ $arrayElemAt: [{ $arrayElemAt: [ { $filter: { input: "$addOns", as:'this', cond: { $eq: [{$arrayElemAt:['$$this', 0]}, 'bananaID'] } } } , 0 ] }, 2 ] }
  1. 我们的数组如下所示:[["appleId", "Apples", 2], ["bananaID", "Bananas", 1]]
  2. 使用$ filter返回数组的一个子集,该子集仅包含符合条件的元素-在这种情况下,我们希望该数组用于香蕉。 $$this代表输入中的每个元素,我们检查$$this的第一个元素是否与'bananaID'相匹配。
{ $filter: { input: "$addOns", as:'this', cond: { $eq: [{$arrayElemAt:['$$this', 0]}, 'bananaID'] } } }

// how the result from $filter should look like
// [["bananaID", "Bananas", 1]]
  1. 因为嵌套的香蕉数组将始终是第一个元素,所以对步骤2的结果使用$ arrayElemAt来检索位置0处的元素。
// How it looks like after step 3: ["bananaID", "Bananas", 1]
  1. 最后一步是再次使用$ arrayElemAt,除了这次我们要在位置2(购买的香蕉数量)上检索元素
  2. 这是评估步骤1-4之后最终的updateMany()查询的样子。
db.collection('orders').updateMany(
     { $elemMatch: { $elemMatch: { $in: ['bananaID'] } } },
     [
          { $set: {'totalPrice': { $add: ['$totalPrice', { $multiply: [{$toDecimal: '0.2'}, 1] } ] } } }
     ], *callback function code*)

// notice how the ??? is now replaced by the quantity of bananas which is 1