MongoDB创建Product前用聚合校验categoryId存在性,无需导入CategoryModel
解决方案:在创建产品前验证分类存在性(无需控制器导入分类模型)
可以通过**Mongoose预保存中间件(pre-save hook)**结合聚合操作实现需求,无需在ProductController中导入CategoryModel,所有验证逻辑放在Schema层面处理。
实现步骤
在productSchema中添加预保存中间件,利用Mongoose的this.model('Category')直接获取分类模型,再通过聚合操作检查分类是否存在:
const productSchema = new mongoose.Schema({ name: { type: String, required: true }, price: { type: Number, required: true }, addedBy: { type: mongoose.Types.ObjectId, ref: 'User' }, categoryId: { type: mongoose.Types.ObjectId, ref: 'Category' }, isDeleted: { type: Boolean, default: false }, }, { timestamps: true, versionKey: false }); // 添加预保存中间件,验证分类存在性 productSchema.pre('save', async function(next) { // 如果未传入categoryId,直接跳过检查 if (!this.categoryId) return next(); // 使用聚合操作检查目标分类是否存在(排除已删除的分类) const checkResult = await this.model('Category').aggregate([ { $match: { _id: this.categoryId, isDeleted: false } }, { $count: 'exists' } ]); // 若聚合结果为空,说明分类不存在,抛出错误阻止保存 if (checkResult.length === 0) { return next(new Error('指定的分类不存在或已被删除')); } // 分类验证通过,继续执行保存流程 next(); });
代码说明
- 无需导入CategoryModel:通过
this.model('Category')可以直接从Mongoose的模型注册表中获取分类模型,避免在控制器或Schema文件外显式导入。 - 聚合逻辑:
$match:匹配传入的categoryId,同时排除isDeleted: true的已删除分类$count:统计匹配到的文档数量,若结果数组长度为0,说明没有符合条件的分类
- 异步处理:中间件使用
async/await处理异步聚合查询,确保验证完成后再执行保存操作。
替代方案(更简洁)
如果不需要严格使用聚合操作,也可以用countDocuments实现相同效果,代码更简洁:
productSchema.pre('save', async function(next) { if (!this.categoryId) return next(); const categoryCount = await this.model('Category').countDocuments({ _id: this.categoryId, isDeleted: false }); if (categoryCount === 0) { return next(new Error('指定的分类不存在或已被删除')); } next(); });
使用方式
在ProductController中正常调用产品创建逻辑即可,验证逻辑会自动触发:
// 控制器中无需导入CategoryModel const createProduct = async (req, res) => { try { const product = await Product.create(req.body); res.status(201).json(product); } catch (err) { res.status(400).json({ message: err.message }); } };
内容的提问来源于stack exchange,提问作者Avijit Sengupta
相关产品推荐
相关产品推荐

