Mongoose:模式方法引用其他模型模式方法的实现疑问
嘿,我来帮你捋清楚这个问题!你碰到的"subthing.myMethod is not a function"报错,本质上是Mongoose的关联数据处理逻辑导致的:
首先明确一点:调用DomainSchema的toJSONFor方法时,Mongoose不会自动把subthings数组里的ObjectId转换成对应的Subdomain文档实例。默认情况下,Mongoose只会存储关联文档的ObjectId,不会主动去数据库拉取关联数据,所以你遍历this.subthings时,每个subthing其实只是一个字符串类型的ObjectId,根本不是Subdomain的文档对象,自然没有mymethod方法。
那你提到的用Promise.all配合Subdomain.findById的实现方式,技术上是可行的,但确实不太优雅——不仅在Domain模型里直接依赖Subdomain模型增加了耦合度,还会产生N+1查询问题(每一个subthing都要单独查一次数据库),如果subthings数量多的话,性能会受影响。
给你两个更合理的解决方案:
方案1:查询Domain时提前做Populate
这是最推荐的方式,在查询Domain文档的时候就把关联的subthings填充好:
// 查询Domain时加上populate const domain = await Domain.findById(domainId).populate('subthings').exec(); // 此时调用toJSONFor就可以直接用subthing.mymethod()了 const domainJson = domain.toJSONFor();
这样拿到的Domain实例里,this.subthings已经是完整的Subdomain文档实例集合,直接调用方法就不会报错,而且只需要一次数据库查询,性能最优。
方案2:在模型方法内部异步Populate
如果确实需要在toJSONFor方法里处理关联数据填充,可以用Mongoose实例自带的populate方法,而不是循环查库:
// 把toJSONFor改成异步方法 DomainSchema.methods.toJSONFor = async function() { // 先填充subthings关联数据 await this.populate('subthings').execPopulate(); return { _id: this._id, thing: this.thing, subthings: this.subthings.map(subthing => subthing.mymethod()) } }
注意这个方法变成异步的了,调用的时候要加await:
const domain = await Domain.findById(domainId); const domainJson = await domain.toJSONFor();
这种方式只需要一次数据库查询就能填充所有subthings,比循环findById高效得多,也避免了直接依赖Subdomain模型的问题。
最后再提醒一句:尽量避免在模型方法里做跨模型的直接依赖,利用Mongoose的populate机制处理关联数据,代码会更清晰、性能也更好。
内容的提问来源于stack exchange,提问作者JSilv

