Daniel Valland
Daniel Valland

Reputation: 1107

Mongoose - How To Update Many Specific Records With One Value Per Reord?

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

Answers (3)

Daniel Valland
Daniel Valland

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

Ashutosh Pandey
Ashutosh Pandey

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

ALPHA
ALPHA

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

Related Questions