mongoose findOne() query called last in function - node.js

New to Node and Mongoose here. I am having trouble running my mongoose findOne() query in a synchronous fashion within a function. Here is my code:
exports.read = function(req, res){
console.log("in articles controller read()");
//try to get article creatorId and use user providerData
//name to make fullName
var userName = "";
//get article creator id
User.findOne({ '_id': req.article.creator._id }, function(err, person){
if(err) { return next(err)};
if (!person) { return next(new Error('Failed to find user'))};
console.log("found person");
//return providerData name
userName = person.providerData.name;
});
//assign username value to article creator
var splitName = userName.split(' ');
req.article.creator.fullName = userName;
req.article.creator.firstName = splitName[0] || '';
req.article.creator.lastName = splitName[1] || '';
console.log("end of read()";
res.json(req.article);
};
When I look at my console, I expect to see the logs in the following order:
in articles controller read()
found person
end of read()
But instead, I see in my console:
in articles controller read()
end of read()
found person
I'm assuming that this issue has to probably do with the async nature of node?
Basically, I would like to run the findOne() query before assigning values to my req object so that I can actually have something to assign. Please help.

Callbacks are async, you need to move your code inside it.
User.findOne({ '_id': req.article.creator._id }, function(err, person){
if(err) { return next(err)};
if (!person) { return next(new Error('Failed to find user'))};
console.log("found person");
//return providerData name
userName = person.providerData.name;
//assign username value to article creator
var splitName = userName.split(' ');
req.article.creator.fullName = userName;
req.article.creator.firstName = splitName[0] || '';
req.article.creator.lastName = splitName[1] || '';
res.json(req.article);
});

You are using the Nodejs which is asynchronous and event-driven.
So it will call the method in sequence way:
console.log("in articles controller read()");
User.findOne();
console.log("end of read()";
but User.findOne is the database call which is slow, so it call User.findOne and it will go tho then another method call and when they will return the result it will print.
That's by you will get the result as
in articles controller read()
end of read()
found person
For solving this you can use the async.js or you can directly put the value inside the findOne result:
exports.read = function(req, res){
console.log("in articles controller read()");
//try to get article creatorId and use user providerData
//name to make fullName
var userName = "";
//get article creator id
User.findOne({ '_id': req.article.creator._id }, function(err, person){
if(err) { return next(err)};
if (!person) { return next(new Error('Failed to find user'))};
console.log("found person");
//return providerData name
userName = person.providerData.name;
//assign username value to article creator
var splitName = userName.split(' ');
req.article.creator.fullName = userName;
req.article.creator.firstName = splitName[0] || '';
req.article.creator.lastName = splitName[1] || '';
console.log("end of read()");
res.json(req.article);
});
}

Related

How to get a string from query result in Nodejs

My code is in Nodejs backend below
app.get('/room_selected', function (req, res){
var clientID = 'a#gmail.com';
var room = 'Room 1';
var query = connection.query ('SELECT clientID FROM clientDevices WHERE deviceName = ?', [room],
function (err, rows, fields){
if (err) throw err;
return rows[0].clientID;
});
console.log (query);
if (clientID == query){
res.status(400).json ('success');
} else {
res.status(400).json('The selected room does not have a device attached');
}
});
When I print console.log(query), it returns [ { clientID: 'a#gmail.com' } ].
I want to return only a#gmail.com. Could you guys know how to figure out it? since I want it to compare with clientID to print out the success message, however, it printed out The selected room does not have a device attached
Please help. Thank you
In your code, you didn't wait for the query to be executed. Following is the code which will give a response only after the query is executed.
And also success should not have a status code of 400 so I have removed that which will give a status code of 200
app.get('/room_selected', function (req, res){
var clientID = 'a#gmail.com';
var room = 'Room 1';
var query = connection.query ('SELECT clientID FROM clientDevices WHERE deviceName = ?', [room],
function (err, rows, fields){
if (err) throw err;
if (rows.length && clientID == rows[0].clientID){
res.json('success');
} else {
res.status(400).json('The selected room does not have a device attached');
}
});
});

Accessing the variable outside the function

I am developing a skill for Amazon Alexa in lambda using Node.js.
I've declared the variable globally, and initialised in function and accessing it outside the function. But I'm getting undefined error. Please help.
var res;
async function classSection(handlerInput){
standard = handlerInput.requestEnvelope.request.intent.slots.Class.value;
section = handlerInput.requestEnvelope.request.intent.slots.Section.value;
var speechOutput = `Starting attendance for class ${standard}, section ${section}. <break time = "3s"/>
I will call the Names of the students, please say Present or Absent to Mark the attendance. <break time = "1s"/> Lets Start. `;
//getting the list of students from database
con.connect(function(err){
if(!err) {
console.log("Database is connected");
} else {
console.log("Error connecting database");
}
});
const sql = `SELECT StudentDetailID,StudentName FROM attendance_system.student_detail where student_detail.Class = ${standard}
and student_detail.Section = '${section}';`;
console.log(sql);
con.query(sql, function (err, result, fields) {
con.end();
if (!err){
console.log(result);
console.log("Table Data : "+result[1].StudentName);
res = result[1].StudentName;
console.log("Speech : "+ speechOutput + res);
//Here in res I get the name of the student.
}
else
console.log('Error while performing Query.');
});
console.log(res);
//here I get undefined error.
return handlerInput.responseBuilder
.speak(speechOutput + res)
.reprompt()
.withSimpleCard('Attendance System',`Class : ${standard} \n Section : ${section}`)
.getResponse();
}
Maybe a possible solution was use the promisify method, because of this execution is asynchronise
const promisify = require('util').promisify
async function classSection(handlerInput){
try {
standard = handlerInput.requestEnvelope.request.intent.slots.Class.value;
section = handlerInput.requestEnvelope.request.intent.slots.Section.value;
var speechOutput = `Starting attendance for class ${standard}, section ${section}. <break time = "3s"/>
I will call the Names of the students, please say Present or Absent to Mark the attendance. <break time = "1s"/> Lets Start. `;
//getting the list of students from database
await promisify(con.connect)();
const sql = `SELECT StudentDetailID,StudentName FROM attendance_system.student_detail where student_detail.Class = ${standard}
and student_detail.Section = '${section}';`;
console.log(sql);
const result = await promisify(con.query)(sql)
console.log(result);
console.log("Table Data : "+result[1].StudentName);
const res = result[1].StudentName;
console.log("Speech : "+ speechOutput + res);
//Here in res I get the name of the student.
console.log(res);
//here I get undefined error.
return handlerInput.responseBuilder
.speak(speechOutput + res)
.reprompt()
.withSimpleCard('Attendance System',`Class : ${standard} \n Section : ${section}`)
.getResponse();
} catch (err) {
console.log('Some Error was throwed', err);
throw err;
} finally {
if (con.isConnect()) con.end() // i dont know if isConnect exists
}
}
I am looking questions related to similar problem very often in this forum. Either the documentation on Async-Await is not sufficient or there is some misunderstanding about the usage of Async-Await with callback functions.
There are 2 problems in your case.
con.query is Asynchronous function. So, by the time the your callback function is called, 'console.log(res);' would have already executed and hence nothing is defined in res variable.
You cannot use callback with Aysnc-Await syntax. You have to promisify your callback and use it in Async function to get the expected result.
Here is the example for it

Retrieve data from MongoDB and save it to global object in Node.js and Express.js

I'm trying to get data from MongoDB collection and then save it to a global object.Later I need to parse it to HTML template.
Here is my code:
When user log onto his profile: then we need to get his projects and here we call findeprojects() function
usrRouter.route('/profile')
.all(function (req,res,next) {
if(!req.user){
res.redirect('/');
}
next();
})
.get(function (req,res,userObj) {
// var proj = findprojects();
userObj = req.user;
var pro = {};
pro = findprojects(userObj);
res.render('index',{name:userObj.username, email:userObj.email});
//res.sendFile('profile.html',{root:path.join(__dirname,'../public'),},{name:userObj.username});
});
Here is findeprojects function code:
var findprojects = function(obj) {
var usern = obj.username;
mongodb.connect(url,function(err, db){
if(err) throw err;
var collection = db.collection('projects');
//console.log(usern);
collection.find({'pusername':usern});
cursor =db.collection('projects').find({ 'pusername': usern }).toArray(function(err,items){
//console.log(items);
var i;
for(i=0; i<items.length;){
userProjects.createdBy = items[i].pusername;
userProjects.proName = items[i].projectName;
userProjects.proType = items[i].projectType;
userProjects.proDesc = items[i].projectDesc;
//return userProjects;
i = i+1;
}
});
console.log(userProjects);
});
};
I have declared global object at the top like:
userProjects = {
createdBy:'',
proName:'',
proType:'',
proDesc:''
};
But when I console userprojects object after calling the findeprojects() function it displays empty values.
why dont you use mongoose to model your stuff.
its more intuitive and you no need to declare the global object and do the mapping in the for loop that you are doing.
also your approach is a bit wrong in terms of when you iterate through for aren't you overwriting ?
say you have two documents where pusername is abdul.
so in your case you loose first object which will get overwritten by the second one.
i see that you commented out a return statement but even that wont work properly.
from a design point of view your approach is not efficient.
in mongoose you can do:
{
var userProjectSchema = new mongoose.Schema({
createdBy: { type: String }
, proName: String
, proType: String
, proDesc: String
});
// Find a single document by username.
userProjectSchema.findOne({ pusername : 'abdul' }, function(err, resDoc) {
if (err) return console.error(err);
// do your html stuff here
});
// Find all documents.
userProjectSchema.find(function(err, results) {
if (err) return console.error(err);
// do your html stuff here
});
}

mongoose findone passing the id of the request to the callback using q model

I am using the mongoose query findone like this:
var findUser = function(){
var deferred = q.defer();
administratorModel.findOne({'username': 'aa'}, function(err, username){
if(err) console.log(err);
if(username){
deferred.resolve(username);
}else{
deferred.reject('username' + query.username)
}
});
return deffered.promise;
}
The query of findone returning null if the username couldnt be found in the db,but I want to know which client the query couldnt find. but I got nothing because both err and username is null.
I want to find a way to access the filter query from the callback option, can i do that? How can I pass to the username in a way I could use it in the callback?
May be you can store your data in variable and access it in callback?
var findUser = function (userToFind) {
return q.when(administratorModel
.findOne({'username': userToFind})
.exec()
.then(function (username) {
if (!username) {
throw new Error('Not found ' + userToFind);
}
}));
}

Error "Undefined is not a function " Using callback node.JS

I am trying to save a new Document (user) in my MongoDb and I use callback. The code runs and goes until save the user, but after that I get an error.So I can save user. I have the following code:
function saveUser(userName, socialMediaType, socialMediaID, setDocNumber, callback){
var user;
if(socialMediaType == "fbUID"){
user = new users({
userName: userName,
userEmail: 'userEmail',
teams:[],
fbUID : socialMediaID
});
}else
if(socialMediaType =="google"){
//do the same
}
var query = {}
query["'"+ socialMediaType +"'" ] = socialMediaID
users.findOne(query, function(err, userFound){
if (err) { // err in query
log.d("Error in query FoundUser", err)
log.d("User Found", userFound)
}else
if(userFound == undefined){ //if user does not exist
user.save(function(err, user){
if(err) return console.error(err);
log.d("user saved", user);
currentSession = sessionOBJ.login(user._id, socialMediaID);
callback(currentSession,"created")
});
}else{
currentSession = sessionOBJ.login(userFound._id, socialMediaID);
callback(currentSession,"logged")
}
});
}
I call the function above through this code:
f(fbUID !== undefined){
userModelOBJ.saveUser(userName,"fbUID", fbUID, function(currentSession, status) {
res.send({"status":status,
"sessionID": currentSession.sessionID,
"expires" : currentSession.date});
});
I am getting this error :
The error is in the line :
callback(currentSession,"created")
What could be the problem?
I already did many researchers but this is a specific case.
Your saveUser() call is missing the setDocNumber argument. It looks like you're not using it in your code though, so you might be able to safely remove it. If you are using it somewhere else (that you haven't shown) then you need to do some argument checking at the top of saveUser() to support optional arguments.

Resources