How would I go about creating multiple documents with different schemas in one REST API request in Node/Mongoose/Express?
Say for example I need to create a user and a site on a single request, say for example /createUser.
I could of course create a user and then in the returned promise, create the next record, but what if that second record doesn't meet validation? Then I've created a user without the second record.
User.create(userData)
.then(user => {
Site.create(siteData)
.then(site => {
// Do something
})
.catch(err => {
console.log(err)
// If this fails, I'm left with a user created without
// a site.
})
})
.catch(err => {
console.log(err)
})
Is there a good practice to follow when creating multiple documents like this? Should I run manual validation instead before each .create() runs? Any guidance/advice would be very much appreciated!
You have the transaction problem here. You are trying to write into two different models but want the whole operation to be atomic and if any of them fails you need to rollback. Up until mongo 4.0 transactions were not supported by mongo and a work around for these sort issues was two phased commits. Now in mongo 4.0 we have transactions to cater such problems.
Hope it helped.
Related
Let me be real simple. I am running node.js server. When I receive data from patch request, first I need to check in database. If the row exists I will update it, otherwise I will create that record. Here is my code. This is what I am calling in the request handler callback.
let dbCollection = db$master.collection('users');
createUser(req.body).then(user => {
dbCollection.updateMany(
{ rmisId: req.body.rmisId },
{ $set: { ...user } },
{ upsert: true }
).then((result) => {
log.debug("RESULTS", user);
return result;
})
.catch((err => {
log.debug(err);
return err;
}));
})
This is working fine in sequential requests. But its creating duplicate record when I receive 10 concurrent request. I am running on my local machine and replicating concurrent request using Apache JMeter. Please help me if you have experienced this kind of problem.
Thank you !
UPDATE
I have tested another approach that reads the database like dbCollection.find({rmisId: req.body.rmisId}) the database for determine its existing or no. But it has no difference at all.
You cannot check-and-update. Mongodb operations are atomic at the document level. After you check and see that the record does not exist, another request may create the document you just checked, and after that you can recreate the same record if you don't have unique indexes or if you're generating the IDs.
Instead, you can use upsert, as you're already doing, but without the create. It looks like you're getting the ID from the request, so simply search using that ID, and upsert the user record. That way if some other thread inserts it before you do, you'll update what the previous thread inserted. If this is not something you prefer, add a unique index for that user ID field.
I wanted to update the field data only ,but my code it adding an object each time i am calling update api.I have gone through many sites and found out updateOne is the method but couldnt end up undersatnding how to implement here.I am quite new to node so any help would be appreciated.
const update=(req,res)=>{
console.log(req);
models.detailsSchema.findByIdAndUpdate(req.body.vehicleId,req.body.data,{new:true}).then((msg,err)=>{
if(err)
res.status(400).json({err})
res.status(200).json({
"resCode":"0000",
"resStatus":"Success",
"resMsg":msg
})
});
}
Looks like you're using Mongoose connected to a MongoDB instance? If that's the case, Schema.findByIdAndUpdate works on the primary key or ObjectId of the record you're trying to update. To make this code work, if my assumptions are correct, change to this:
models.detailsSchema.findByIdAndUpdate(req.body._id, req.body.data, { new:true })
Of course, you're going to want to put in some check to make sure _id is defined if this is a create/update route.
I have a node application with MySQL as DB. I on my way to making an endpoint that will update multiple rows with different data for each. Also, I am using sequelize as ORM.
Now I know I can update a row like
model.update(data).then(()=>{res.end('Row Updated')});
Now my question is where should I call update method for second model. ie in the cb function passed to then() or after the update.model method
I mean which of the following would be a best practice.
models1.update(data1).then(()=>{console.log('Row 1 Updated')});
model2.update(data2).then(()=>{console.log('Row 2 Updated')});
**OR**
model1.update(data1).then(()=>{
model2.update(data2).then(()=>{console.log('All the rows have been updated')})
});
Neither of the above are correct because promises aren't chained properly. This impedes proper control flow and error handling.
If queries are independent, they could be performed in parallel:
Promise.all([
model1.update(data1),
model2.update(data2)
])
.then(() => {
console.log('All the rows have been updated');
});
My server application (using node.js, mongodb, mongoose) has a collection of documents for which it is important that two client applications cannot modify them at the same time without seeing each other's modification.
To prevent this I added a simple document versioning system: a pre-hook on the schema which checks if the version of the document is valid (i.e., not higher than the one the client last read). At first sight it works fine:
// Validate version number
UserSchema.pre("save", function(next) {
var user = this
user.constructor.findById(user._id, function(err, userCurrent) { // userCurrent is the user that is currently in the db
if (err) return next(err)
if (userCurrent == null) return next()
if(userCurrent.docVersion > user.docVersion) {
return next(new Error("document was modified by someone else"))
} else {
user.docVersion = user.docVersion + 1
return next()
}
})
})
The problem is the following:
When one User document is saved at the same time by two client applications, is it possible that these interleave between the pre-hook and the actual save operations? What I mean is the following, imagine time going from left to right and v being the version number (which is persisted by save):
App1: findById(pre)[v:1] save[v->2]
App2: findById(pre)[v:1] save[v->2]
Resulting in App1 saving something that has been modified meanwhile (by App2), and it has no way to notice that it was modified. App2's update is completely lost.
My question might boil down to: Do the Mongoose pre-hook and the save method happen in one atomic step?
If not, could you give me a suggestion on how to fix this problem so that no update ever gets lost?
Thank you!
MongoDB has findAndModify which, for a single matching document, is an atomic operation.
Mongoose has various methods that use this method, and I think that they will suit your use case:
Model.findOneAndUpdate()
Model.findByIdAndUpdate()
Model.findOneAndRemove()
Model.findByIdAndRemove()
Another solution (one that Mongoose itself uses as well for its own document versioning) is to use the Update Document if Current pattern.
I'm having a devil of a time understanding the one to many relationship options in MongoDB. I have a fairly simple use-case that I want to prove out, and I can't seem to find any good step-by-steps with my Google (and Stack Overflow)-Fu.
If I access mongo's command line tool, insert a document into the "users" collection with a String array of role_id that corresponds to the _id in the "roles" collection ... how do I tie all of it together on the mongoose side? When I use the findOne method, it just seems to pull the "role_id"s as is, so I know I must be missing something.
If I put all of these roles into the users without any reference what so ever, what happen if I needed to enable or disable roles? Would I need to update every single relevant role in every single user?
Thanks muchly for the assistance and patience for another new Mongoer!
According to your explanation you just forgot to use 'populate' in your query. Please note that results will not contain linked document, so you need to use 'populate' in order to let mongoose know - 'Hey, I want to pull info about this role as well'.
Example:
//promises
User
.findOne(<searchQuery>)
.populate('roles')
.exec()
.then(function(foundUser){
return foundUser; //do something with results (foundUser)
})
.onReject(function(err){
throw err; //do something with error
});
//callbacks
User
.findOne(<searchQuery>)
.populate('roles')
.exec(function(err, foundUser){
if(err){
throw err; //do something with error
} else {
//do something with results (foundUser)
}
});
Please check the following links:
http://mongoosejs.com/docs/2.7.x/docs/populate.html
http://mongoosejs.com/docs/populate.html