如何在Mongoose更新请求中验证$push操作?
Mongoose $push 更新时数组验证不生效的解决方法
嘿,这个问题我之前也踩过坑!Mongoose 默认的 Schema 验证逻辑只在创建文档(比如你的 POST 请求)或者调用 save() 方法时才会触发,而用 $push 这类更新操作时,默认是不会跑 Schema 级别的验证的,包括你写的那个 maxOption 函数。下面给你两种实用的解决思路:
方法一:启用更新操作的验证选项
在使用 findOneAndUpdate()、updateOne() 这类更新方法时,只要加上 { runValidators: true } 参数,Mongoose 就会在更新时执行你定义的验证规则了。举个实际的代码例子:
// 示例:给findOneAndUpdate加上验证选项 Product.findOneAndUpdate( { _id: productId }, // 查询条件 { $push: { options: newOption } }, // 更新操作 { runValidators: true, new: true } // 关键是runValidators: true,new返回更新后的文档 ) .then(updatedProduct => { // 处理更新成功后的文档 }) .catch(err => { // 这里就能捕获到options超过3个的验证错误了 console.error(err.message); });
注意:这个选项是 Mongoose 4.0 版本之后才支持的,如果你用的是老版本,得先升级一下。
方法二:用预更新钩子做自定义验证
有时候 runValidators 可能没法完全贴合你的需求——比如它是针对你要 push 的值本身,而不是 push 之后整个数组的长度。这时候可以用 Mongoose 的预更新钩子(pre hook)来手动做验证,逻辑更精准:
// 给ProductSchema添加pre findOneAndUpdate钩子 ProductSchema.pre('findOneAndUpdate', function(next) { // 获取这次更新要push的options数据 const pushItem = this.getUpdate()?.$push?.options; if (pushItem) { // 先查询当前文档的options数组长度 Product.findById(this.getQuery()._id) .then(targetProduct => { const currentLength = targetProduct.options.length; // 验证push之后总长度是否超过3 if (currentLength + 1 > 3) { next(new Error('options exceeds the limit of 3')); } else { next(); // 验证通过,继续执行更新 } }) .catch(dbErr => next(dbErr)); } else { next(); // 没有push操作,直接放行 } });
这个钩子会在更新操作执行前触发,先拿到当前文档的数组长度,加上要新增的元素数量,判断是否超限,能完美覆盖你要的场景。
补充说明
Mongoose 默认不在更新时跑验证,主要是为了性能考虑——更新操作通常只修改部分字段,而 Schema 验证是针对整个文档的。所以需要我们显式启用验证,或者自定义钩子来处理这类场景。
内容的提问来源于stack exchange,提问作者SwadhIn
相关产品推荐
相关产品推荐

