Home > Back-end >  Mongoose bulk update
Mongoose bulk update

Time:12-09

I want to be able to update an array of objects where each object has a new unique value assigned to it.

Here is a simplified example of what I'm doing. items is an array of my collection items.

let items = [{_id: '903040349304', number: 55}, {_id: '12341244', number: 1166}, {_id: '667554', number: 51115}]

I want to assign a new number to each item, and then update it in collection:

items = items.map(item => {
  item.number = randomInt(0, 1000000);
  return item;
})

What would be the best way to update the collection at once? I know that I could do it in forEach instead of map, how ever this seems as a dirty way of doing it, as it won't do the bulk update.

items.forEach(async (item) => {
  await this.itemModel.update({_id: item._id}, {number: randomInt(0, 1000000)})
});

I've checked the updateMany as well but my understanding of it is that it's only used to update the documents with a same new value - not like in my case, that every document has a new unique value assigned to it.

CodePudding user response:

After a bit of thinking, I came up with this solution using bulkWrite.

const updateQueries = [];
items.forEach(async (item) => {
  updateQueries.push({
    updateOne: {
      filter: { _id: item._id },
      update: { number: item.number },
    },
  });
});
await this.itemModel.bulkWrite(updateQueries);

About bulkWrite

Sends multiple insertOne, updateOne, updateMany, replaceOne, deleteOne, and/or deleteMany operations to the MongoDB server in one command. This is faster than sending multiple independent operations (like) if you use create()) because with bulkWrite() there is only one round trip to MongoDB.

CodePudding user response:

You can call an aggregate() to instantly update them without needing to pull them first:

Step1: get a random number with mongoDb build in $rand option which returns a number between 0 and 1 Step2: $multiply this number by 1000000 since that is what you defined ;) Step3: use another $set with $floor to remove the decimal portion

YourModel.aggregate([
  {
    '$set': {
      'value': {
        '$multiply': [
          {
            '$rand': {}
          }, 1000000
        ]
      }
    }
  }, {
    '$set': {
      'value': {
        '$floor': '$value'
      }
    }
  }
])

Here a picture of how that looks in mongo Compass as a proof of it working: enter image description here

  • Related