Mongoose still saves a document when there is an error - node.js

I have a pre-save method defined when saving a document, as follows:
Org.pre("save",function(next, done) {
var Currency = require('./currency');
var cur = this.get('currency');
console.log("checking currency: " + cur);
Currency
.findOne({name: cur})
.select('-_id name')
.exec(function (err, currency) {
if (err) done(err);
if (!currency) done(new Error("The currency you selected ('" + currency + "') is not supported. Please select one from /currencies"));
next();
});
});
This method checks the currencies collection to see if the currency field input is supported. In testing my API, I get the appropriate error returned (500 error with the message: The currency you selected...), but the document is still saved in MongoDB. I would expect that when an error is sent the document should not be saved. Am I missing something here?

You're still calling next(); in the error case, so try rewriting that part as:
Currency
.findOne({name: cur})
.select('-_id name')
.exec(function (err, currency) {
if (err) return done(err);
if (!currency) return done(new Error("The currency you selected ('" + currency + "') is not supported. Please select one from /currencies"));
next();
});

Seems like by not putting the next() in brackets, the flow continues. I changed the exec function as follows in order for this to work:
if (err) {
done(err);
} else if (!currency) {
done(new Error("The currency you selected ('" + currency + "') is not supported. Please select one from /currencies"));
} else {
next();
}
Problem solved.

Related

Nodejs, MongoDB asynchronous callback problem

I'm the beginner of nodejs and MongoDB.I tried to design RESTAPI but there was a problem.
Here is my router code.
app.post('/fcm',function(req,res){
var beaconid = req.body.beacon_id;
var my_token_id = req.body.user_id;
User.find({beacon_id: beaconid}, function(err, output){
if(err) return res.status(500).json({error: err});
if(!output) return res.status(404).json({error: 'user not found in User collections.'});
console.log("output user id :"+output.user_id + " beacon: " +output.beacon_id );
target_token_id = output.user_id;
res.json(output);
});
});
And this is user schema.
var userSchema = new Schema({
user_id: String,
beacon_id: String
});
The result of above function in the console is:
output user id: undefined , beacon: undefined.
But json from res function is printed properly.
This codes look like very simple but I don't know what is the problem.
Please somebody help me.
By using find, you are expecting 1 document or more. This means that the method <Model>.find() should logically return an array. You can make sure that is what happens by logging the output just after the query to make sure it is an array of documents.
To solve your problem, you can either access the document at index 0 of the array:
User.find({beacon_id: beaconid}, function(err, output){
if(err) return res.status(500).json({error: err});
if(!output) return res.status(404).json({error: 'user not found in User collections.'});
console.log("output user id :"+output[0].user_id + " beacon: " +output[0].beacon_id );
target_token_id = output[0].user_id;
res.json(output);
});
Or use findOne() instead, that returns only one document.
User.findOne({beacon_id: beaconid}, function(err, output){
if(err) return res.status(500).json({error: err});
if(!output) return res.status(404).json({error: 'user not found in User collections.'});
console.log("output user id :"+output.user_id + " beacon: " +output.beacon_id );
target_token_id = output.user_id;
res.json(output);
});

Node.js mongodb update over ObjectID

I want to update my Document but it's not working 100% .
// Initialize connection once
MongoClient.connect("mongodb://localhost:27017/testDB", function(err, database) { //"mongodb://localhost:27017/test"
if(err) throw err;
db = database;
});
My collection row looks like:
{ "_id" : ObjectId("53f9379ce9575bbe9ec29581"), "name:paco",
"status:student" }
Now if I want to update the row over the Document as follows:
db.collection('user', function(err, collection){
collection.update({'_id':ObjectID(req.session.loggedIn)}, {image : filename}, {w:1}, function(err, result){
console.log(result);
I am getting just:
{ "_id" : ObjectId("53f9379ce9575bbe9ec29581"), "image:filename" }
How can I make an update to get my data like this??:
{ "_id" : ObjectId("53f9379ce9575bbe9ec29581"), "name:paco",
"status:student" , "image:filename"}
Doing an update the way you did it is going to retrieve the document in your collection with the specified _id, then it is going to replace the content of this document with what you specified as your second parameter. In your case, it will retrieve the document with _id 53f9379ce9575bbe9ec29581, and replace the existing fields with the field you passed, image:filename (that means the existing fields will be removed, as you noticed).
What you want to do is use the $set operator. This operator will not touch the document retrieved, but only modify the field that you specified, or add it if it does not exist.
So your update command should look something like this:
db.collection('user').update({'_id':ObjectID(req.session.loggedIn)}, {$set: {image : filename}}, {w:1}, function(err, result){
console.log(result);
to update record by _id
var ObjectID = require('mongodb').ObjectID;
exports.updateUser = function(req, res) {
var collection = db.collection('users');
collection.update(where, $set:req.body, function(err, result) {
if (err) {
console.log('Error updating user: ' + err);
res.send({'error':'An error has occurred'});
} else {
console.log('' + result + ' document(s) updated');
res.send(user);
}
});
}

Pre-populating documents using Mongoose + Express

I am new to Node+Mongoose, and am currently in the process of creating an API using KeystoneJS. I've managed to populate all posts with the author's email and name. My question is, is there a way to populate the post with author everytime, possibly with some middlewear, without having to rewrite it in each method where I retrieve posts? My goal is to not have multiple instances of populate('author', 'email name') scattered throughout the code. For instance, in the future, I'd like to include the author's profile photo url as well, and I'd prefer to be able to make that change in a single place, that will then be reflected in every place that I retrieve posts.
Current implementation:
Post.model.find().populate('author', 'email name').exec(function (err, posts) {
if (!err) {
return res.json(posts);
} else {
return res.status(500).send("Error:<br><br>" + JSON.stringify(err));
}
});
Post.model.findById(req.params.id).populate('author', 'email name').exec(function (err, post) {
if(!err) {
if (post) {
return res.json(post);
} else {
return res.json({ error: 'Not found' });
}
} else {
return res.status(500).send("Error:<br><br>" + JSON.stringify(err));
}
});
You can create model using statics. This is example of methods for schema
PostSchema.statics = {
getAll: function(cb) {
return this
.find()
.populate('author', 'email name')
.exec(cb);
}
}
You still should use "populate", but it will be in schema file, so you will not care about it in future

update a record in mongodb through mongoose

I tried to update existing record in a collection of mongodb. existing record is {UserName: "niren",AccessToken:"abced",RefreshToken:"fghigh"} and I want to update as {UserName: "niren",AccessToken:"mobile",RefreshToken:"phone"}. I used following code but it's not working.
myModel.update({UserName:"niren"},{$inc:{AccessToken:"mobile",RefreshToken:"phone"}},function(err){
if(err){
console.log("some error happened when update");
}
else{
console.log("update successfull! with name = " + "niren");
myModel.findOne({UserName:"niren"}, function(err, users) {
console.log("updated : " + users);
});
}
})
It was my mistake, I shouldn't use $inc. I should have used {AccessToken:"mobile",RefreshToken:"phone"} instead of {$inc:{AccessToken:"mobile",RefreshToken:"phone"}}. Now its working fine.

MongoDB node native driver creating duplicate documents

I'm getting a duplicate document when using the mongodb-native-driver to save an update to a document. My first call to save() correctly creates the document and adds a _id with an ObjectID value. A second call creates a new document with a text _id of the original ObjectID. For example I end up with:
> db.people.find()
{ "firstname" : "Fred", "lastname" : "Flintstone", "_id" : ObjectId("52e55737ae49620000fd894e") }
{ "firstname" : "Fred", "lastname" : "Flintstone with a change", "_id" : "52e55737ae49620000fd894e" }
My first call correctly created Fred Flinstone. A second call that added " with a change" to the lastname, created a second document.
I'm using MongoDB 2.4.8 and mongo-native-driver 1.3.23.
Here is my NodeJS/Express endpoint:
app.post("/contacts", function (req, res) {
console.log("POST /contacts, req.body: " + JSON.stringify(req.body));
db.collection("people").save(req.body, function (err, inserted) {
if (err) {
throw err;
} else {
console.dir("Successfully inserted/updated: " + JSON.stringify(inserted));
res.send(inserted);
}
});
});
Here is the runtime log messages:
POST /contacts, req.body: {"firstname":"Fred","lastname":"Flintstone"}
'Successfully inserted/updated: {"firstname":"Fred","lastname":"Flintstone","_id":"52e55737ae49620000fd894e"}'
POST /contacts, req.body: {"firstname":"Fred","lastname":"Flintstone with a change","_id":"52e55737ae49620000fd894e"}
'Successfully inserted/updated: 1'
Why doesn't my second update the existing record? Does the driver not cast the _id value to an ObjectID?
What you are posting back the 2nd time contains a field named "_id", and it's a string. That is the problem.
Look at the document, what the save method does is a "Simple full document replacement function". I don't use this function quit often so here's what I guess. The function use the _id field to find the document and then replace the full document with what you provided. However, what you provided is a string _id. Apparently it doesn't equal to the ObjectId. I think you should wrap it to an ObjectId before passing to the function.
Besides, the save method is not recommended according to the document. you should use update (maybe with upsert option) instead
I don't exactly know why a second document is created, but why don't you use the update function (maybe with the upsert operator)?
An example for the update operation:
var query = { '_id': '52e55737ae49620000fd894e' };
db.collection('people').findOne(query, function (err, doc) {
if (err) throw err;
if (!doc) {
return db.close();
}
doc['lastname'] = 'Flintstone with a change';
db.collection('people').update(query, doc, function (err, updated) {
if (err) throw err;
console.dir('Successfully updated ' + updated + ' document!');
return db.close();
});
});
And now with the upsert operator:
var query = { '_id': '52e55737ae49620000fd894e' };
var operator = { '$set': { 'lastname': 'Flintstone with a change' } };
var options = { 'upsert': true };
db.collection('people').update(query, operator, options, function (err, upserted) {
if (err) throw err;
console.dir('Successfully upserted ' + upserted + ' document!');
return db.close();
});
The difference is that the upsert operator will update the document if it exist, otherwise it will create a new one. When using the upsert operator you should keep in mind that this operation can be underspecified. That means if your query does not contain enough information to identify a single document, a new document will be inserted.

Resources