I'm having an issue with what I believe is the scope of a variable in mongoose. My code is this:
var blogUserId;
blogs.forEach(function(blog, index) {
User.findOne({'username': blog.username}, function(err, user) {
blogUserId = user._id;
console.log(blogUserId);
});
console.log(blogUserId);
Blog.find({'title': blog.title}, function(err, blogs) {
if (!err && !blogs.length) {
console.log(blogUserId);
Blog.create({title: blog.title, author: blogUserId, body: blog.body, hidden: blog.hidden});
}
if (err) {
console.log(err);
}
});
});
This is part of a seed file just for development, but I'm rather confused why it wouldn't be working. blogs is just an array of objects to load into the collection. I've searched on all similar answers but I haven't found a correct answer that would explain this.
Your blogUserId is not set when your Blog.find() is called. You'd have to nest it differently, something like this:
var blogUserId;
blogs.forEach(function(blog, index) {
User.findOne({'username': blog.username}, function(err, user) {
blogUserId = user._id;
console.log(blogUserId);
Blog.find({'title': blog.title}, function(err, blogs) {
if (!err && !blogs.length) {
console.log(blogUserId);
Blog.create({title: blog.title, author: blogUserId, body: blog.body, hidden: blog.hidden});
}
if (err) {
console.log(err);
}
});
});
});
I haven't tested it so I'm not sure that there are no other errors in your code but was definitely a problem that you were calling Blog.find that expected the blogUserId to be set possibly before it was set in the User.findOne callback.
It can be written in a more readable way with named callbacks.
When working in Node you need to keep in mind that you are working in an asynchronous environment.
Related
I know that it is possible to do this, but my question is whether there is a good reason not to do it, or is there a better way? I feel like it could make the code easier to follow and potentially make testing easier, but that's as far as I've gotten.
Example Code:
postSchema.methods.publishPost = function(data, cb) {
var self = this,
options = options || {};
mongoose.model('Version').findById(data.version).exec(function(err, version) {
if (err) return cb(err);
mongoose.model('Translation').findTranslatedVersion(version, function(err, translation) {
if(err) return cb(err);
publisher.publish(self, translation ? translation : version, function(err, published) {
if (err) return cb(err);
self.save(published);
});
});
});
}
I am pretty new to Node.js or Javascript in general when it comes to serverside stuff. Currently I am tring to validate some of the user input and set default values if something is wrong. Now if I run my validation the json object appears in the database befor my validation is completed.
The way I am doing the validation isnt maybe the best right now but if someone can explain me the behavior, I am pretty sure i can understand Javascript alot better in the future.
Is there a better way of doing validation (without mongoose or other ODM modules) with callbacks, middleware or should I use some async module?
Here is my code:
module.exports = function(app, express, todoDB, listDB, statusDB) {
var moment = require('moment');
var todoRouter = express.Router();
todoRouter.post('/', function(req, res, next) {
console.log('1');
if (!(moment(req.body.createDate).isValid())) {
req.body.createDate = moment().format("DD-MM-YYYY HH:mm:ss");
}
else {
req.body.createDate = moment(req.body.createDate).format("DD-MM-YYYY HH:mm:ss");
}
console.log('2');
if (req.body.list_id == '') {
listDB.findOne({list: 'Neu'}, function(reqdb, docs) {
if (docs == null) {
listDB.insert({list: 'Neu', index: 1});
listDB.findOne({list: 'Neu'}, function(reqdb, docs) {
console.log('AnlageListID');
console.log(docs._id);
req.body.list_id = docs._id;
});
}
else {
console.log('BestehendeListID');
console.log(docs._id);
req.body.list_id = docs._id;
}
});
}
console.log('3');
if (req.body.status_id == '') {
statusDB.findOne({status: 'offen'}, function(reqdb, docs) {
if (docs == null) {
statusDB.insert({status: 'offen', index: 1});
statusDB.findOne({status: 'offen'}, function(reqdb, docs) {
console.log('AnlageStatusID');
console.log(docs._id);
req.body.status_id = docs._id;
});
}
else {
console.log('BestehendeStatusID');
console.log(docs._id)
req.body.status_id = docs._id;
}
});
}
console.log('4');
console.log('StatusID');
console.log(req.body.status_id);
console.log('ListID');
console.log(req.body.list_id);
todoDB.insert({
todo: req.body.todo,
createDate: req.body.createDate,
endDate: req.body.endDate,
discription: req.body.discription,
comment: req.body.comment,
list_id: req.body.list_id,
priority_id: req.body.priority_id,
section_id: req.body.section_id,
user_id: req.body.user_id,
status_id: req.body.status_id,
company_id: req.body.company_id
});
res.json({message: 'TODO erfolgreich hinzugefĆ¼gt!'});
});
return todoRouter;
};
... and this is the ouput:
1
2
3
4
StatusID
ListID
POST /api/todos 200 76.136 ms - 44
BestehendeListID
M3Xh46VjVjaTFoCM
BestehendeStatusID
48v80B4fbO87c8um
PS: Its a small "project" just for me learing the MEAN Stack so I am using neDB.
If I understand correctly you try to sequentially execute a number of asynchronous calls and introduce checks in the code to validate if previous asynchronous calls have completed. This is not going to work in a general case because your checks may be processed before the asynchronous call goes through. It might work now and then just by chance, but I would not expect even that.
There are standard mechanisms for that. One of them is using promises, another one using async and yet another one if stacking up all callbacks one into another. Below I will demonstrate how to address the problem using async, but the same general idea applies to using promises. Check the async project on Github then the following part-solution will become clear:
var async = require("async")
async.waterfall([
function(next) {
listDB.findOne({list: 'Neu'}, next); // quits on error
},
function(doc, next) {
if (doc) {
return next(null, doc._id);
}
statusDB.insert({status: 'offen', index: 1}, function(err) {
if (err) return next(err); // quit on error
statusDB.findOne({status: 'offen'}, function(err, doc) {
next(err, doc._id); // quits on error
});
});
},
function(id, next) {
// do next step and so on
next();
}
],
// this is the exit function: it will get called whenever an error
// is passed to any of the `next` callbacks or when the last
// function in the waterfall series calls its `next` callback (with
// or without an error)
function(err) {
console.error("Error processing:", err)
});
User.find().exec(function (err, users) {
if (err){
callback(err);
} else {
callback(users);
}
});
User.find(function (err, users) {
if (err) {
callback (err);
} else {
callback(users);
}
});
What is the benefit of using the top code? both seem to work equally well
They are identical and there's no benefit in your example
When you do not pass a callback to find function it won't execute but instead returns a query then you need to use exec()
var query = User.find();
now you can add some more criteria
query.where({age: 15});
and some more
query.select({name:1}); // or {firstname:1, lastname:1} etc.
now you've built up your query so to get the results you need to execute it.
query.exec(function(err, users){
});
But you can also do this like
User.find({age:15}, {name:1}, function(err, users){
});
Above is identical to
User.find({age:15}, {name:1}).exec(function(err, users){
});
since there's no callback in find function it will return query which means no results, exec will give you the results
I am learning Node.js; due to asynchronous of Node.js I am facing an issue:
domain.User.find({userName: new RegExp(findtext, 'i')}).sort('-created').skip(skip).limit(limit)
.exec(function(err, result) {
for(var i=0;i<result.length;i++){
console.log("result is ",result[i].id);
var camera=null;
domain.Cameras.count({"userId": result[i].id}, function (err, cameraCount) {
if(result.length-1==i){
configurationHolder.ResponseUtil.responseHandler(res, result, "User List ", false, 200);
}
})
}
})
I want to use result in Cameras callback but it is empty array here, so is there anyway to get it?
And this code is asynchronous, is it possible if we make a complete function synchronous?
#jmingov is right. You should make use of the async module to execute parallel requests to get the counts for each user returned in the User.find query.
Here's a flow for demonstration:
var Async = require('async'); //At the top of your js file.
domain.User.find({userName: new RegExp(findtext, 'i')}).sort('-created').skip(skip).limit(limit)
.exec(function(err, result) {
var cameraCountFunctions = [];
result.forEach(function(user) {
if (user && user.id)
{
console.log("result is ", user.id);
var camera=null; //What is this for?
cameraCountFunctions.push( function(callback) {
domain.Cameras.count({"userId": user.id}, function (err, cameraCount) {
if (err) return callback(err);
callback(null, cameraCount);
});
});
}
})
Async.parallel(cameraCountFunctions, function (err, cameraCounts) {
console.log(err, cameraCounts);
//CameraCounts is an array with the counts for each user.
//Evaluate and return the results here.
});
});
Try to do async programing allways when doing node.js, this is a must. Or youll end with big performance problems.
Check this module: https://github.com/caolan/async it can help.
Here is the trouble in your code:
domain.Cameras.count({
"userId": result[i].id
}, function(err, cameraCount) {
// the fn() used in the callback has 'cameraCount' as argument so
// mongoose will store the results there.
if (cameraCount.length - 1 == i) { // here is the problem
// result isnt there it should be named 'cameraCount'
configurationHolder.ResponseUtil.responseHandler(res, cameraCount, "User List ", false, 200);
}
});
I am using filterSeries of the async npm but when I call the truthy next on the object, for some reason it only passes the user and not the parts trying to be taken out of the query...
If you notice what is wrong with my code or have a more efficient way of going about this, because I also, heard that looping through each users and calling a query is a bad idea instead to do a $in or something but not sure how.
The main thing is I want to combine both documents and feed it back as data...
Here is the code:
exports.searchContactPost = function(req, res) {
if(req.body.searchContacts === '') { res.send('Oops you searching for nothing, well here is nothing!'); };
async.waterfall([
function(callback) {
User.find({$or:[
{firstName: req.body.searchContacts.toLowerCase()},
{lastName: req.body.searchContacts.toLowerCase()},
{email: req.body.searchContacts.toLowerCase()}]
}, function(err, users) {
if(err || users.length === 0) { res.send(err);}
callback(null, users)
});
},
function(users, callback) {
async.filterSeries(users, function(user, next) {
console.log(user);
Friend.findOne({userId: req.signedCookies.userid, friend_id: user}, function(err, friend) {
if(err) {
console.log("houston we got a problem.")
}
var object = {'fav': friend.favorites, 'notes': friend.notes, 'labels': friend.labels, 'user': user, 'status':friend.friend_status};
console.log(friend);
next(object.status === 3);
})
}, function(friendResults){
console.log(friendResults);
callback(null, friendResults);
});
}
],
function(err, results) {
res.render('contactListResults', {title: 'Weblio', friendsFound: results});
});
};
The async filter function takes an array of items and filters out items from that array based on a true or false callback. Therefore you will get back a subset of the original array passed into the filter. Which in this case is users, I believe your trying to build up a friend object and return it, which won't work. What you should do instead is just query the database for all friends of the appropriate status instead of using a filter.