使用 virtual 属性 访问 Mongoose 模型中的嵌套文档

Use virtual property to access nested document in Mongoose model

我正在尝试在我的一个模式中创建一个虚拟项目,这将需要访问该模式中引用的项目所引用的数据(是的,那是 2 个深度引用,连接 3 个 schemas/models)

我在这里尝试尽可能接近地模拟代码,使用 Model/Schema A/B/C..

这将是 ModelA 的架构,其中包含依赖于引用的虚拟项目:

// 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'
});

然后是 ModelB 的架构:

// models/modelb.js

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

以及 ModelC 的架构:

// models/modelc.js

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

正如您在上面看到的,我需要做的是从 中的虚拟 something 项目中访问 Modelc.haz模型A

我认为,如果我通过查询本身对两个群体都进行了处理,那么也许这会奏效,所以我尝试了类似的方法:

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

哪个没用(实际上我真的没想到,但是哦好吧)

可以使用Model.populate方法实现:

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);
    });
  });

您可以这样定义虚拟 属性:

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

您可以在 this github issue 找到更多相关信息。您可以使用查询挂钩。

我有类似的问题,所以我用它来填充参考模型以在虚函数中使用。这是一个示例。

const tutorialSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    videos: [
        {
            type: mongoose.Schema.Types.ObjectId,
            ref: 'Video'
        }
    ]
});

tutorialSchema.pre('findOne', function (next) {
    this.populate('videos');  // now can be accessed using this.videos
    next();
});

tutorialSchema.virtual('totalTime').get(function () {
    let times = [];
    times = this.videos.map((v) => {
        return v.duration;
    });
    if(times.length === 0) return 0;
    let totalTime; // find total time of videos and then return
    return totalTime;
});