Multiple populates - mongoosejs

Just a simple query, for example with a double ref in the model.

Schema / Model

var OrderSchema = new Schema({

    user: {
        type    : Schema.Types.ObjectId,
        ref     : 'User',
        required: true
    },

    meal: {
        type    : Schema.Types.ObjectId,
        ref     : 'Meal',
        required: true
    },
});

var OrderModel = db.model('Order', OrderSchema);

Query

OrderModel.find()
    .populate('user') // works
    .populate('meal') // dont works
    .exec(function (err, results) {
         // callback
    });

I already tried something like

.populate('user meal')
.populate(['user', 'meal'])

In fact only one of the populates works.

So, how do is get two populates working ?


You're already using the correct syntax of:

OrderModel.find()
    .populate('user')
    .populate('meal')
    .exec(function (err, results) {
         // callback
    });

Perhaps the meal ObjectId from the order isn't in the Meals collection?


UPDATE:
This solution remains for the version 3.x of Mongoose
http://mongoosejs.com/docs/3.8.x/docs/populate.html
but is no longer documented for >= 4.x versions of Mongoose and so the answer from @JohnnyHK is the only valid one for now on.

ORIGINAL POST
If you're using Mongoose >= 3.6, you can pass a space delimited string of the path names to populate:

OrderModel.find()
    .populate('user meal')
    .exec(function (err, results) {
         // callback
    });

http://mongoosejs.com/docs/populate.html


This has probably been resolved already, but this is my take on multiple & deep population in Mongodb > 3.6:

OrderModel.find().populate([{
    path: 'user',
    model: 'User'
}, {
    path: 'meal',
    model: 'Meal'
}]).exec(function(err, order) {
    if(err) throw err;
    if(order) {
        // execute on order
        console.log(order.user.username); // prints user's username
        console.log(order.meal.value);    // you get the idea
    }
});

There are probably other ways to do this, but this makes very readable code for beginners (like me)


The best solution in my opinion is arrays when you are populating more than one foreign field on the same level. My code shows that I have multiple populates for different levels.

const patients = await Patient.find({})
                    .populate([{
                        path: 'files',
                        populate: {
                            path: 'authorizations',
                            model: 'Authorization'
                        },
                        populate: {
                            path: 'claims',
                            model: 'Claim',
                            options: {
                                sort: { startDate: 1 }
                            }
                        }
                    }, {
                        path: 'policies',
                        model: 'Policy',
                        populate: {
                            path: 'vobs',
                            populate: [{
                                path: 'benefits'
                            }, {
                                path: 'eligibility', 
                                model: 'Eligibility'
                            }]
                        }
                    }]);

As you can see, wherever I needed more than one field of a document populated, I encased the populate key in an array and provided an array of objects, each object having a different path. Most robust and concise way to do it, in my opinion.