How to populate with a second model if first one is empty

111 Views Asked by At

I've three models,

modelA, modelB and ModelC

ModelC's data is here

{
 "_id" : ObjectId("586e1661d9903c6027a3b47c"),
 "RefModel" : "modelA",
 "RefId" : ObjectId("57f37f18517f72bc09ee7632")
},
{
 "_id" : ObjectId("586e1661d9903c6027a3b47c"),
 "RefModel" : "modelB",
 "RefId" : ObjectId("57f37f18517f72bc09ee7698")
},

Howto write a populate query for ModelC, to populate with RefId .

it should populate with modelA or modelB which RefModel refers.

I've tried with

 ModelC.find({})
       .populate({ path: 'RefId', model: 'modelA' })
       .populate({ path: 'RefId', model: 'modelB' })

But taking only the last model.

modelC schema.

new mongoose.Schema({
  RefModel: String,
  RefId:{ type: Schema.ObjectId}});

I could do it with aggregate, but preferring populate.

2

There are 2 best solutions below

1
On

Fields' names in your database and schema are very confusing, let me explain on more clear example.

Suppose you have 3 models: User, Article and Comment. Article and Comment belongs only to single User. User can have multiple comments and articles (As you shown in your example).

  1. (More efficient and reccommending way). Store comments and articles ids in User model like:

comments: [{ id: '..', ref: Comment }], articles: [{ id: '..', ref: Article }]

and populate your document:

User.find({})
    .populate('comments')
    .populate('articles');
  1. Store User id in Comment and Article models like

user: { id: '...', ref: User }

and use mongoose-reverse-populate module for population, for example for comments model:

var reversePopulate = require('mongoose-reverse-populate');

User.find().exec(function(err, users) {

    var opts = {
        modelArray: users,
        storeWhere: "comments",
        arrayPop: true,
        mongooseModel: Comments,
        idField: "user"
    }

    reversePopulate(opts, function(err, popUsers) {
        // populated users will be populated with comments under .comments property
    });
});

Also you dont need to keep RefModel in your database, only in schema.

0
On

Here, you can try and populate it inside the callback of find itself.

Try this:

ModelC.find({}).exec(function(err,modelC){
    //find returns an array of Object, you will have to populate each Object individually.
    var modelCObjects =[];
    modelC.forEach(function(tempModelC){
        //populate via the Model
        ModelC.populate(tempModelC,{path : tempModelC.refId , model :tempModelC.refModel},function(err2,newModelC){
            //handle newModelC however you want
            //Push it into array of modelC Objects
            modelCObjects.push(newModelC);
        });


        //OR
        //Populate directly on the resultObject <-im not so sure about this
        tempModelC.populate({path : tempModelC.refId , model :tempModelC.refModel},function(err2,newModelC){
            //Push it into array of modelC Objects
            modelCObjects.push(newModelC);
        })
    });

    //here you have modelCObjects -> Array of populated modelC Objects
});

Please make sure to handle the errors.