Mongoose更新查询出现RangeError:调用栈溢出问题求助
解决Mongoose更新数组时的RangeError: Maximum call stack size exceeded问题
嘿,我之前也踩过这个坑!这种栈溢出问题通常是Mongoose的封装逻辑和原生Mongo客户端不一样导致的,尤其是处理数组类型的时候。咱们一步步来排查解决:
可能的原因
- Mongoose的数组是MongooseArray类型(不是普通JS数组),它自带了getter/setter和变更追踪,如果你更新的写法不对,可能触发递归调用导致栈溢出。
- 你可能在Schema的pre/post钩子(比如
pre('save')、pre('update'))里不小心写了递归逻辑,比如在钩子中又调用了save()或者update(),无限循环导致栈满。 - 当
discovery数组里包含复杂对象时,Mongoose会自动将其转换为Mongoose文档对象,如果对象存在循环引用,也会触发栈溢出。
解决方案
1. 直接使用原生Mongo集合操作
既然原生客户端运行正常,那咱们可以绕过Mongoose的封装,直接调用原生的集合方法:
// 用Model.collection直接调用原生updateOne方法 User.collection.updateOne( { _id: req.user._id }, // 查询条件 { $set: { `discovery.${yourIndex}`: yourUpdatedData } }, // 替换成你要更新的数组位置和数据 (err, result) => { if (err) { return next(err); } // 可以再查询一次获取更新后的文档,或者直接返回结果 User.findById(req.user._id).then(user => res.send(user)); } );
这种方式和你在原生客户端执行的逻辑完全一致,不会触发Mongoose的额外处理,大概率能解决栈溢出问题。
2. 检查并修复递归的中间件
去检查你的Schema有没有类似这样的钩子:
UserSchema.pre('save', function(next) { // 错误示例:这里又调用了save,会无限递归 this.save().then(() => next()); });
如果有这种逻辑,赶紧修改,比如改成只在特定条件下执行,或者避免在钩子中再次触发save/update操作。
3. 规范Mongoose的更新语法
如果你想继续用Mongoose的ORM方法,确保用正确的更新操作符(比如$set)来定位数组元素,不要直接修改数组后调用save():
// 正确的Mongoose更新方式,用$set指定数组索引 User.findByIdAndUpdate( req.user._id, { $set: { `discovery.${yourIndex}`: yourUpdatedData } }, { new: true, runValidators: true } // new: true 返回更新后的文档;runValidators: true 触发验证 ) .then(updatedUser => res.send(updatedUser)) .catch(err => next(err));
另外,如果discovery不需要Mongoose的特殊处理,可以把Schema定义改成明确的混合类型,避免自动转换为Mongoose文档:
const UserSchema = new mongoose.Schema({ username: { type: String, unique: true, required: true, trim: true }, discovery: { type: mongoose.Schema.Types.Mixed, default: [] } });
总结
优先试试第一种方法(原生集合操作),快速验证是不是Mongoose的封装导致的问题。如果解决了,再根据你的需求选择是继续用原生方法,还是调整Mongoose的写法和Schema定义。
内容的提问来源于stack exchange,提问作者adot
相关产品推荐
相关产品推荐

