Mongodb 更新管道更新嵌套数组内元素的字段

Aru*_*ath 4 mongodb aggregation-framework mongodb-nodejs-driver

我有一个包含此类对象的集合。

{
  materials: {
    "m1": {
      inventory: [
        {
          price: 100,
          amount: 65
        }
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,库存是层次结构深处的一个数组。我想更新amount它的字段。

我从客户端收到物料 ID ( "m1") 和库存索引 ( 0)。

我必须使用更新管道,因为我正在设置和取消设置本文档中的一些其他字段。

这是我尝试过的:

await products.findOneAndUpdate(filters, [
  {
    $set: {
      "materials.m1.inventory.0.amount": 100,
    },
  },
]);
Run Code Online (Sandbox Code Playgroud)

但它在第 0 个元素内创建一个名为 0 的新字段,并设置amount该对象内部。所以生成的文档看起来像这样。

{
  materials: {
    "m1": {
      inventory: [
        {
          0: {
            amount: 100
          }
          price: 100,
          amount: 65
        }
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

而我想要的是这样的:

{
  materials: {
    "m1": {
      inventory: [
        {
          price: 100,
          amount: 100
        }
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我确定要更新数组中哪个元素的唯一方法是它的索引。

我正在使用nodejs mongodb 驱动程序。如何编写$set这个更新管道的阶段?

tur*_*hal 5

我不认为有任何其他直接方法可以使用聚合管道更新特定索引位置元素,

  • $range生成从 0 到materials.m1.inventory数组长度的数组
  • $map迭代上述范围数组的循环
  • $cond检查当前元素是否为 0,然后转到更新部分,否则materials.m1.inventory按输入索引返回元素
  • $arrayElemAt从中获取特定元素materials.m1.inventory
  • $mergeObjects将当前对象与amount更新的属性合并
await products.findOneAndUpdate(filters, [
  {
    $set: {
      "materials.m1.inventory": {
        $map: {
          input: {
            $range: [0, { $size: "$materials.m1.inventory" }]
          },
          in: {
            $cond: [
              { $eq: ["$$this", 0] }, // 0 position
              {
                $mergeObjects: [
                  { $arrayElemAt: ["$materials.m1.inventory", "$$this"] },
                  { amount: 100 } // input amount
                ]
              },
              { $arrayElemAt: ["$materials.m1.inventory", "$$this"] }
            ]
          }
        }
      }
    }
  }
]);
Run Code Online (Sandbox Code Playgroud)

操场

  • 非常好的解决方案。`$mergeObjects` 在这里派上了用场。 (2认同)