here is my code snippet.
I am trying to get details of my friends.friendsList is in reside within the user collection itself and there i am inserting users's id. So first i am fetching userId,then fetching each of their details.but my problem is i am not getting the values of friendsDetails outside the fetchDetailsfunction. I tried many time. i am new to node and sails js. i think its problem of asynchronous execution. How can i solve this problem?
getFriendsDetails:function(req,res){
var userId = req.param('id');
var friendsDetails=[];
User.findOne({
id: userId
}).exec(function(err,user){
var friendsIds=user.friends;
friendsIds.forEach(function(id){
User.findOne({
id: id
}).exec(function fetchDetails(err,Userdetails){
var obj={
id:Userdetails.id,
name:Userdetails.name,
pro_pic:Userdetails.profile_pic
}
friendsDetails.push(obj);
console.log(friendsDetails);//Here consoling pushed data correctly.
});
console.log(friendsDetails);//here i am getting null array
});
});
You can use mongoDB $in clause to get an array of friend documents.
getFriendsDetails:function(req,res){
var userId = req.param('id');
var friendsDetails=[];
User.findOne({
id: userId
}).exec(function(err,user){
var friendsIds = user.friends;
User.find({id : { $in : friendsIds}}).toArray(function(err, data){
console.log(data);
data.forEach(function(friendObj){
var obj={
id: friendObj.id,
name: friendObj.name,
pro_pic: friendObj.profile_pic
}
friendsDetails.push(obj);
});
console.log(friendsDetails);
});
});
}
Your variable is not in the same scope. Therefore, it is null outside the exec function. You are right, it is due to the async nature of javascript.
What you can do is pass a callback to the exec function and pass your variable in.
.exec(function(err, user, callback)
and then call it like this
callback(friendsDetails);
Your second console.log won't wait until your function is finished because it is async.
Related
I have a sever connected to a mongodb database. When I add a first level data and then save that, it works.
For example :
// this works fine
router.post('/user/addsomedata', async (req,res)=>{
try {
const user = await User.findOne({email : req.body.email})
user.username = req.body.username
await user.save()
res.send()
} catch(e) {
res.status(404).send(e)
}
})
BUT if I try to save the object with deeper level data, it's not getting saved. I guess the update is not detected and hence the user didn't get replaced.
Example :
router.post('/user/addtask', auth ,async (req,res)=>{
const task = new Task({
name : req.body.name,
timing : new Date(),
state : false,
})
try {
const day = await req.user.days.find((day)=> day.day == req.body.day)
// day is found with no problem
req.user.days[req.user.days.indexOf(day)].tasks.push(task)
// console.log(req.user) returns exactly the expected results
await req.user.save(function(error,res){
console.log(res)
// console.log(res) returns exactly the expected results with the data filled
// and the tasks array is populated
// but on the database there is nothing
})
res.status(201).send(req.user)
} catch(e) {
res.status(400).send(e)
}
})
So I get the tasks array populated on the console even after the save callback but nothing on the db image showing empty tasks array
You're working on the user from the request, while you should first find the user from the DB like in your first example (User.findOne) and then update and save that model.
Use .lean() with your find queries whenever you are about to update the results returned by mongoose. Mongoose by default return instance objects which are immutable by nature. lean() method with find returns normal js objects which can be modified/updated.
eg. of using lean()
const user = await User.findOne({email : req.body.email}).lean();
You can read more about lean here
Hope this helps :)
It would be awesome if anyone can give me a helping hand with this problem.
I am currently running into a problem where calling this express route is giving me the error show below a RangeError: Maximum call stack size exceeded
It only seems to happen when I include the res.json(users[user]); function. If I substitute this function with res.send('Done'); the function runs fine.
I have tried setTimeout, setImmediate and process.nextTick, all giving me the same error, just about.
I'm sorta hoping it's a silly mistake on my end.
//Send Friend Request
router.post('/send/friend/request', auth, function(req, res, next){
var query = User.find({$or: [
{username: req.body.username},
{username: req.payload.username}
]})
.select(' username friends notifications');
query.exec(function(err, users){
if(err) return next(err);
if(users.length < 2) console.log('/send/friend/request - There was an error in accessing the users from the db');
else{
//Identify users in array
var user = users.findIndex(function(element, index){
if(element.username === req.payload.username)
return element;
});
var requestUser = (user + 1) % 2;
//addFriends
users[user].friends.push({
user: users[requestUser],
});
users[requestUser].friends.push({
user: users[user],
sent: false
});
//notifications
users[user].notifications.push({
user: users[requestUser],
type: 0,
summary: "Your friend request has been sent to " + req.body.username +".",
status: "Pending..."
});
users[requestUser].notifications.push({
user: users[user],
type: 1,
summary: "You have a new friend request from " + req.payload.username + ".",
status: "Pending..."
});
users[requestUser].save(function(err){
if(err) return next(err);
});
users[user].save(function(err){
if(err) return next(err);
});
//Here lies the culprit
res.json(users[user]);
}
});
});
You're creating a circular reference between users[user] and users[requestUser].
Here's an example that demonstrates the problem:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = Schema({ friends : [] });
const User = mongoose.model('User', userSchema);
let user1 = new User();
let user2 = new User();
user1.friends.push(user2);
user2.friends.push(user1); // the circular reference is created here
console.log( JSON.stringify(user1) );
JSON.stringify() will thrown a RangeError because of that circular reference.
You may need to rethink your schema, as I don't think you can use subdocs for your purpose. Instead, you should use "population" to store references between documents.
When you change the schema above to the following, it won't throw:
const userSchema = Schema({
friends : [ { type : Schema.Types.ObjectId, ref : 'User' } ]
});
EDIT: if you're using a complex object as array contents, even with population, you should push _id values and not documents:
users[user].friends.push({
user: users[requestUser]._id,
});
Perhaps
If user contains reference by itself then res.json fail. Because req.json attempt to explode unlimited object. To avoid this problem you can:
Specify user fields to send
res.send(JSON.stringify(user, ['name', 'age', ...]))
Define reference as not enumerable property
Object.defineProperty(user, 'your-ref', {enumerable: false});
Definitely object users is not JSON.Its POJO object with additional mongo properties which contain recursive referance to __proto__ property.
Do either serialization and then de-serialize on client side again.
OR extract the property in new Object literal then pass to it resp.json()
I just start to use node.js with express and mongoose, and I have a stupid question...
Somewhere in my routes.js file, I have the following section :
// DASHBOARD SECTION, GET MY GROUPS
app.get('/dashboard', isLoggedIn, function(req, res) {
var Group = require('../app/models/group'); //adding mongoose Schema
Group.find({"groupDetails.userId" : req.user._id})
.exec(function(err, myGroups) {
if (err)
res.send(err);
var myGroups = myGroups;
//console.log("myGroups: " + myGroups); // check with "heroku logs"
res.render('dashboard.ejs', {
user : req.user,
myGroups : myGroups
});
});
});
This code works. When someone browse the dashboard page, I receive "myGroups" which is an array with all the groups for the current logged in user.
Now, here is my question :
Actually when someone browse the dashboard page, I would like to make a second query (based on the exact same pattern) to get all groups and all files for the current logged in user.
Then I will send "user", "myGroups" and "myFiles" to the dashboard page.
How can I do that ?
I tried several things with no result so far... I think I'm a little bit lost in node.js callback functions :D
Thanks a lot for your help.
You have two options here:
1) deal with callback hell (callback inside callback inside...) to retrieve 3 sets of data. This way is least elegant and efficient
2) Use a library that will do the job asynchronously and have one callback when all the data is retrieved, you can use async library which is just awesome. In this case you will have just one callback in which you can access all the data you have fetched.
Here's what you can do with async in your case:
var async = require('async');
..........
app.get('/dashboard', isLoggedIn, function(req, res) {
var Group = require('../app/models/group'); //adding mongoose Schema
var User = require('../app/models/user'); //adding mongoose Schema
var Files = require('../app/models/files'); //adding mongoose Schema
async.parallel({
groups: function(callback){
Group.find(...).exec(callback);
},
users: function(callback){
Users.find(...).exec(callback);
},
files: function(callback){
Files.find(...).exec(callback);
}
}, function(err, results) {
if (err)
res.send(err);
var groups = results.groups;
var users = results.users;
var files = results.files;
res.render('dashboard.ejs', {
user : req.user,
myGroups : groups,
users: users,
files: files
});
});
});
I am using mean.io to make sports event management system.I am trying to update a model Player with rest api and it throws this error from mongoose:
{ _id: 5411c79895d600440c698fa1,
email: 'abc#bcd.com',
name: 'James Bond',
username: 'james.bond',
games: [ 5411bd54786cfe2420f1e27a ],
teams: [],
roles: [ 'authenticated' ] }
[TypeError: Cannot read property '_id' of undefined]
PUT /api/players/5411c79895d600440c698fa1 500 199.402 ms - 36
I also tried to delete the _id proterty from player but it doesnot works either.
the method i have used to update the model player is :
exports.update = function(req, res) {
var player = req.player;
player = _.extend(player, req.body);
console.log(player);
Player.findOneAndUpdate(req.params.playerId, player,{new:true}, function (err, updatedPlayer) {
console.log(err);
if (err) {
return res.status(500).json({
error: 'Cannot update the player'
});
}
res.json(updatedPlayer);
});
And also if i used the model.save method provided in the default package article of mean.io, it shows another error. I have extended the user model in player package app.js file. So whenever I try to update one field, the field that I have declared in app.js are required and the path required error from mongoose is thrown.
You have two issues in your update request.
First, the findOneAndUpdate expects a dict as the query and not just the id, so you should give it {_id: req.params.playerId} instead.
Second, passing a mongoose object as the update data is risky, instead you should convert it to a dict like this var _player = player.toObject() and then have _player be passed to the update request. Remember that you need to remove the _id param of_player because you can't change the _id of a document. Before doing the update just do delete _player._id and you should be fine. Also, new is set to true by default so you won't need the options dict.
Here is your working code:
var player = _.extend(req.player, req.body);
var _player = player.toObject();
delete _player._id;
var query = {_id: req.params.playerId};
Player.findOneAndUpdate(query, _player, function (err, updatedPlayer) {
...
});
But in this case you shouldn't even have to do the first operation. Since you just want to update the data of req.body you can do:
var query = {_id: req.params.playerId};
var data = {
$set: req.body,
};
Player.findOneAndUpdate(query, data, function (err, updatedPlayer) {
...
});
This will update all the fields of the player matching the query with the values from req.body.
I'm doing a simple update when my node.js app receives a certain POST request. This is my code:
app.post('/comment', function (req,res) {
var params = req.body;
BlogPost.update({"title": params.title}, {$push: { comments: {author : params.author, content: params.content, date: new Date().toUTCString()}}});
res.redirect('back');
});
where BlogPost is a mongoose Model. (This model works when querying for documents).
Now the problem is, when I do subsequent queries, nothing happens. For example, running the above code for a document with "title" "aaa" (which is supposed to push an object to the array "comments", querying for that document with title "aaa" returns something like
{ _id: 51954d4663aa986aa93a734f,
title: 'aaa',
comments: [] }
Anything I'm doing really wrong?
You should add a callback to get the error message.
I was having a similar issue and simply adding the callback, everything was working fine, even with an empty callback.
Try:
app.post('/comment', function (req,res) {
var params = req.body;
BlogPost.update({"title": params.title}, {$push: { comments: {author : params.author, content: params.content, date: new Date().toUTCString()}}},function(error){console.log(error);});
res.redirect('back');
});
This is such strange behavior by mongoose. I also had this and since update() is deprecated I used updateOne() instead, but it too only works when adding the callback function.