在NestJS中使用Mongoose实例化Document时崩溃问题
兄弟,我之前也踩过一模一样的坑!你遇到的TypeError: Cannot read property 'plugin' of undefined,核心原因是不能直接手动new Cat(dto)创建Mongoose Document实例——Mongoose的Document子类(也就是你定义的Cat)必须由对应的Model来生成,这样Mongoose才能在内部完成Schema绑定、插件初始化这些必要操作。你直接调用构造函数的话,实例根本没关联到任何Schema,自然找不到plugin相关的内部属性。而且更坑的是,你写的自定义构造函数其实根本不会被Mongoose的实例创建流程调用,你的逻辑等于白写了。
给你几个可行的解决方案,都能保住SOLID原则,把逻辑留在模型层:
方案1:用Mongoose的pre('save')钩子(最推荐)
把模型专属的逻辑放到Schema的预保存钩子中,这样每次保存前Mongoose会自动执行这段逻辑,完美符合模型单一职责:
// cat.model.ts import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document, HookNextFunction } from 'mongoose'; import { CreateCatDTO } from './dto/create-cat.dto'; @Schema() export class Cat extends Document { @Prop() name: string; @Prop() info: string; } export const CatSchema = SchemaFactory.createForClass(Cat); // 预保存钩子,处理模型内部的算法逻辑 CatSchema.pre('save', function(next: HookNextFunction) { // 这里的this就是当前的Cat实例 this.info = this.name + algorithmResultBlah; // 替换成你的算法结果 next(); });
然后服务层改成用Model创建实例并保存:
// cat.service.ts async create(dto: CreateCatDTO) { const cat = new this.catRepository(dto); // 用Model创建合法的Document实例 await cat.save(); // 保存时自动触发pre钩子处理info字段 return cat; }
方案2:给Model加静态方法
如果你的逻辑需要在实例化前就处理数据,可以给Schema加静态方法,把逻辑封装在模型层面:
// cat.model.ts export const CatSchema = SchemaFactory.createForClass(Cat); // 定义静态方法,封装创建+逻辑处理的流程 CatSchema.statics.createWithLogic = function(dto: CreateCatDTO) { const processedInfo = dto.name + algorithmResultBlah; return this.create({ ...dto, info: processedInfo }); };
服务层直接调用这个静态方法:
// cat.service.ts async create(dto: CreateCatDTO) { const cat = await this.catRepository.createWithLogic(dto); return cat; }
方案3:用实例方法(适合依赖实例属性的逻辑)
如果你的逻辑需要操作实例的其他属性,可以定义实例方法:
// cat.model.ts export const CatSchema = SchemaFactory.createForClass(Cat); // 定义实例方法,处理info字段 CatSchema.methods.processInfo = function() { this.info = this.name + algorithmResultBlah; return this; };
服务层使用方式:
// cat.service.ts async create(dto: CreateCatDTO) { const cat = new this.catRepository(dto); cat.processInfo(); // 调用实例方法执行逻辑 await cat.save(); return cat; }
再帮你理清楚原代码为什么不行:
当你直接new Cat(dto)时,创建的只是一个继承了Document的普通JS对象,但Mongoose的Document需要依赖Model和Schema的上下文才能正常工作——比如内部的$__schema属性(就是报错里找不到的plugin所属的对象),是在Model创建实例时才会注入的,手动实例化的话这个属性是undefined,所以才会抛出那个错误。而且Mongoose自己有一套实例创建流程,根本不会调用你写的自定义构造函数,所以你在构造函数里的逻辑完全没生效。
内容的提问来源于stack exchange,提问作者Thimma

