Update Mongoose data conditionally using an array of objects

I am facing a challenge with my Cart schema in Mongoose. The CartItems are stored as an array of objects.

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

My question is whether it's possible to push an item into the cart when the product and options don't match, or add the quantity if they do?

Unfortunately, the code that I have tried does not seem to work properly. Here it is:

Cart.findByIdAndUpdate(_id, {
    items: {
      $cond: [
        {
          // This logic doesn't work
          $elemMatch: {
            product,
            "options.color": options.color,
            "options.size": options.size,
          },
        },
        {
          // I expected this part to increase the quantity if there's a match
        },
        {
          $push: {
            product,
            productName,
            options,
            quantity,
          },
        },
      ],
    },
  });

Answer №1

Unique Text

  • To update the pipeline, you need to have MongoDB version 4.2 or higher
  • newProduct refers to the product that you wish to add (a JavaScript variable)
  • Check if the product already exists, and if not, add a field called "not-exist?"
  • If it doesn't exist, add it to the end of the array
  • If it does exist, find it in the array and update its quantity
  • Remove the fields newProduct and not-exists

*This approach requires two array reads. An alternative method could be using the $reduce function, but if you have numerous products, using $concatArrays within reduce can lead to slower performance. Thus, this solution is faster despite performing two array reads.

*To perform an update with a pipeline, you may need to check if Mongoose supports it. We are currently using MongoDB 5, so Java likely supports it. In case it's not supported, use the updateCommand command and execute it with runCommand(...).

You can test the 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"]}])

Unique Text 2

  • If you prefer not to use the update pipeline, you can achieve the same result with multiple queries

Check if the item already exists:

db.collection.find({
  "_id" : "1",
  "items": {
    "$elemMatch": {
      "product": "p1",
      "options": {
        "size": "s1",
        "color": "c1"
      }
    }
  }
})

If the item does not exist:

db.collection.update({
  "_id": "1"
},
{
  "$push": {
    "items": "NEWITEM"   //Insert the new object here
  }
})

If the item exists:

db.collection.update({"_id" : "1"},
{
  "$inc": {
    "items.$[i].quantity": 1
  }
},
{
  arrayFilters: [
    {
      "i.product": "p1",
      "i.options.size": "s1",
      "i.options.color": "c1"
    }
  ]
})

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Understanding the scope of variables in a JavaScript callback function

I am attempting to send an object variable to a callback function const sql = require('mssql'); const asset_update = function (connection, addr) { this.connection = connection; this.addr = addr; this.addr_long = parseInt(addr, 16); } ...

Extract a JSON substring by referencing a specific object node key

I have a JSON string that contains nested objects. Here's a portion of the JSON string: "foldChildren": "false", "branchColor": "#000000", "children": [ ...

In the 4.13.1 version of Express, req.body is not defined

I'm facing a problem and seeking help to find the solution. Here's what I have in the Angular view: <form ng-submit="submit()"> <input ng-model="stickie_text" type="text" id="sticky_content" /> <button type="submit" id="add_stick ...

Convert JSON data into an array of strings that are related to each other

I'm encountering an issue with converting JSON into a string array I must convert a JSON into a string array to dynamically INSERT it into our database, as I need this to work for any type of JSON without prior knowledge of its structure. Here is th ...

Sending the user to their destination

There is a code in place that triggers when an email is sent to the user, containing a link that leads them to a specific endpoint upon opening. I am experiencing issues with redirection at the end of the function. The redirect does not seem to be working ...

Parsing DOM data from an HTTP request in Node.js

Looking to extract data attributes from an SVG element on github.com/profile_name using a node.js HTTP request, and then parsing it into JSON format. <rect class="day" width="10" height="10" x="-36" y="10" fill="#ebedf0" data-count="0" data-date="2017- ...

The dependencies listed in the JSON package are not in sync with each other

I find the behavior in NPM and the package.json file regarding package dependencies confusing. A prime example is the package gulp-watch. Looking at its package.json file on GitHub, we can see that it has a dependency on "anymatch": "^3.1.1& ...

Comparing Node WASI to the creation of a child process

I came across information in the NodeJS documentation that caught my attention: The WebAssembly System Interface (WASI) API is described as a way to provide sandboxed WebAssembly applications with access to the underlying operating system through a set o ...

Automatically log out after making any changes to a user's information using passport-local-mongoose

After attempting to use req.login, a method provided by Passport that is typically used during the signup process, I realized it was not working as expected. While searching for a solution, I came across this post on Stack Overflow: Passport-Local-Mongoose ...

Are Prisma schema objects secure for sending as requests to REST APIs?

I have developed a simple custom API using Next.js and I wanted to give more flexibility to the frontend in terms of calling the API. Essentially, I want the frontend to have control over what data is included or selected in the server response. With the u ...

Using JavaScript to launch a new window for a specific folder

When opening a popup window with a specific URL, I typically use the following code: $("#OpenFolder").click(function () { var url = "https://stackoverflow.com"; windowObjectReference = window.open(url, "ModulesList", " ...

npm encountered an error with code EPEERINVALID, indicating an issue with

Welcome, I'm a new member here and encountering an npm ERR! code EPEERINVALID. Currently, my setup includes: nvm 0.32.1 npm 2.15.9 node v4.5.0 grunt-cli v1.2.0 grunt v0.4.5 While attempting to upgrade a package or module, I came across the foll ...

Troubleshooting the Non-functioning req.value & ip with Node.js - Joi schema

I have implemented a joi schema to validate email and password that are sent as JSON in the request body. Now, I want to add an additional validation for the IP address. However, when I tried to do so, I encountered the following error: { "isJoi": tru ...

While local production and development prove effective, Heroku restricts calls to the Express server only

Update: Heroku is only recognizing my server file, struggling to incorporate the client as well. Attempting to deploy my first solo-built app on Heroku. While still a work in progress, the basic functionalities are operational. The current version works s ...

What emerging patterns can be observed in the security of client-side coding within the realm of web development

Keeping up with the constant flow of new frameworks and technologies can be overwhelming. One area that particularly interests me is client-side frameworks, such as AngularJS, Backbone, Knockout, jsviews, knockback, SPA... These are currently among the mos ...

Ways to Safeguard Your API Key with HERE Map on the Client-Side

My current challenge involves concealing my API key from being visible in the network tab of a browser. Below are some GET requests I made to HERE Map, where the API key is exposed: https://1.base.maps.ls.hereapi.com/maptile/2.1/info?xnlp=CL_JSMv3.1.30.4&a ...

Running cy.task after all test suites can be done by adding the task in a

I need some guidance on running cy.task after executing all test suites. I have a file generated at the start of the tests that I would like to remove once they are completed. Regardless of whether any tests passed or failed, I want to trigger cy.task im ...

Basic library using babel, TypeScript, and webpack - Error: (...) is not a recognized function; within Object.<anonymous>

Recently, I encountered a strange issue while trying to bundle a simple function using babel, typescript, and webpack. You can find the example that is not working at this link. To test it, simply download the code, run npm/yarn install, npm/yarn run buil ...

Nodejs encountering issues with reading data from collection despite having "readWrite" role in Mongodb permissions

I developed an application using Node.js and MongoDB. In MongoDB, I added a new user with the roles specified below: { "_id" : "testdb.testdbuser", "user" : "testdbuser", "db" : "testdb", "roles" : [ { ...

Guide on transferring numerous files (50k+) from a folder to AWS S3 using node.js

Currently, my Node.js API on a Windows machine is generating numerous XML files that are later sent to an S3 bucket. The quantity of files sometimes surpasses 50k. For the uploading process, I am utilizing the aws-sdk package. Essentially, I iterate throu ...