4

I have following data in my Mongodb.

 { "_id" : ObjectId("54a0d4c5bffabd6a179834eb"), "is_afternoon_scheduled" : true, "employee_id" : ObjectId("546f0a06c7555ae310ae925a") } 

I would like to use populate with aggregate, and want to fetch employee complete information in the same response, I need help in this. My code is:

var mongoose = require("mongoose"); var empid = mongoose.Types.ObjectId("54a0d4c5bffabd6a179834eb"); Availability.aggregate() .match( { employee_id : empid} ) .group({_id : "$employee_id",count: { $sum: 1 }}) .exec(function (err, response) { if (err) console.log(err); res.json({"message": "success", "data": response, "status_code": "200"}); } ); 

The response i am getting is

{"message":"success","data":{"_id":"54a0d4c5bffabd6a179834eb","count":1},"status_code":"200"} 

My expected response is:

{"message":"success","data":[{"_id":"54aa34fb09dc5a54232e44b0","count":1, "employee":{fname:abc,lname:abcl}}],"status_code":"200"} 

2 Answers 2

4

You can call the model form of .populate() on the result objects from an aggregate operation. But the thing is you are going to need a model to represent the "Result" object returned by your aggregation in order to do so.

There are a couple of steps, best explained with a complete listing:

var async = require('async'), mongoose = require('mongoose'), Schema = mongoose.Schema; var employeeSchema = new Schema({ "fname": String, "lname": String }) var availSchema = new Schema({ "is_afternoon_scheduled": Boolean, "employee_id": { "type": Schema.Types.ObjectId, "ref": "Employee" } }); var resultSchema = new Schema({ "_id": { "type": Schema.Types.ObjectId, "ref": "Employee" }, "count": Number }); var Employee = mongoose.model( "Employee", employeeSchema ); var Availability = mongoose.model( "Availability", availSchema ); var Result = mongoose.model( "Result", resultSchema, null ); mongoose.connect('mongodb://localhost/aggtest'); async.series( [ function(callback) { async.each([Employee,Availability],function(model,callback) { model.remove({},function(err,count) { console.log( count ); callback(err); }); },callback); }, function(callback) { async.waterfall( [ function(callback) { var employee = new Employee({ "fname": "abc", "lname": "xyz" }); employee.save(function(err,employee) { console.log(employee), callback(err,employee); }); }, function(employee,callback) { var avail = new Availability({ "is_afternoon_scheduled": true, "employee_id": employee }); avail.save(function(err,avail) { console.log(avail); callback(err); }); } ], callback ); }, function(callback) { Availability.aggregate( [ { "$group": { "_id": "$employee_id", "count": { "$sum": 1 } }} ], function(err,results) { results = results.map(function(result) { return new Result( result ); }); Employee.populate(results,{ "path": "_id" },function(err,results) { console.log(results); callback(err); }); } ); } ], function(err,result) { if (err) throw err; mongoose.disconnect(); } ); 

That's the complete example, but taking a closer look at what happens inside the aggregate result is the main point:

 function(err,results) { results = results.map(function(result) { return new Result( result ); }); Employee.populate(results,{ "path": "_id" },function(err,results) { console.log(results); callback(err); }); } 

The first thing to be aware of is that the results returned by .aggregate() are not mongoose documents as they would be in a .find() query. This is because aggregation pipelines typically alter the document in results from what the original schema looked like. Since it is just a raw object, each element is re-cast as a mongoose document for the Result model type defined earlier.

Now in order to .populate() with data from Employee, the model form of this method is called on the array of results in document object form along with the "path" argument to the field to be populated.

The end result fills is the data as it comes from the Employee model it was related to.

[ { _id: { _id: 54ab2e3328f21063640cf446, fname: 'abc', lname: 'xyz', __v: 0 }, count: 1 } ] 

Different to how you process with find, but it is necessary to "re-cast" and manually call in this way due to how the results are returned.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! Exactly what I was looking for. I wasn't aware you had to create an actual Model for the aggregated/grouped Result that you get. I assumed you could just do a .populate() on whatever you get back! (But how would it know without a schema, doi... my own fault haha)
-1

This is working like applied populate with aggregate using inner query.

 var mongoose = require("mongoose"); var empid = mongoose.Types.ObjectId("54a0d4c5bffabd6a179834eb"); Availability.aggregate() .match( { employee_id : empid} ) .group({_id : "$employee_id",count: { $sum: 1 }}) .exec(function (err, response) { if (err) console.log(err); if (response.length) { var x = 0; for (var i=0; i< response.length; i++) { empID = response[i]._id; if (x === response.length -1 ) { User.find({_id: empID}, function(err, users){ res.json({"message": "success", "data": users, "status_code": "200"}); }); } x++; } } } ); 

2 Comments

How is throwing away the "count" values you asked from aggregate considered to be working? The looping does not work as it does not wait for the response of the query and that is also not how you build an array to pass to $in. So many problems here.
This is kind of the worst answer to give to someone asking for help. If you're not using native mongodb queries please at least give examples on how to properly use mongoose after reading the manual!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.