(mongoose@4.9.1, mongodb@3.4.2)
TL;DR
await GasStation.collection.bulkWrite([ // <<==== use the model name
{
'updateOne': {
'filter': { 'id': '<some id>' },
'update': { '$set': { /* properties to update */ } },
'upsert': true, // <<==== upsert in every document
}
},
/* other operations here... */
]);
Long story:
After struggling with Mongoose API poor documentation, I solved the bulk upsert tweaking updateOne:{}
operation in the bulkWrite()
method.
A couple of undocumented things to consider:
// suppose:
var GasStation = mongoose.model('gasstation', gasStationsSchema);
var bulkOps = [ ];
// for ( ... each gasStation to upsert ...) {
let gasStation = { country:'a', localId:'b', xyz:'c' };
// [populate gasStation as needed]
// Each document should look like this: (note the 'upsert': true)
let upsertDoc = {
'updateOne': {
'filter': { 'country': gasStation.country, 'localId': gasStation.localId },
'update': gasStation,
'upsert': true
}};
bulkOps.push(upsertDoc);
// end for loop
// now bulkWrite (note the use of 'Model.collection')
GasStation.collection.bulkWrite(bulkOps)
.then( bulkWriteOpResult => {
console.log('BULK update OK');
console.log(JSON.stringify(bulkWriteOpResult, null, 2));
})
.catch( err => {
console.log('BULK update error');
console.log(JSON.stringify(err, null, 2));
});
The two key things here are incomplete API documentation issues (at the time of writing, at least):
'upsert': true
in each document. This is not documented in Mongoose API (), which often refers to node-mongodb-native driver. Looking at updateOne in this driver, you could think to add 'options':{'upsert': true}
, but, no... that won't do. I also tried to add both cases to the bulkWrite(,[options],)
argument, with no effect either.
GasStation.collection.bulkWrite()
. Although Mongoose bulkWrite() method claims it should be called Model.bulkWrite()
(in this case, GasStation.bulkWrite()
), that will trigger MongoError: Unknown modifier: $__
. So, Model.collection.bulkWrite()
must be used.
Additionally, note:
- You don't need to use the
$set
mongo operator in the updateOne.update
field, since mongoose handles it in case of upsert (see bulkWrite() comments in example).
- Note that my unique index in the schema (needed for upsert to work properly) is defined as:
gasStationsSchema.index({ country: 1, localId: 1 }, { unique: true });
Hope it helps.
==> EDIT: (Mongoose 5?)
As noticed by @JustinSmith, the $set
operator added by Mongoose doesn't seem to be working anymore. Maybe it's because of Mongoose 5?
In any case, using $set
explicitly should do:
'update': { '$set': gasStation },
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…