I have a collection that holds documents that contains three fields: first_name, last_name, and age. I'm trying to figure out what query in Mongoose I can use to do a bulk upsert. My app is occasionally receiving a new array of objects with those same three fields. I want the query to check if the first AND last name already exist within a document, and if they do - update the age if it's different. Otherwise, if the first and last name don't exist, insert a new document.
Currently, I'm only doing the import - and haven't yet built out the logic for this upsert piece.
app.post('/users/import', function(req, res) { let data = req.body; let dataArray = []; data.forEach(datum => { dataArray.push({ first: datum.first, last: datum.last, age: datum.age }) }) User.insertMany(dataArray, answer => { console.log(`Data Inserted:`,answer) })
`
And my User model looks like this:
const mongoose = require('mongoose'); const Schema = mongoose.Schema; const userSchema = new Schema({ first: String, last: String, age: Number, created_at: { type: Date, default: Date.now } }); var User = mongoose.model('User', userSchema); module.exports = User;
May 20, 2019. In MongoDB, an upsert means an update that inserts a new document if no document matches the filter . To upsert a document in Mongoose, you should set the upsert option to the Model.
When you create an instance of a Mongoose model using new , calling save() makes Mongoose insert a new document. If you load an existing document from the database and modify it, save() updates the existing document instead.
Mongoose by default does not create any collection for the model in the database until any documents are created. The createCollection() method is used to create a collection explicitly.
Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment. Mongoose supports both promises and callbacks.
([email protected], [email protected])
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:
$set
mongo operator in the updateOne.update
field, since mongoose handles it in case of upsert (see bulkWrite() comments in example).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 },
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With