asajadi84
asajadi84

Reputation: 559

MongoDB updating the document fails in a foreach loop

I'm working on a website which we get every day's order in a JS array like this:

let todayOrders = [
    {
        order_name: "apple",
        order_count: 2
    },
    {
        order_name: "orange",
        order_count: 3
    },
    {
        order_name: "apple",
        order_count: 1
    }
];

I need to add every order count to the total order count document which keeps track of every order. So I used a foreach loop and an updateOne method inside it like this:

todayOrders.forEach(function(item, index){
    Product.updateOne(
        {
            product_name: item.order_name
        },
        {
            $inc: {product_count: item1.order_count}
        },
    );
});

The problem is this code only works fine if the todayOrders does not contain any duplicate order_name which in that case it does not apply all the changes. I believe it has something to do with the async behavior of Mongoose.

Upvotes: 2

Views: 1165

Answers (2)

Tom Slabbaert
Tom Slabbaert

Reputation: 22296

You are not waiting for your promises to resolve, this is considered bad practice that could lead to unexpected behaviour, in your case it should not affect the intended logic as Mongo write operators on a single document are atomic.

However I still do recommend you refactor your code to wait until the promise resolves, this can't be done with forEach due to it not having a return value.

Here is a quick example of how you can do it:

for (const item of todayOrders) {
    await Product.updateOne(
        {
            product_name: item.order_name
        },
        {
            $inc: {product_count: item1.order_count}
        },
    );
}

Or


// ### This is not recommended as it not limited by scale and can cause the app to run out of memory and crash ###


const promises = [];
todayOrders.forEach(function(item, index){
    promises.push(Product.updateOne(
        {
            product_name: item.order_name
        },
        {
            $inc: {product_count: item.order_count}
        },
    ))
});
await Promise.all(promises)

For parallel running I recommend you use something like bluebird, however you can do it yourself with js native promise.

Upvotes: 2

Abdul Haseeb
Abdul Haseeb

Reputation: 372

Maybe group the orders.

todayOrders.reduce((acc,item)=>{
  const index = acc.map(i=>i.order_name).indexOf(item.order_name);
  if(index === -1)
    acc.push(item);
  else 
    acc[index].order_count += item.order_count;
  return acc;
},[]).forEach(...)

Upvotes: 1

Related Questions