mongodbaggregation-frameworkmongodb-nodejs-driver

Mongodb update pipeline update a field of a element inside a nested array


I have a collection that contains objects such as this.

{
  materials: {
    "m1": {
      inventory: [
        {
          price: 100,
          amount: 65
        }
      ]
    }
  }
}

As you can see inventory is an array deep inside the hierarchy. I want to update the amount field of it.

From the client I receive the material id ("m1") and the inventory index (0).

I have to use an update pipeline because I am setting and unsetting some other fields in this document.

This is what I tried:

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

But it creates a new field named 0 inside the 0th element and sets the amount inside that object. So the resulting document looks like this.

{
  materials: {
    "m1": {
      inventory: [
        {
          0: {
            amount: 100
          }
          price: 100,
          amount: 65
        }
      ]
    }
  }
}

Whereas what I want is this:

{
  materials: {
    "m1": {
      inventory: [
        {
          price: 100,
          amount: 100
        }
      ]
    }
  }
}

The only way for me to identify which element in the array to update is the index of it.

I am using nodejs mongodb driver. How to write the $set stage for this update pipeline?


Solution

  • I don't think any other straightway to update the specific index position element using aggregation pipeline,

    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"] }
                ]
              }
            }
          }
        }
      }
    ]);
    

    Playground