i'm new with node and mongoose.
I do multiple queries to find my datas so I use async to populate an array and then send it back the the client.
I try to get all the stats from the games I found.
Here is my Schema : [Team] 1 ---> N [Game] 1 ---> N [Stat]
var json = []; // The variable I will send back
async.waterfall([
function( team, next ) { // Find all games from a team (with its id)
dbGame.find({ team_id: req.params._id }).exec( next );
},
function( games, next ) {
for (key in games) { // For all games, finds their stats
dbStat.find({ game_id: games[key]._id }).exec(function(err, stats) {
json.push({
_id : games[key]._id,
stats : stats
}); // The json I need to return
});
if ( Number(key) === Number(games.length -1) ) {
next(json);
}
}
}
], function () {
res.status(200);
res.json(json);
});
The variable json send is always empty because of the asynchronous and I don't know how to have the full one.
update #1
Ok it's cool it's working.
But just a trouble, all the stats are in all json in the object :
Juste a problem : The stats from all games are stored in all json.
[{
_id:"57cb15f73945ac9808fe5422",
stats:Array[13]
}, {
_id:"57ff76fd78ca91a17b25dd1b",
stats :Array[13]
}]
But I think i can sort then. Here's my code now :
async.waterfall([
function(next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next);
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function(err, json) {
jsonResponse(res, 200, json);
});
Try below:
var json = []; // The variable I will send back
async.waterfall([
function(team, next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next);
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function() {
res.status(200);
res.json(json);
});
Comment if it has any issue or I have missed something as I have not tested it.
You are not passing the results of the first find to the next step of waterfall, and when you call next(json); you are passing json as error, as the first argument is an error. Try:
async.waterfall([
function(team, next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next(err, games));
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function(err, json) {
res.status(200);
res.json(json);
});
Related
my Schema :
var schoolSchema = new mongoose.Schema({
name: String,
classes:[
{
c_name:String,
c_strn:String,
students:[
{s_name:String,
s_roll:String,
s_address:String
}
]
}
],
});
var school = mongoose.model('school',schoolSchema);
Sample Doc :
var sainik = new school({name:'sain',
classes:
[
{
c_name:'1(one)',
c_strn:'100',
students:[
{
s_name:"Leo",
s_roll:"17",
s_address:"strt24",
},
{
s_name:"Kyle",
s_roll:"18",
s_address:"strt24",
}
]//array of students
}
]//array of classes
});
sainik.save().then(()=>console.log("Save it"));
Code :
app.get('/home/:id/:cid',function(req, res){
school.find().exec(function(err,data){
if (err){
console.log(err);
}
else{
console.log(data);
res.render("classDet",{data:data});
}
})
});
Here i need to know how to get access to the individual classes using the class id and to print the array of students as list in the "classDet"
Basically I am creating a school management system in which there will be many classes and inside that classes will be list of students.
I want to print all the students in each classes only when the parent class is access.
You can try either one of these :
app.get('/home/:id/:cid', function (req, res) {
school.find({ id: id, 'classes.c_id': cid }, { _id: 0, 'classes.c_id.$.students': 1 }).lean().exec(function (err, data) {
if (err) {
console.log(err);
}
else {
console.log(data);
res.render("classDet", { data: data });
}
})
})
Test : MongoDB-Playground
app.get('/home/:id/:cid', function (req, res) {
school.aggregate([{ $match: { id: id } }, { $unwind: '$classes' }, { $match: { 'classes.c_id': cid } }, { $project: { _id: 0, students: '$classes.students' } }]).exec(function (err, data) {
if (err) {
console.log(err);
}
else {
console.log(data);
res.render("classDet", { data: data });
}
})
})
Test : MongoDB-Playground
Note : There will be a slight difference in both results, you can test it & choose one out of those, Also keep in mind if in case id from request is being searched against _id in DB then you need to convert id string to ObjectId() before using it in queries. Also we're using .lean() in .find() to convert mongoose docs into Js objects in order to access keys in data while working on it in code.
I have a PUT request that I'm trying to have hit the backend, but for some reason, it never reaches it. What's odd is the if(req.body.bidderId){} hits no problem, but not the if(req.body.watchingGroup){}
The watching angular service uses identical code to the bidderId so I don't know what's different between the two where only one would reach the endpoint? Whats wrong with the addToWatchList call? I did testing and both console.log statements in code block return the correct value. So the data is ready to be passes, but is never received.
console.log("MADE IT TO LISTINGS BACKEND");
never outputs for watchingGroup scenario
watching.service.ts
addToWatchList(id: string, watchingGroup: string[]) {
const watching: Watching = {
id: id,
watchingGroup: watchingGroup
};
console.log("Here are the values being passed to backend");
console.log(watching.id);
console.log(watching.watchingGroup);
console.log(watching);
return this.http.put(`http://localhost:3000/api/listings/${watching.id}`, watching,
);
}
app.js
app.put("/api/listings/:id", (req, res) => {
console.log("MADE IT TO LISTINGS BACKEND");
if (req.body.biddingGroup) {
console.log("bidding has been received");
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
currentBid: req.body.currentBid,
lastBidTimeStamp: Date.now(),
bidderId: req.body.bidderId,
auctionEndDateTime: req.body.auctionEndDateTime,
biddingGroup: req.body.biddingGroup,
lastBidTimeStamp: req.body.lastBidTimeStamp
},
function(err, docs) {
if (err) res.json(err);
else {
console.log(docs);
}
}
);
}
if (req.body.watchingGroup) {
console.log("watching has been received");
Post.findByIdAndUpdate(
{ _id: req.params.id },
{
watchingGroup: req.body.watchingGroup
},
function(err, docs) {
if (err) res.json(err);
else {
console.log(docs);
}
}
);
}
});
addToWatchList
addToWatchList(
auctionId: string,
watchingGroup: string[]
) {
this.watchItStatus = true;
this.userId = localStorage.getItem("userId: ");
var unique = watchingGroup.filter(function(elem, index, self) {
return index === self.indexOf(elem);
});
this.uniqueResult = unique;
watchingGroup.push(this.userId);
this.watchListService.addToWatchList(auctionId, this.uniqueResult);
}
As i suspected you're not subscribing to it. It's weird but you need to subscribe to it.
this.watchListService.addToWatchList(auctionId, this.uniqueResult).subscribe(
(res) => {
// Handle success response
console.log("SUCCESS");
},
(err) => {
// Handle error response
console.log("ERROR");
}
);
Trying to aggregate some data from a list of MongoDB collection and when aggregation is done over all the collections, I want to return the complete aggregation of all the suitable collections with the name and aggregated value as a JSON object back.
New to node.js programming, hence not able to solve this trivial problem. any help appreciated
Code:
app.get('/tester', (req, res) => {
var AggArr = [];
db.listCollections().toArray(function (err, collInfos) {
async.forEachOf(collInfos, (value, key, callback) => {
aggregate(value);
}, err => {
if (err)
console.error(err.message);
// configs is now a map of JSON data
res.send(JSON.stringify(AggArr));
});
});
function aggregate(collname) {
if (collname.name.includes("_tps")) {
db.collection(collname.name).aggregate([{
$match: {
$and: [{
TIME: {
$gte: new Date("2017-01-01T00:00:00Z")
}
}, {
TIME: {
$lte: new Date("2017-12-31T23:59:59Z")
}
}
]
}
}, {
$group: {
_id: collname.name,
sum: {
$sum: "$TPS"
}
}
}
]).toArray(
(err, result) => {
if (err) {
return console.log(err)
} else {
if (Object.keys(result).length === 0) {}
else {
console.log(result[0]);
AggArr.push(result[0]);
}
}
})
}
}
})
I'm new to Node.js programming and stuck at this trivial problem, any help is appreciated.
The above workflow can be refactored to the following
const pipeline = collectionName => (
[
{ '$match': {
'TIME': {
'$gte': new Date("2017-01-01T00:00:00Z"),
'$lte': new Date("2017-12-31T23:59:59Z")
}
} },
{ '$group': {
'_id': collectionName,
'sum': { '$sum': "$TPS" }
} }
]
);
db.listCollections().toArray((err, collInfos) => {
const agg = collInfos
.filter(col => col.name.includes("_tps"))
.map(col => db.collection(col.name).aggregate(pipeline(col.name)).toArray());
Promise.all(agg).then(data => {
const results = data.map(d => d[0]);
res.send(JSON.stringify(results));
});
});
When you return a response of any query it should be like below. It will gives response in JSON format.
app.get('/tester', (req, res) => {
//DB Operaations
if (success) {
res.status(200).json(success);
} else {
res.status(500).json({
message: 'Something went wrong'
});
}
});
I am getting a Callback was already called error while trying to make asynchronous queries using the MEAN stack. I need the second callback to only trigger after the nested query has been completed (as per the comments in the code). Why am I getting this error?
Example of the route:
router.route('/teams/:user_id').get(function (req, res) {
TeamProfile.find({
Members : {
$in : [req.params.user_id]
}
}).exec(function (err, teamProfiles) {
var asyncTasks = [];
teamProfiles.forEach(function (teamProfile) {
asyncTasks.push(function (callback) {
UserProfile.find({
UserID : {
$in : teamProfile.Members.map(function (id) {
return id;
})
}
}, function (err, userProfiles) {
teamProfile.Members = userProfiles;
callback();
})
});
});
teamProfiles.forEach(function (teamProfile) {
asyncTasks.push(function (callback) {
Draft.find({
_id : {
$in : teamProfile.Drafts.map(function (id) {
return id;
})
}
}, function (err, drafts) {
teamProfile.Drafts = drafts;
drafts.forEach(function (draft) {
Comment.find({
_id : {
$in : draft.Comments.map(function (id) {
return id;
})
}
}).exec(function (err, comments) {
draft.Comments = comments;
callback();
//This is where the callback should be called
//Throws Error: Callback was already called.
})
})
})
});
});
async.parallel(asyncTasks, function () {
res.json(teamProfiles)
});
});
})
I am using async.parallel() to perform all the queries. I am very new to all of this so please excuse some beginner mistakes.
You are iterating over drafts synchronously and calling async's callback function on the first item. Getting an error when you try to call it again with the second item is expected behaviour.
You should instead call the done callback once you have finished all the draft queries, not for each one. Since you are using async, you could nest another async.each to handle this:
router.route('/teams/:user_id').get(function (req, res) {
TeamProfile.find({
Members : {
$in : [req.params.user_id]
}
}).exec(function (err, teamProfiles) {
var asyncTasks = [];
teamProfiles.forEach(function (teamProfile) {
asyncTasks.push(function (callback) {
UserProfile.find({
UserID : {
$in : teamProfile.Members.map(function (id) {
return id;
})
}
}, function (err, userProfiles) {
teamProfile.Members = userProfiles;
callback();
});
});
});
teamProfiles.forEach(function (teamProfile) {
asyncTasks.push(function (callback) {
Draft.find({
_id : {
$in : teamProfile.Drafts.map(function (id) {
return id;
})
}
}, function (err, drafts) {
teamProfile.Drafts = drafts;
async.each(drafts, function(draft, draftCallback){
Comment.find({
_id : {
$in : draft.Comments.map(function (id) {
return id;
})
}
}).exec(function (err, comments) {
draft.Comments = comments;
draftCallback();
});
}, function(err){
callback();
});
});
});
});
async.parallel(asyncTasks, function () {
res.json(teamProfiles)
});
});
});
I am trying to get all course from the database and then add course_has_users if it exist.
The code works until I try to JSON encode it. Then I lose course_has_users when my angular front-end receives it.
Course.findAll({include: [
{model:Course_has_material},
{model:Course_has_competence},
{model:Organization},
{model:Module_type},
{model:Category},
{model:User, as:'mentor'}
],
where: {organization_id: user.organization_id}
}).then(function (courses) {
async.each(courses, function (course, callback) {
Course_has_user.findAll({
where: {user_id: user.user_id, course_id:course.id}
}, {}).then(function (course_has_user) {
course.course_has_users = course_has_user;
callback();
})
}, function (err) {
onSuccess(courses);
});
});
Route
.get(function (req, res) {
var course = Course.build();
course.retrieveAll(req.user, function (courses) {
if (courses) {
res.json(courses);
} else {
res.status(401).send("Courses not found");
}
}, function (error) {
res.send("Courses not found");
});
})
async.each will just iterate through it.
Use async.map and return course after setting course has users on it.
It should just work then. ;)
Course.findAll({include: [
{model:Course_has_material},
{model:Course_has_competence},
{model:Organization},
{model:Module_type},
{model:Category},
{model:User, as:'mentor'}
],
where: {organization_id: user.organization_id}
}).then(function (courses) {
async.map(courses, function (course, callback) {
Course_has_user.findAll({
where: {user_id: user.user_id, course_id:course.id}
}, {}).then(function (course_has_user) {
course.course_has_users = course_has_user;
callback(null, course);
})
}, function (err, _courses) {
// Note that we use the results passed back by async here!
onSuccess(_courses);
});
});
So you could also do, to simplify things a bit
Course.findAll({include: [
{model:Course_has_material},
{model:Course_has_competence},
{model:Organization},
{model:Module_type},
{model:Category},
{model:User, as:'mentor'}
],
where: {organization_id: user.organization_id}
})
.map(function (course) {
return Course_has_user.findAll({
where: {user_id: user.user_id, course_id:course.id}
}, {})
.then(function (course_has_user) {
course.course_has_users = course_has_user;
return course;
})
})
.then(onSuccess);
});
The problem was with Sequelize and fixed the issue by changing it's toJSON method and using async.map
Haven't tested with async.each but should work without map
instanceMethods: {
toJSON: function () {
var json = this.values;
json.course_has_users = this.course_has_users;
return json;
},
};
Retrieve method
retrieveMyCourses: function (user, onSuccess, onError) {
Course.findAll({include: [
{model:Course_has_material},
{model:Course_has_competence},
{model:Organization},
{model:Module_type},
{model:Category},
{model:User, as:'mentor'}
],
where: {organization_id: user.organization_id}
}).
then(function (courses) {
async.map(courses, function (course, callback) {
Course_has_user.findAll({
where: {user_id: user.user_id, course_id:course.id}
}, {}).then(function (course_has_user) {
course.course_has_users = course_has_user;
callback(null, course);
})
}, function (err, _courses) {
var test = JSON.parse(JSON.stringify(_courses));
onSuccess(_courses);
});
});
},
Route
router.route('/api/myCourses')
// Get all courses
.get(function (req, res) {
var course = Course.build();
course.retrieveMyCourses(req.user, function (courses) {
if (courses) {
res.json(courses);
} else {
res.status(401).send("Courses not found");
}
}, function (error) {
res.send("Courses not found");
});
});
Sequelize issue:
https://github.com/sequelize/sequelize/issues/549