Search code examples
node.jsmongodbmongoosemongoose-populate

How do you populate a field from a sibling collection using Mongoose?


I would like to use the populate method in Mongoose to query a collection. Here is my schema:

var UnitSchema = new Schema({
    name: {
        type: String,
        required: true
    },
    unitNumber: Number,
    members: [MemberSchema],
    orgs: [OrgSchema]
});

var MemberSchema = new mongoose.Schema({
    name: {
        first: String,
        last: String
    },
    phone: String,
    email: String
});

var OrgSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true
    },
    sortIndex: Number,
    roles: [RoleSchema]
});

var RoleSchema = new mongoose.Schema({
    name: String,
    sortIndex: Number,
    member: {
        type: Schema.Types.ObjectId,
        ref: 'Member'
    }
});

Here is a sample Unit collection:

{
    "_id": "58da7bd6e7092e8735646ba0",
    "name": "Unit Name",
    "orgs": [
        {
            "name": "Org name",
            "sortIndex": 0,
            "_id": "58da7bd6e7092e8735646c2d",
            "roles": [
                {
                    "name": "Role Name",
                    "sortIndex": 0,
                    "member": "58dbd1e6c9a893a7840472ad"
                }
            ]
        }
     ],
     "members": [
         {
             "name": {
                 "first": "First Name",
                 "last": "Last Name"
             },
             "_id": "58dbd1e6c9a893a7840472ad"
         }
     ]
 }

How do I populate a Member doc to the member field in the Roles collection so my output would be like this:

[
    {
        "name": "Org name",
        "sortIndex": 0,
        "_id": "58da7bd6e7092e8735646c2d",
        "roles": [
            {
                "name": "Role Name",
                "sortIndex": 0,
                "member": {
                    "name": {
                        "first": "First Name",
                        "last": "Last Name"
                     },
                     "_id": "58dbd1e6c9a893a7840472ad"
                }
             }
         ]
     }
 ]

Here is the query I was thinking of:

Unit.findOne({unitNumber: unitNumber}).populate('roles.callings.member').exec(...)

Solution

  • Here you go!

    unit.findOne({unitNumber: 543})
    .populate('orgs')
    .exec(function(err, unitDoc){
        var subDoc = unitDoc.orgs;
        for(var org of subDoc){
            org.populate('roles', function(err, data){
                var rolesDoc = data.roles;
                for(var role of rolesDoc){
                    role.populate('member', function(err, rdata){
                        console.log(JSON.stringify(unitDoc));
                    })
                }
            })
        }
    })
    

    You need to populate each sub-documents as done above.