简体   繁体   中英

Mongoose conditional update with array of objects

I have a Cart schema in Mongoose including CartItems as an array of objects.

{
  _id: string;
  items: [
    {
      product: string;
      options: {
        size: string;
        color: string;
      }
      quantity: number;
    }
  ]
}

In this case, is it possible to push an item when product and options doesn't match or add quantity when it does?

Here is the code that I have tried which does not work.

Cart.findByIdAndUpdate(_id, {
    items: {
      $cond: [
        {
          // Does not work
          $elemMatch: {
            product,
            "options.color": options.color,
            "options.size": options.size,
          },
        },
        {
          // Should add quantity where it is matched.
        },
        {
          $push: {
            product,
            productName,
            options,
            quantity,
          },
        },
      ],
    },
  });

Query

  • pipeline update requires MongoDB >= 4.2
  • newProduct is the product you want to add (a js variable)
  • check if product already exists => add not-exist? field
  • if not-exists add it in the end
  • else map to find it and update the quantity
  • unset the 2 fields newProduct and not-exists

*it does 2 array reads, alternative could be to use $reduce but if you have many products $concatArrays is slow to be inside a reduce, so this is faster solutions (even if reduce would read the array 1 time only)

*you need a method to do update with pipeline, i don't know if mongoose is updated to support it, we are MongoDB 5 so i guess it will be(java is), in worst case you can useupdateCommand and call it with runCommand(...)

Test code here

update({"_id" : "1"},
[{"$set":
  {"newProduct":
   {"product":"p1",
    "options":{"size":"s1", "color":"c1"},
    "quantity":1}}},
 {"$set":
  {"not-exists?":
   {"$eq":
    [{"$filter":
      {"input":"$items",
       "cond":
       {"$and":
        [{"$eq":["$$this.product", "$newProduct.product"]},
         {"$eq":["$$this.options.size", "$newProduct.options.size"]},
         {"$eq":
          ["$$this.options.color", "$newProduct.options.color"]}]}}},
     []]}}},
 {"$set":
  {"items":
   {"$cond":
    ["$not-exists?", {"$concatArrays":["$items", ["$newProduct"]]},
     {"$map":
      {"input":"$items",
       "in":
       {"$cond":
        [{"$and":
          [{"$eq":["$$this.product", "$newProduct.product"]},
           {"$eq":
            ["$$this.options.size", "$newProduct.options.size"]},
           {"$eq":
            ["$$this.options.color", "$newProduct.options.color"]}]},
         {"$mergeObjects":
          ["$$this", {"quantity":{"$add":["$$this.quantity", 1]}}]},
         "$$this"]}}}]}}},
 {"$unset":["not-exists?", "newProduct"]}])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM