I am querying my mongodb for the user's email that is being passed through a session. When that email is found it looks for that user's friends, and those friends are supposed to be passed to the usersFriends array which is then sent in the chunk to the browser. I included all the code in this block even though the transaction block isn't really pertinent or so I think.
The Problem: is that the usersFriends array is outputting an empty array everywhere except when the console.log is inside the for loop. Thoughts?
app.get('/api/chunk', function(req, res){
var last5;
var usersFriends = [];
Transaction.find().sort({$natural:-1}).limit(5).exec(function(err, docs){
if (err) {
console.log(err);
} else {
last5 = docs;
}
});
User.findOne({ email: req.user.email }, function(err, user){
if (!user) {
console.log(err);
} else {
for (var i = 0; i < user.friends.length; i++) {
(function(cntr){
User.findOne({ email: user.friends[cntr].email}, function(err, result) {
result.password = "Sneaky sneaky"
var name = result.firstName + " " + result.lastName;
usersFriends.push({
name: name,
index: cntr
});
});
})(i);
var chunk = {
"friends": usersFriends,
"transactions": last5
};
} }console.log(usersFriends); // empty array
});
});
Combining all the various things we've discussed in comments for this list of changes:
Pass the cntr in an IIFE so it is uniquely captured for each separate .findOne() request.
In each response, check if this is the last one done so we know that all results have arrived.
Start the .findOne() operations from the completion of the Transaction.find() operation (since I don't know that operation, I'm guessing how this particular aspect should be implemented, but you should be able to see the general idea).
Though would result in this code:
app.get('/api/chunk', function(req, res) {
var last5;
var usersFriends = [];
Transaction.find().sort({
$natural: -1
}).limit(5).exec(function(err, docs) {
if (err) {
console.log(err);
} else {
last5 = docs;
User.findOne({
email: req.user.email
}, function(err, user) {
if (!user) {
console.log(err);
} else {
var totalCnt = user.friends.length;
for (var i = 0; i < totalCnt; i++) {
(function(cntr) {
User.findOne({
email: user.friends[cntr].email
}, function(err, result) {
result.password = "Sneaky sneaky"
var name = result.firstName + " " + result.lastName;
usersFriends.push({
name: name,
index: cntr
});
if (usersFriends.length === totalCnt) {
// all results are done here
// you can create the final response
var chunk = {
"friends": usersFriends,
"transactions": last5
};
}
});
})(i);
}
}
});
}
});
});
Related
This loop is supposed to be checking for opps in the DB that match opps being pulled from the salesforce api results and then either creating a new opp or finding the existing opp and pushing it to the array. It seems that the res.render is running before the opp is found. It creates a new opp but the array returns empty when the page renders.
Account.find({owner:req.user._id, prospect:'false'}).sort({ammount:'desc'}).populate({path: "notes", options:{ sort:{ 'date': -1 } } }).exec(function(err, allAccounts) {
let callGoal = req.user.callGoal;
if(err){
res.send(err);
}else{
// if auth has not been set, redirect to index
if (!req.session.accessToken || !req.session.instanceUrl) { res.redirect('/'); }
//SOQL query
let q = "SELECT Id,Amount,CloseDate,LastActivityDate,Name,StageName,account.Name FROM Opportunity WHERE CloseDate < 2018-10-01 AND OwnerId = '0050a00000J12PdAAJ' AND IsClosed = false AND StageName != 'Stage 6: Won'";
//instantiate connection
let conn = new jsforce.Connection({
oauth2 : {oauth2},
accessToken: req.session.accessToken,
instanceUrl: req.session.instanceUrl
});
//set records array
let softopps = [];
let sfOpps = [];
let query = conn.query(q)
.on("record", function(record) {
sfOpps.push(record);
})
.on("end", function() {
console.log("total in database : " + query.totalSize);
console.log("total fetched : " + query.totalFetched);
let user = req.user;
sfOpps.forEach(function(sfopp){
if(err){
res.send(err);
}else{
Opp.findOne({sfid:sfopp.Id}).exec(function(err, opp){
if(!opp.length){
Opp.create(req.body, function(err, newOpp) {
if(err){
res.send(err)
}else{
newOpp.sfid = sfopp.Id;
newOpp.name = sfopp.Name;
newOpp.owner = user.sfid;
newOpp.save();
return softopps.push(newOpp)
}
})
}else{
return softopps.push(opp);
}
})
}
})
res.render("myaccounts", {accounts:allAccounts, callGoal:callGoal, user:user, sfOpps:sfOpps, opps:softopps});
})
.on("error", function(err) {
console.error(err);
})
.run({ autoFetch : true, maxFetch : 4000 });
}
});
Your Opp.findOne() and Opp.create() calls are asynchronous, so they fire after the res.render().
On the side, you pretty much kill your mongodb with all that unnecessary requests.
Try this logic instead (starting at .forEach)
Find all Opps by ids of sfOpps
Figure out which Opps were not found and create them
Concat all the found and not found Opps
Then, and only then respond
I didn't test that code, but it can roughly give you an idea of what I mean
Opp.find({ sfid : { $in: sfOpps.map(opp => opp.id) } })
.then(found => {
const foundIds = found.map(opp => opp.sfid)
const notFound = sfOpps.filter(opp => !foundIds.includes(opp.sfid)).map(sfopp => {
return {
sfid: sfopp.sfid,
name: sfopp.name,
owner: user.sfid
}
})
Opp.insertMany(notFound)
.then((insertResult) => {
res.render("myaccounts", {
accounts: allAccounts,
callGoal: callGoal,
user: user,
sfOpps: found.concat(notFound),
opps: softopps
});
}).catch(handleError)
}).catch(handleError)
When the page rendered your still empty because the call of creation/find opps is an asynchronous but your page rendering is synchronous that's why your page rendered first before your calls will be complete.I think you should try to use async.eachOfSeries(coll, iteratee, callback) instead of sfOpps.forEach().
Account.find({owner:req.user._id, prospect:'false'}).sort({ammount:'desc'}).populate({path: "notes", options:{ sort:{ 'date': -1 } } }).exec(function(err, allAccounts) {
let callGoal = req.user.callGoal;
if(err){
res.send(err);
}else{
// if auth has not been set, redirect to index
if (!req.session.accessToken || !req.session.instanceUrl) { res.redirect('/'); }
//SOQL query
let q = "SELECT Id,Amount,CloseDate,LastActivityDate,Name,StageName,account.Name FROM Opportunity WHERE CloseDate < 2018-10-01 AND OwnerId = '0050a00000J12PdAAJ' AND IsClosed = false AND StageName != 'Stage 6: Won'";
//instantiate connection
let conn = new jsforce.Connection({
oauth2 : {oauth2},
accessToken: req.session.accessToken,
instanceUrl: req.session.instanceUrl
});
//set records array
let softopps = [];
let sfOpps = [];
let query = conn.query(q)
.on("record", function(record) {
sfOpps.push(record);
})
.on("end", function() {
console.log("total in database : " + query.totalSize);
console.log("total fetched : " + query.totalFetched);
let user = req.user;
async.eachOfSeries(sfOpps,function(sfopp,next){
if(err){
next(err);
}else{
Opp.findOne({sfid:sfopp.Id}).exec(function(err, opp){
if(!opp.length){
Opp.create(req.body, function(err, newOpp) {
if(err){
next(err);
}else{
newOpp.sfid = sfopp.Id;
newOpp.name = sfopp.Name;
newOpp.owner = user.sfid;
newOpp.save();
softopps.push(newOpp);
next();
}
})
}else{
softopps.push(opp);
next();
}
})
}
},function(err){
if (err) res.send(err);
else {
res.render("myaccounts", {accounts:allAccounts, callGoal:callGoal, user:user, sfOpps:sfOpps, opps:softopps});
}
});
})
.on("error", function(err) {
console.error(err);
})
.run({ autoFetch : true, maxFetch : 4000 });
}
});
I hope this will help you.
My User model has a field of type Array with values like this
"courseId" : [
"5ac1fe64cfdda22c9c27f264",
"5ac207d5794f2910a04cc9fa",
"5ac207d5794f2910a04cc9fa"
]
My routes are configured in this way:
router.get('/:userid/vendor-dashboard', function (req, res) {
var courseID = [];
User.findById(req.params.userid, function (err, doc) {
if (err) {
res.send(err);
} else {
for (var i = 0; i < doc.courseId.length; i++) {
Course.findById(doc.courseId[i], function (err, course) {
if (err) {
console.log(err);
} else {
console.log(course.title);
courseID.push(course.title);
}
})
}
res.send(JSON.stringify(courseID));
}
})
})
First I'm finding a user and when user is found, He should find all the courses in the array and display their title.
Now, I'm able to get the title in console, but when I try to send it through res.send, it shows an empty array.
What am I doing wrong?
The main problem is that you are sending the response before getting the response from Course model.
The correct way using callback will be:
router.get('/:userid/vendor-dashboard', function(req, res) {
var courseID = [];
User.findById(req.params.userid, function(err, doc) {
if (err) {
return res.send(err);
}
var resolved = 0;
for (var i = 0; i < doc.courseId.length; i++) {
Course.findById(doc.courseId[i], function(err, course) {
if (err) {
return console.log(err);
}
courseID.push(course.title);
if (++resolved === doc.courseId.length) {
res.send(JSON.stringify(courseID));
}
})
}
})
})
Let's say i need to constantly collecting some data from a lot of clients and in parallel running some complex loop that solving some stuff with this data. How can i do it? Should i just write this in my piece of code:
app.get('/', function(req, res) {
res.sendFile(__dirname + '/public/views/index0.html');
});
io.sockets.on('connection', function(socket) {
// SOME STUFF WITH THE SOCKET
socket.on('disconnect', function(data) {
//SOME OTHER STUFF
});
});
while(...) {
//THE LOOP STUFF
}
Or i need to use the setTimeout() and setInterval() functions? How can i do the loop on the server that runs in parallel with the callbacks' stuff?
Don’t use while for make it, this block a thread. setTimeout() will run only once. You need to use setInterval() function.
You can use the async module to handle the async operation with the callback or use promise to avoid callback.
Here how I handle a complex async for each operation, that might helpfull to you get idea handeling ayncs forach
var cond = { _schedule: schedule_id }; // find curse by schedule id
Course.find(cond, function (err, courses) {
if (err) {
callback({ "success": false, "message": "Not able to update" });
} else {
async.forEachLimit(courses, 1, function (course, coursesCallback) {
async.waterfall([
function (callback) {
var schedule_date = moment(change_data.date).format('YYYY-MM-DD') + "T00:00:00.000Z"
var Assignmentcond = {
assignment_schedule_order: {
$gte: schedule_date
},
_course: course._id,
_schedule: schedule_id,
_user: userid
};
Assignment.find(Assignmentcond)
.populate({
path: '_course',
})
.lean()
.sort({ assignment_schedule_order: 1 })
.exec(function (err, AssignmentList) {
if (err) {
callback(null, '');
} else {
//console.log("------------------AssignmentList---------------------------");
//console.log(AssignmentList);
async.forEachLimit(AssignmentList, 1, function (ThisAssignmentCell, ThisAssignmentCellCallback) {
async.waterfall([
function (callback) {
var SearchObj = items;
var lebelObject = {};
for (var i = 0, flag = 0, insert = 0; i < SearchObj.length; i++) {
if (SearchObj[i].date == ThisAssignmentCell.assignment_schedule_date) {
flag = 1;
}
if (flag == 1 && SearchObj[i].label != "") {
if (ThisAssignmentCell.day == SearchObj[i].day_index) {
insert = 1;
var lebelObject = SearchObj[i];
break;
}
}
}
callback(null, ThisAssignmentCell, lebelObject, insert);
},
function (ThisAssignmentCell, SearchObj, insert, callback) {
console.log('----------------------');
console.log('ThisAssignmentCell', ThisAssignmentCell);
console.log('SearchObj', SearchObj);
console.log('----------------------');
if (insert > 0) {
var query = { _id: ThisAssignmentCell._id },
fields = {
assignment_date: moment(SearchObj.date).format('MM/DD/YYYY'),
assignment_schedule_date: moment(SearchObj.date).format('YYYY-MM-DD'),
assignment_schedule_order: new Date(SearchObj.date),
day: SearchObj.day_index,
dayNum: SearchObj.weekday_num
},
options = { upsert: false };
Assignment.update(query, fields, options, function (err, affected) {
callback(null, '');
});
} else {
// var cond = { _id: ThisAssignmentCell._id};
// Assignment.remove(cond)
// .exec(function (err, cnt) {
// callback(null, '');
// });
}
}
], function (err, result) {
// result now equals 'done'
console.log('done')
ThisAssignmentCellCallback();
});
}, function (err) {
console.log("Assignment For Loop Completed");
callback(null, AssignmentList);
});
}
});
}
], function (err, result) {
// result now equals 'done'
console.log('done')
coursesCallback();
});
}, function (err) {
console.log("courses For Loop Completed");
});
}
});
So I am trying to get an array with a loop and the asynchronous nature of nodejs is killing me. Here is my code:
getDevices(userIDs, function(result) {
if (result) {
sendNotification(messageUser, messageText, result);
res.send("Success");
} else {
res.send("ERROR");
}
});
});
function getDevices(userIDs, callback) {
var userDevices = [];
var device = [];
for (var i = 0; i < userIDs.length; i++) {
searchRegisterDevices(userIDs[i], function(result) {
if (result) {
for (var j = 0; j < result.length; j++) {
device = {platform: result[j].platform, token: result[j].token};
userDevices.push(device);
}
} else {
console.log("ERROR");
}
});
}
callback(userDevices);
}
function searchRegisterDevices(userID, callback) {
MongoClient.connect(url, function(err, db) {
if (err) {
console.log(err);
} else {
console.log("We are connected");
}
var collection = db.collection('RegisteredDevices');
collection.find({userID: userID}).toArray(function (err, result) {
if (err) {
console.log("Error: " + err);
} else if (result.length) {
callback(result);
} else {
console.log('No document found');
}
db.close();
});
});
I first need to get all my devices out of my mongodb collection that match the ID in userIDs. SO userIDs is an array of IDs that are tied to devices in the collection. Once I get the device I need to get the device token out of the returned object.
So:
1) call getDevices passing an array of userIDs
2) call searchRegisterDevices with a device ID.
3) searchRegisterDevices returns an array of devices.
4) get the device token/s out of that array and push to userDevices array.
5) return userDevices array
6) call sendNotification with the array of userDevices
I know my issues, I just am having a hard time solving them
Instead of getting user device for each user you should get them using single query:
First: It will reduce number of calls
Second: It will save you to handle callbacks o/p.
For it use $in operator.
Change searchdevices method:
function searchRegisterDevices(userID, callback) {
MongoClient.connect(url, function(err, db) {
if (err) {
console.log(err);
} else {
console.log("We are connected");
}
var collection = db.collection('RegisteredDevices');
collection.find({
userID: {
$in: userIDs
}).toArray(function(err, result) {
if (err) {
console.log("Error: " + err);
} else if (result.length) {
callback(result);
} else {
console.log('No document found');
}
db.close();
});
});
}
It will return array of userdevices for passed userids.
I have the following function in one of my mongoose models:
UserSchema.methods.checkUsernameExists = function checkUsernameExists(req){
User.count({ username: req.body.username }, function(err, count){
if(err){
return console.error(err);
}
console.log(count); //Logs 2
});
}
Now when I log it, it gives the correct count. But if I return the count and try doing this, in one of my controllers it returns undefined.
var User = require("../models/user").User;
var user = new User();
exports.signup = function(req, res){
var count = user.checkUsernameExists(req);
console.log(test)
}
Any help would be appreciated thank you.
Remember that Node works asynchronously, meaning that you can't return a normal value from a function that executes an asynchronous function itself, like your checkUsernameExists does.
The most common way to deal with this is by passing a callback function which is called when the value is retrieved:
UserSchema.methods.checkUsernameExists = function checkUsernameExists(req, callback) {
User.count({ username: req.body.username }, callback);
};
This will pass the err and count variables that are the result of User.count as arguments to the callback function you supply. To use:
user.checkUsernameExists(req, function(err, count) {
if (err) {
console.error(err);
} else {
console.log('the count is', count);
}
});
To make the function to what it's name suggests, namely to 'return' a boolean to signify if a username already exists, you might use something like this:
UserSchema.methods.checkUsernameExists = function checkUsernameExists(req, callback) {
User.count({ username: req.body.username }, function(err, count) {
if (err) {
callback(err);
} else {
callback(null, count !== 0);
}
});
};