【问题标题】:MongoDB Update (Aggregation Pipeline) using values from nested nested Arrays使用来自嵌套嵌套数组的值的 MongoDB 更新(聚合管道)
【发布时间】:2020-11-04 00:35:29
【问题描述】:

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

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

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

假设香蕉的价格上涨了 20 美分,所以我需要查找其 addOns 中包含香蕉的所有文档,然后根据购买的香蕉数量增加这些文档的 totalPrice。我计划使用一个聚合管道来使用 updateMany() 查询,它应该大致类似于下面的示例。这 ???应该是购买的香蕉数量,但我不确定如何检索该值。到目前为止,我一直在考虑使用 $unwind、$filter 和 $arrayElemAt,但不知道如何一起使用它们。非常感谢任何帮助!

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

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

**编辑:为 ??? 考虑了这一点,随着文档的更新,过滤步骤似乎有些工作,但值为 null 而不是更新后的价格。不知道为什么

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

【问题讨论】:

  • 我建议重新计算整个价格,而不是仅仅调整。如果在中途发生某些事情中断了您的 updateMany,那么重新同步所有文档将是一个很大的挑战。
  • 这是否意味着我必须检索所有文档,计算新价格,然后为每个文档调用 updateOne() 并使用相应的 ObjectId 和更新价格?
  • 我想这取决于价格的存储方式。
  • 是的,这样会很复杂,rip

标签: node.js arrays mongodb aggregation-framework


【解决方案1】:

设法自己解决了这个问题 - 尽管只有在您不介意问题 cmets 中 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-26
    • 1970-01-01
    • 2019-07-20
    • 2017-07-16
    • 1970-01-01
    • 2015-12-06
    • 2019-12-26
    相关资源
    最近更新 更多