Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose multi update

I want to update multiple docs with different values.

My Database looks something like this.

[
    {
        "_id": 1,
        "value": 50
    },
    {
        "_id": 2,
        "value": 100
    }
]

This Query return an error because i'm passing an array instead of an object in the $set.

    Model.update({_id: {$in: ids}}, {$set: ids.value}, {multi: true};

I want my database to look like this

[
        {
            "_id": 1,
            "value": 4
        },
        {
            "_id": 2,
            "value": 27
        }
    ]
like image 781
Millenial2020 Avatar asked Feb 04 '16 19:02

Millenial2020


3 Answers

Supposing you had an array of objects that you wanted to update in your collection on matching ids like

var soldItems = [
        {
            "_id": 1,
            "value": 4
        },
        {
            "_id": 2,
            "value": 27
        }
    ];

then you could use the forEach() method on the array to iterate it and update your collection:

soldItems.forEach(function(item)){
    Model.update({"_id": item._id}, {"$set": {"value": item.value }}, callback);
});

or use promises as

var updates = [];
soldItems.forEach(function(item)){
    var updatePromise = Model.update({"_id": item._id}, {"$set": {"value": item.value }});
    updates.push(updatePromise);
});

Promise.all(updates).then(function(results){
    console.log(results);
});

or using map()

var updates = soldItems.map(function(item)){
    return Model.update({"_id": item._id}, {"$set": {"value": item.value }});       
});

Promise.all(updates).then(function(results){
    console.log(results);
}); 

For larger arrays, you could take advantage of using a bulk write API for better performance. For Mongoose versions >=4.3.0 which support MongoDB Server 3.2.x, you can use bulkWrite() for updates. The following example shows how you can go about this:

var bulkUpdateCallback = function(err, r){
    console.log(r.matchedCount);
    console.log(r.modifiedCount);
}
// Initialise the bulk operations array
var bulkOps = soldItems.map(function (item) { 
    return { 
        "updateOne": { 
            "filter": { "_id": item._id } ,              
            "update": { "$set": { "value": item.value } } 
        }         
    }    
});

// Get the underlying collection via the native node.js driver collection object
Model.collection.bulkWrite(bulkOps, { "ordered": true, w: 1 }, bulkUpdateCallback);

For Mongoose versions ~3.8.8, ~3.8.22, 4.x which support MongoDB Server >=2.6.x, you could use the Bulk API as follows

var bulk = Model.collection.initializeOrderedBulkOp(),
    counter = 0;

soldItems.forEach(function(item) {
    bulk.find({ "_id": item._id }).updateOne({ 
        "$set": { "value": item.value }
    });

    counter++;
    if (counter % 500 == 0) {
        bulk.execute(function(err, r) {
           // do something with the result
           bulk = Model.collection.initializeOrderedBulkOp();
           counter = 0;
        });
    }
});

// Catch any docs in the queue under or over the 500's
if (counter > 0) {
    bulk.execute(function(err,result) {
       // do something with the result here
    });
}
like image 104
chridam Avatar answered Oct 17 '22 10:10

chridam


First of all your update() query is not ok..

See documentation for this here

Model.update(conditions, update, options, callback);

Suppose your current db model contains docs like this (as you described in question as well):

[
    {
        "_id": 1,
        "value": 50
    },
    {
        "_id": 2,
        "value": 100
   }
];

and you've below array which contains objects (i.e., docs) to be modified with current db's docs to like this:

idsArray: [
    {
        "_id": 1,
        "value": 4
    },
    {
        "_id": 2,
        "value": 27
    }
];

From my experience with mongodb & mongoose, i don't think you can update all docs with single line query (that's you're trying to do).. (P.S. I don't know about that so I am not sure to this..)

But to make your code work, you will be doing something like this:

Idea: Loop over each doc in docs i.e, idsArray and call update() over it..

So, Here's code to this:

idsArray.forEach(function(obj) {
    Model.update({_id: obj._id}, {$set: {value: obj.value}});
});

In above code, I am supposing you've _id values in db docs as they 're written above (i.e, "_id": 1).. But if they're like this "_id": ObjectId('1')

[
  {
    "_id": ObjectId('1'),
    "value": 50
  },
  .....
  .....
]

then you'll be need to convert _id to ObjectId(obj._id) in update() query.. so for that you'll be doing like this.

var ObjectId = require('mongodb').ObjectID;

idsArray.forEach(function(obj) {
   Model.update({_id: ObjectId(obj._id)}, {$set: {value: obj.value}});
});

P.S. Just confirm it (i.e., _id) before go forward..

Hope this helps.

Cheers,

like image 6
narainsagar Avatar answered Oct 17 '22 10:10

narainsagar


Multi update can be used only for updating multiple documents to the same value(s) or updating with the same update operation for all documents.

If you want to update to different values you have to use several update statements.

like image 2
Matthias M Avatar answered Oct 17 '22 10:10

Matthias M