Mongoose multiple deep populates
Asked Answered
E

2

10

Say, I have Manager schema:

{
    name: { type: String },
    clients: [{ type: Mongoose.Schema.ObjectId, ref: 'Client'}]
}

And, I have Client schema:

{
    name  : { type String },
    cars  : [{ type: Mongoose.Schema.ObjectId, ref: 'Car' }],
    houses: [{ type: Mongoose.Schema.ObjectId, ref: 'House' }]
}

And Car and House as well (their structure is not important for the matter. How do I deep populate multiple Client's fields in the single .populate() call?

What have I tried:

Manager.find()
    .populate({
         path: 'users',
         populate: { path: 'cars' },
         populate: { path: 'houses' }
    });

and it would truly surprise me if it worked (as I overwrite previously declared populate key passed to the .populate() method). My example obviously returns populated houses field per user - the one which comes last. Honestly, no idea, nothing in the docs either. Is it even supported? I know it is for 'shallow' populate, based on my example:

User.populate('cars')
    .populate('houses')

What about the deep one?

Equinoctial answered 1/9, 2017 at 13:22 Comment(4)
Surely that's just Users.populate({ path: 'users', populate: { path: 'cars houses' } }). Being 'cars houses' as the space delimited list of paths in the current level. At least that's true for a single level, so it surely should be the same for anything else.Ovovitellin
Thank you, it helps. Did I overlook an explanation for it in the docs?Equinoctial
The space delimited list is mentioned somewhere around the start of the documentation. It supersedes the .populate().populate() you mentioned.Ovovitellin
OK, thanks. Consider posting your helpful comment as an answer, it might help someone else.Equinoctial
E
22

1) Pass an array of objects with properties to be populated:

Manager.find()
    .populate({
         path    : 'users',
         populate: [
             { path: 'cars' },
             { path: 'houses' }
         ]
    });

This solution enables to use deep populate to its full extent. Consider:

Manager.find()
    .populate({
         path    : 'users',
         populate: [
             { path: 'cars' },
             { 
                path    : 'houses',
                populate: {
                    path    : 'rooms',
                    populate: 'windows'
                }
             }
         ]
    });

2) Pass string of space-delimited collections to be populated:

Manager.find()
    .populate({
         path    : 'users',
         populate: 'cars houses'
    });
Equinoctial answered 3/10, 2017 at 6:30 Comment(0)
S
1

This is the way to do nested population. In this case First it will populate users and then populate hobbies of user and then populate football from hobbies of user and then it will populate favourite playerNames from user -> hobbies -> football -> favouritePlayers.

Model.find()
    .populate({
         path    : 'users',
         populate: [
             { path: 'Hobbies' },
             { 
                path    : 'Football',
                populate: {
                    path    : 'favouritePlayers',
                    populate: 'playerNames'
                }
             }
         ]
    });
Seaplane answered 9/9, 2021 at 12:24 Comment(1)
While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.Montague

© 2022 - 2024 — McMap. All rights reserved.