Search code examples
javascriptnode.jsmongodbmongoosemongoose-populate

Use virtual property to access nested document in Mongoose model


Im trying to create a virtual item in one of my schemas, that will need access to data thats referenced by an item thats referenced in this schema (Yes, thats 2 references deep, connecting 3 schemas/models)

I've tried to simulate the code as close as I can here, using Model/Schema A/B/C..

This would be the schema for the ModelA, which contains the virtual item which depends on the references:

// models/modela.js

// SchemaA for ModelA
const SchemaA = new Schema({
    _foo: {
        // References ModelB
        type: Schema.Types.ObjectId,
        ref: 'ModelB'
    }
})

// Virtual `Modela.something`, which needs the data from ModelC.baz
SchemaA.virtual('something').get(function () {
    // How can I access ModelC.baz
    return 'Value from ModelC'
});

Then heres the schema for ModelB:

// models/modelb.js

// SchemaB for ModelB
const SchemaB = new Schema({
    _bar: [{
        // References ModelC.baz
        type: Schema.Types.ObjectId,
        ref: 'ModelC'
    }]
})

And the schema for ModelC:

// models/modelc.js

// SchemaC for ModelC
const SchemaC = new Schema({
    baz: Schema.Types.String
})

As you can see above, what I need to do, is access Modelc.haz from within the virtual something item in ModelA

I thought that if I did both of the populations via the query itself, then maybe this would work, So I tried something like:

this.find()
    .populate( '_foo' )
    .populate( '_foo._bar' )

Which didn't work (which I actually didn't really expect it to, but oh well)


Solution

  • You can use the Model.populate method to achieve this:

    ModelA
      .find()
      .populate({
        path: '_foo'
      })
      .exec(function(err, docs) {
        if (err) throw err;
    
        // Populate the '_bar' array of the '_foo' property which is
        // an instance of ModelB
        ModelB.populate(docs[0]._foo, { path: '_bar' }, function(err, result) {
          console.log('Showing ModelC.baz:', docs[0].something);
        });
      });
    

    You can define the virtual property like this:

    SchemaA.virtual('something').get(function () {
        // How can I access ModelC.baz
        return this._foo._bar[0].baz;
    });