使用虚拟属性访问猫鼬模型中的嵌套文档

Use virtual property to access nested document in Mongoose model

本文关键字:模型 嵌套 文档 虚拟 属性 访问      更新时间:2023-09-26

我试图在我的一个架构中创建一个虚拟项目,这将需要访问此架构中引用的项目引用的数据(是的,这是 2 个引用深度,连接 3 个架构/模型)

我试图在这里尽可能接近地模拟代码,使用模型/模式 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'
});

下面是模型 B 的架构:

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

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

我想如果我通过查询本身做这两个人口,那么也许这会起作用,所以我尝试了这样的东西:

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

您可以在此 github 问题中找到有关此内容的更多信息。您可以使用查询挂钩。

我遇到了类似的问题,所以我用它来填充参考模型以在虚拟函数中使用。下面是一个示例。

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