Reputation: 2973
I have a query that fetches the top 5 people for a leaderboard. In robomongo this query works fine.
When I do something like
var leaderboard = User.find({points: {$exists: true}}, {
"firstname": 1,
"lastname": 1,
"points": 1
}).sort({points : -1}).limit(5)
console.log('leaderboard');
I get a lot of meaningless json with [object] almost everywhere.
How would I execute this query for use with mongoose + express so I can pass through to the view as an array of
firstname, lastname, points
So I can loop it through in the view?
My complete code is
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
User.find({}, function(err, docs) {
// console.log(docs);
});
// Get total points after submit
var leaderboard = User.find({points: {$exists: true}}, {
"firstname": 1,
"lastname": 1,
"points": 1
}).sort({points : -1}).limit(5).toArray();
console.log(leaderboard);
User.find({
points: {
$exists: true
}
}, function(err, docs) {
if(err){
console.log(err);
//do error handling
}
//if no error, get the count and render it
var count = 0;
for (var i = 0; i < docs.length; i++) {
count += docs[i].points;
}
var totalpoints = count;
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: totalpoints
});
});
} else {
// not logged in
return res.redirect('/login');
}
});
Upvotes: 0
Views: 203
Reputation: 50426
So you are really only returning a cursor here and not executing the query. You can of course always nest the queries, but you can be a bit nicer and use async.waterfall to avoid the indenting mess.
Also I would use .aggregate()
rather than looping all the documents just to get a total. And mongoose automatically converts results to an array, so the .toArray()
is not required here:
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
async.waterfall(
[
function(callback) {
User.find(
{ "points": { "$exists": true } },
{
"firstname": 1,
"lastname": 1,
"points": 1
}
).sort({points : -1}).limit(5).exec(callback);
},
function(leaderboard,callback) {
User.aggregate(
[
{ "$match": { "points": { "$exists": true } }},
{ "$group": {
"_id": null,
"totalpoints": { "$sum": "$points" }
}}
],
function(err,result) {
callback(err,result,leaderboard)
}
);
}
],
function(err,result,leaderboard) {
if (err) {
console.log(err);
//do error handling
} else {
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: result[0].totalpoints,
leaderboard: leaderboard
});
}
}
);
} else {
// not logged in
return res.redirect('/login');
}
});
So you get your leaderboard
result and just put it in the response, much as is done in the example here.
An alternate approach is using async.parallel, since you don't need the output of the first query within the second. In that case the results of both queries are sent to the callback at the end, much like above. Again you just use the results in your final response.
app.get('/dashboard', function(req, res){
if (req.user) {
// logged in
async.parallel(
{
"leaderboard": function(callback) {
User.find(
{ "points": { "$exists": true } },
{
"firstname": 1,
"lastname": 1,
"points": 1
}
).sort({points : -1}).limit(5).exec(callback);
},
"totalpoints":function(callback) {
User.aggregate(
[
{ "$match": { "points": { "$exists": true } }},
{ "$group": {
"_id": null,
"totalpoints": { "$sum": "$points" }
}}
],
function(err,result) {
callback(err,result[0].totalpoints)
}
);
}
},
function(err,results) {
if (err) {
console.log(err);
//do error handling
} else {
res.render('dashboard', {
title: 'Dashboard',
user: req.user,
totalpoints: results.totalpoints,
leaderboard: results.leaderboard
});
}
}
);
} else {
// not logged in
return res.redirect('/login');
}
});
Upvotes: 1