Multiple populates - mongoosejs

Javascriptnode.jsMongodbMongoose

Javascript Problem Overview


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 ?

Javascript Solutions


Solution 1 - Javascript

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?

Solution 2 - Javascript

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

Solution 3 - Javascript

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)

Solution 4 - Javascript

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.

Solution 5 - Javascript

You can use array syntax:

let results = await OrderModel.find().populate(['user', 'meal']);

You can also select which properties you want from each populate:

let results = await OrderModel.find().populate([{path: 'user', select: 'firstname'}, {path: 'meal', select: 'name'}]);

Solution 6 - Javascript

You can try:

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

or with array options

OrderModel.find()
    .populate([
      {
        path: "path1",
        select: "field",
        model: Model1
      },
      {
        path: "path2",
        select: "field2",
        model: Model2
      }
    ])
    .exec(function (err, results) {
         // callback
    });

Solution 7 - Javascript

Latest mongoose v5.9.15 has ability to take array of populate fields so you can do,

.populate([ 'field1', 'field2' ])

Solution 8 - Javascript

In model file do something like:-

 doctorid:{
        type:Schema.Types.ObjectId, ref:'doctor'
        },
    clinicid:{
        type:Schema.Types.ObjectId, ref:'baseClinic'
        }

In js file for adding operator use Something like:-

 const clinicObj = await BaseClinic.findOne({clinicId:req.body.clinicid})
 const doctorObj = await Doctor.findOne({ doctorId : req.body.doctorid}) ;
**and add data as:-**
const newOperator = new Operator({
      clinicid:clinicObj._id,
      doctorid: doctorObj._id
    });

Now, while populating

apiRoutes.post("/operator-by-id", async (req, res) => {
    const id = req.body.id;
    const isExist = await Operator.find({ _id: id }).populate(['doctorid','clinicid'])
    if (isExist.length > 0) {
      res.send(isExist)
    } else {
      res.send("No operator found");
    }
  });

Solution 9 - Javascript

i have same problem , but my mistake not in populate , i have an error in Model

if you do this

uncorrected

user: {
 type: [Schema.Types.ObjectId],
 ref: 'User'
} 

correct

user: [{
 type: Schema.Types.ObjectId,
 ref: 'User'
}]

you must put array around of object like this

Solution 10 - Javascript

To populate multiple fields with array of objects in controller/action function, model of both is already referred in schema of post

post.find({}).populate('user').populate('comments').exec(function (err,posts)
    {
    
    if(err)
    {
        console.log("error in post");
    }
    
        return  res.render('home',{
            h1:"home Page",
            posts:posts,
            
            });      
        });

Solution 11 - Javascript

I think you are trying to the nested population you can visit official docs

User.
  findOne({ name: 'Val' }).
  populate({
    path: 'friends',
    // Get friends of friends - populate the 'friends' array for every friend
    populate: { path: 'friends' }
  });

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questione382df99a7950919789725ceeec126View Question on Stackoverflow
Solution 1 - JavascriptJohnnyHKView Answer on Stackoverflow
Solution 2 - JavascriptMichel SahliView Answer on Stackoverflow
Solution 3 - JavascriptAlexanderDSmithView Answer on Stackoverflow
Solution 4 - JavascriptMatthew WolmanView Answer on Stackoverflow
Solution 5 - JavascriptNeNaDView Answer on Stackoverflow
Solution 6 - JavascriptFranz MedranoView Answer on Stackoverflow
Solution 7 - JavascriptSwapnil SoniView Answer on Stackoverflow
Solution 8 - JavascriptVishalRajputView Answer on Stackoverflow
Solution 9 - Javascripthamidreza nikooniaView Answer on Stackoverflow
Solution 10 - JavascriptWasi ShariefView Answer on Stackoverflow
Solution 11 - JavascriptMD SHAYONView Answer on Stackoverflow