ServerSideSkittles
ServerSideSkittles

Reputation: 2973

Passing two query results into a response

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

Answers (1)

Blakes Seven
Blakes Seven

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

Related Questions