Reputation: 1107
How can I update multiple records using mongoose in NodeJS - Express, each with a unique value (the items do not all have the same value) without causing an N+1 problem? I have the following scenario:
Products:
[
{
_id: "407f191e810c19729de860ea",
title: "....",
in_stock: 100,
},
{
_id: "507f1f77bcf86cd799439011",
title: "....",
in_stock: 100,
},
....
]
Then given an "order" request like this:
[
{
item_id: "507f1f77bcf86cd799439011",
quantity: 5
},
{
item_id: "407f191e810c19729de860ea",
quantity: 8
},
...
]
In other words, given an array of id,quantity tuples, I need to reduce the stock of each corresponding record in the database. How can this be done without having to run one query per item?
From the example above, the end result in the db should be:
[
{
_id: "407f191e810c19729de860ea",
title: "....",
in_stock: 92,
},
{
_id: "507f1f77bcf86cd799439011",
title: "....",
in_stock: 95,
},
....
]
Upvotes: 4
Views: 1290
Reputation: 1107
For those who encounter this question, I have yet to find a non N+1 solution to this. To expand upon the answers above, the best solution I have found is to use the async module, to at least avoiding potentially blocking code:
itemModel.find({ // find items from ids
'_id': itemIds
}).then(function (items) {
if (items) {
asyncModule.each(items,
function (item, callback) {
item.in_stock -= orderQuantities[item._id];
item.save();
callback(); // tell async that current object is done processing
}, onCompleteFunc);
}
}).catch(handleError);
If anybody has any comment or improvement suggestion to this solution, feedback is welcome.
Upvotes: 1
Reputation: 197
You will need to iterate over your order array finding item in stock using id then you can update the stock count, like,
order.forEach((item) => {
Products.findByIdandUpdate(item._id,
{$set: {in_stock: (in_stock - item.quantity)}}, {new:true})
.then((updatedItem) => {
if(!updatedItem) {
console.log('Cannot update!');
}
else {
console.log(updatedItem);
}
});
});
Upvotes: 2
Reputation: 1155
well, in this case, you somewhat can use a code like this. But I highly doubt that's what you want to achieve.
I have also tried the same problem in my project but came across the only way to do is to iterate by id and run different queries only.
db.Element.update(
{ _id: { $in: ['id1', 'id2', 'id3'] } },
{ $set: { visibility : yourvisibility } }
)
a code like this should do the trick
updateById(0); //call updates iteration
function updateById(i) {
if (i < order.length) {
Products.findByIdAndUpdate({ _id: order[i]._id }, { in_stock: order[i].in_stock }, (err, data) => {
if (!err) { updateById(i++); }
else{throw err}
});
}
else {
// return
}
}
Upvotes: 3