Amani
Amani

Reputation: 247

Mongoose update subdocument array with subdocument value

I have a document like this one:

{
 _id:ObjectId('111'),
 products:[
  {
   _id:ObjectId('aaa'),
   quantity:2,
   price:800
  }
 ]
}

I want to update price field by multiplying it with quantity field ie (2 * 800) of which the result gets updated/assigned to price. (as for this example price gets updated to 1600).

Document after Update :

{
 _id:ObjectId('111'),
 products:[
  {
   _id:ObjectId('aaa'),
   quantity:2,
   price:1600  //the updated field by multiplying the initial 800 * 2
  }
 ]
}

My query for selecting is like this below:

    Shop.findOneAndUpdate(
        { "_id": '111, "products._id": 'aaa' }
    )

How can I achieve this?

Upvotes: 0

Views: 954

Answers (2)

SuleymanSah
SuleymanSah

Reputation: 17858

As @Casey suggested in the comments, you can do this in multi step, find the shop, find the product, change product price, save the shop.

router.patch("/shops/:shopId/:productId", async (req, res) => {
  const { shopId, productId } = req.params;

  let shop = await Shop.findById(shopId);

  if (!shop) return res.status(400).send("Shop not found");

  const productIndex = shop.products.findIndex((p) => p._id.toString() === productId);

  if (productIndex < 0) return res.status(400).send("Product not found in shop");

  let product = shop.products[productIndex];

  product.price *= product.quantity;

  shop = await shop.save();

  res.send(shop);
});

Let's say you have this existing shop with two products:

{
    "_id": "5eb85ab17c2bfb3e2cfc15d0",
    "products": [
        {
            "_id": "5eb85ab17c2bfb3e2cfc15d2",
            "quantity": 2,
            "price": 800
        },
        {
            "_id": "5eb85ab17c2bfb3e2cfc15d1",
            "quantity": 3,
            "price": 500
        }
    ]
}

If you want to update the price with the "_id": "5eb85ab17c2bfb3e2cfc15d2", we send a patch request to the url http://your base url/shops/5eb85ab17c2bfb3e2cfc15d0/5eb85ab17c2bfb3e2cfc15d2

The output will be like this:

{
    "_id": "5eb85ab17c2bfb3e2cfc15d0",
    "products": [
        {
            "_id": "5eb85ab17c2bfb3e2cfc15d2",
            "quantity": 2,
            "price": 1600  => UPDATED
        },
        {
            "_id": "5eb85ab17c2bfb3e2cfc15d1",
            "quantity": 3,
            "price": 500
        }
    ]
}

Upvotes: 3

whoami - fakeFaceTrueSoul
whoami - fakeFaceTrueSoul

Reputation: 17915

On MongoDB version >= 4.2 as you can execute aggregation-pipeline in updates, try below query :

Shop.update(
  /** Remember to convert input strings to type `ObjectId()` prior to querying */
  { _id: ObjectId("111"), "products._id": ObjectId("aaa") },
  /** This aggregation pipeline will re-create `products` array,
   *  if condition is met for an object then price will be multiplied & price field is merged to original object & pushed back to `products` array, 
   *  if condition is not met actual object is pushed back to array */
  [
    {
      $set: {
        products: {
          $map: {
            input: "$products",
            in: {
              $cond: [
                { $eq: ["$$this._id", ObjectId("aaa")] },
                {
                  $mergeObjects: [ "$$this", { price: { $multiply: ["$$this.quantity", "$$this.price"] }}]
                },
                "$$this"
              ]
            }
          }
        }
      }
    }
  ]
);

Upvotes: 1

Related Questions