Mongoose: Populate after find() - node.js

I'm new to mongo and node js, here's what I'm trying to do:
The API is to check if there is an existing entry in the DB, based on the query.
(a) If no existing document, create a new document, populate, send to
client.
(b) If document exists, return the document, populate, send to
client.
Problem: In scenario (a), after creating document, the API sends "null" to the client.
Suspect: .populate() & .exec() runs before the API finishes creating a new document. The snippet from the code returns null:
console.log('Inside IF' + video_res); // returns null
What's the best way to resolve this?
model_video.findOne( video_entry,
function(err, video_req) { // Send Back Object ID
if (err) res.send(err);
if (!video_req) { // Does not work
console.log('-----STATUS : No Video Found');
model_video.create(video_entry, function(err, video_res) {
console.log('Call back activated');
if (err) res.send(err);
console.log('Response is ' + video_res);
return video_res; // Does not work here!
}); // Ends - Create
console.log('Inside IF ' + video_res);
}
else { // Works
console.log('-----STATUS : Video Found')
if (err) return res.send(err);
var video_res = video_req;
console.log('Response is ' + video_res);
return video_res;
};
})
.populate('_chirps')
.exec(function(err, video_res) {
if (err) return res.send(err);
res.json(video_res);
console.log('Final Output is ' + video_res)
});
};
Your help is greatly appreciated!

The callback exec() callback executes immediately after your .findOne query, you need to put the rest of your code in that callback. I've refactored your code to make it more inline with what you're trying to do.
model_video.findOne(video_entry)
.populate('_chirps')
.exec(function(err, video_res) {
if (err) return res.send(err);
if (video_res) {
console.log('-----STATUS : Video Found')
console.log('Response is ' + video_res);
res.json(video_res)
}
else {
console.log('-----STATUS : No Video Found');
model_video.create(video_entry, function(err, new_video_res) {
if (err) return res.send(err);
console.log('Response is ' + new_video_res);
res.json(new_video_res);
});
}
})

Related

Node JS res.send not a function

i'm trying create first node js api.
i wroted this code but when i start server.js it's not working.
here code :
var executeQuery = function(res, query){
sql.connect(dbConfig, function (err) {
if (err) {
console.log("Error while connecting database :- " + err);
res.send(err);
}
else {
// create Request object
var request = new sql.Request();
// query to the database
request.query(query, function (err, res) {
if (err) {
console.log("Error while querying database :- " + err);
res.send(err);
}
else {
res.send(res);
}
});
}
});
}
when i start it error say :
C:\node\atlasErpApi\server.js:47 res.send(res);
TypeError: res.send is not a function at C:\node\atlasErpApi\server.js:47:43
You are declaring a new variable res as argument to your request.query callback which shadows the res the comes as argument to your executeQuery function.
This should be most obvious at the line res.send(res) which, if you think about it, doesn't make sense at all.
Use different variable names for the two. For example, change the inner one to sqlRes:
var executeQuery = function(res, query) {
sql.connect(dbConfig, function(err) {
if (err) {
console.log("Error while connecting database :- " + err);
res.send(err);
} else {
// create Request object
var request = new sql.Request();
// query to the database
request.query(query, function(err, sqlRes) { // <<< CHANGED to sqlRes
if (err) {
console.log("Error while querying database :- " + err);
res.send(err);
} else {
res.send(sqlRes); // This makes more sense now :-)
}
});
}
});
}
Note: Technically this also happens for err - you have an err from request.query's callback shadowing the err from sql.connect's callback. But this usually doesn't matter because you won't need to use the outer err in the inner callback.

Duplicate mongoose Documents

I'm looking for a very simple way to duplicate a document in my DB but can't find a way to do it.
I have 2 models with the exact same schema.
What i'm doing is
1stDB.findOne({id:"1"}, function(error, results){
if(!error){
var 2ndb = new 2nDB(results);
2nd.save(function (err) {
if (err) {
return err;
}
else {
console.log("SUCCESSFULL");
}
});
}
})
There seems to be an issue as in my console results is formatted properly but just wont save.
But if i do it manually : 2ndb.anyfield = anyvalue it works.
I think it might have to do with promise ? but i'm not very familiar with the concept and might be wrong.
I've tried this :
1stDB.findOne({id:"1"}, function(error, results){
if(!error){
var 2ndb = new 2nDB(**{results}**);
2nd.save(function (err) {
if (err) {
return err;
}
else {
console.log("SUCCESSFULL");
}
});
}
})
and this ( In the hope that deleting the _id and keeping my custom .id field to identify similar document but still having an uniq _id by document would work but it didn't )
1stDB.findOne({id:"1"}, function(error, results){
if(!error){
**var objectResponse = results;
delete objectResponse._id;**
var 2ndb = new 2nDB(results);
2nd.save(function (err) {
if (err) {
return err;
}
else {
console.log("SUCCESSFULL");
}
});
}
})
You can use the following to achieve the required results
1stDB.findOne({id:"1"}).lean().exec(function(error, results){
if(!error){
var objectResponse = results;
delete objectResponse._id;
var 2ndb = new 2nDB(objectResponse);
2nd.save(function (err) {
if (err) {
return err;
}
else {
console.log("SUCCESSFULL");
}
});
}
})
If the lean option is not used, mongoose will return a mongoose object instead of a simple json. This is why you were not able to pass the result directly to the constructor of the 2nd schema. Using the lean query the response will be a plain JSON object which can be passed to the constructor of the 2nd schema. For more information check this stackoverflow post on returning a plan object as response from mongoose

Sending express response with data from 2 different mongoose queries

I need to query 2 different collections and send it in the express response. I have a very vague idea of what is needed to do so. I tried to contact the query documents to an empty array and send that new array as the response. But I receive an empty array as a response.
This is my route.
site.route('/campus/development')
.get(function(req, res) {
var devPosts = [];
development.find().exec(function(err, docs) {
if (err) {
console.log('Error : ' + err);
} else {
if (docs != null) {
devPosts = devPosts.concat(docs);
console.log(docs);
} else {
console.log('No posts found');
}
}
});
jobs.find().exec(function(err, jobs) {
if (err) {
console.log('Error : ' + err);
} else {
if (jobs != null) {
devPosts = devPosts.concat(jobs);
console.log(jobs);
} else {
console.log('No jobs');
}
}
});
res.send(devPosts);
});
This is due to the async operation of the requests to the database. There are a variety of solutions to this but basically distill down to two types: callbacks or promises.
A callback solution might look like:
site.route('/campus/development')
.get(function(req, res) {
development.find().exec(function(err, devDocs) {
if (err) {
console.log('Error : ' + err);
} else {
if (devDocs != null) {
console.log(devDocs);
jobs.find().exec(function(err, jobs) {
if (err) {
console.log('Error : ' + err);
} else {
if (jobs != null) {
console.log(jobs);
res.send([devDocs, jobs]);
} else {
console.log('No jobs');
}
}
});
} else {
console.log('No posts found');
}
}
});
});
But this introduces a couple of interesting issues: one is the phenomenon known as callback hell and the other is that you should be responding with the errors which means you would need to have a response call for each error (albeit this is a very simplistic approach to it).
As mentioned earlier there is another type of solution which involves using promises. There are a bunch of libraries that you can use and actually Mongoose returns a promise from the exec method. However if you are on Node 0.12.x you can also use the native Promise (it was introduced in 0.11 but you should be using 0.12.x over 0.11.x). A benefit to using the native promise over the one returned from Mongoose is that you can execute these requests in parallel since they don't depend on each other.
site.route('/campus/development')
.get(function(req, res) {
Promise.all([
development.find().exec(), // returns a promise
jobs.find().exec() // returns a promise
]).then(function(results) {
// results is [devDocs, jobs]
console.log(results);
res.send(results);
}).catch(function(err) {
res.send(err);
});
});

Mongoose not updating the MongoDB document

Here is the nodejs code I'm using to update the document in the mongoDB, req.body contains the document which was send as an post request to the nodejs server,
it is not throwing any errors but not updating the document too, any suggestions why this is happening;
router.route('/results').post(function(req,res){
var toupdate = req.body;
delete toupdate._id;
console.log(toupdate)
Question.update({_id:req.body._id}, toupdate, function(err){
if(err){
console.error(err.stack);
}
});
// even tried Question.update({_id:req.body._id}, {$set:{questions:toupdate.question}});
});
I also tried using findById and then saving the document this time got 500 as an response:
router.route('/results').post(function(req,res){
var toupdate = req.body;
delete toupdate._id;
console.log(toupdate)
Question.findById(eq.body._id, function (err, tank) {
if (err){
console.log(err.stack);
return handleError(err);
}
toupdate.save(function (err){
if (err){
console.log(err.stack);
return handleError(err);
}
});
});
});
Thanks for the support, tried your solution JohnnyHK but it didn't work, but found a way to update the document, had to assign fields of req.body to the fields of an object, here :
router.route('/results').post(function(req,res){
Question.findOne(req.body._id, function (err, questions) {
if (err) return handleError(err.stack);
questions.question = req.body.question;
questions.options = req.body.options;
questions.difficulty = req.body.difficulty;
questions.type = req.body.type;
questions.answer = req.body.answer;
questions.domainof = req.body.domainof;
questions.topic = req.body.topic;
questions.weightage = req.body.weightage;
questions.created_by = req.body.created_by;
questions.save(function (err){
if (err) return handleError(err.stack);
console.log(questions);
});
});
});

Update an array of documents in Mongoose

How can I update ALL documents in a collection where an attributes value needs to be different (a unique number) for each document?
Below is my current code. This actually seems to update (I don't get an error) but the values in the db are not being updated.
Model.find({}, function (err, docs) {
if (err) {
console.log(err);
};
if(docs && docs.length > 0){
for(var i=0; i<docs.length; i++){
//SET NEW VALUE FOR EACH DOC - VALUE MUST BE UNIQUE TO EACH DOC
docs[i].code = generateRandomCode();
}
// PASS IN ARRAY OF UPDATED DOCS TO BE SAVED
Model.update(docs, function (err, docs) {
if (err) {
console.log(err);
}
if(!err){
req.updatedSuccessfully = true;
}
return next();
});
}
else{
return next();
}
});
Before this I was trying to do something like this:
Model.update({}, { code: generateRandomCode() }, { multi: true }, function (err, numberAffected, raw) {
if (err) return handleError(err);
console.log('The number of updated documents was %d', numberAffected);
console.log('The raw response from Mongo was ', raw);
});
The problem with this is that generateRandomCode() is only called once but I need to create a different code for each document. So neither of these example work.
Instead of trying model.update(), can you try to simply save the documents?
See answer to this question on this url: Update model with Mongoose, Express, NodeJS

Resources