Mongoose save()不更新数据库:pull移除files数组元素后查询仍保留旧值
问题根因分析
以下是导致该问题的典型可能性,按概率从高到低排列:
- Mongoose 未识别files数组变更,未提交到数据库
Mongoose的save方法返回的是内存中修改后的文档对象,不等同于变更已成功持久化到数据库。你两次输出的__v字段值都是7,正常save操作成功后版本号会自动+1,说明本次修改实际没有触发数据库写入。
排查方向:- 检查Package模型的
files字段Schema定义,是否设置了immutable: true,该属性会导致字段变更被Mongoose静默忽略,就算调用markModified也不会生效 - 检查是否存在
pre('save')中间件,在保存逻辑前重置了files数组的内容,但是未修改size字段,所以size字段的变更被保留 - 确认
files数组存储的元素类型和你传入pull的参数类型一致:如果files数组存的是字符串类型的ID,你传入ObjectId类型的file._id执行pull,会导致Mongoose匹配不到对应项,数组修改实际未生效
- 检查Package模型的
- 读写分离+主从同步延迟
如果你配置了Mongoose读写分离,写入请求走主节点,查询请求走从节点,刚写入后主从还未完成同步时,查询从节点会拿到旧数据。但该场景下版本号应该会更新,如果你确认版本号确实未变化,可排除该可能性 - 并发写冲突
你当前使用的是「查询-修改-保存」的非原子操作模式,如果同一时间有其他请求也在修改同一条Package记录,你的修改可能被其他请求覆盖。该场景下版本号通常会有变化,可结合业务并发情况排查。
修复建议
- 开启Mongoose debug模式查看实际发送到数据库的更新语句:在代码中添加
mongoose.set('debug', true),确认save操作发送的更新请求中是否包含files字段的变更 - 替换为原子更新操作,规避非原子操作的并发问题和markModified识别问题,示例代码:
app.post('/remove-package-file', async (req, res) => { const { package: packageId, file: fileId } = req.body if (!packageId || !fileId) return res.status(500).json({ error: true, message: 'Please specify file and package to remove' }) try { const file = await File.findOne({ _id: fileId }) if (!file) return res.status(500).json({ error: true, message: 'Unable to locate file' }) const updatedPack = await Package.findOneAndUpdate( { _id: packageId, files: fileId }, { $pull: { files: fileId }, $inc: { size: -file.stats.size } }, { new: true, runValidators: true } ) if (!updatedPack) return res.status(500).json({ error: true, message: 'Package not found or file not in package' }) res.status(200).json({ error: false, package: updatedPack }) } catch (e) { saveError(e) return res.status(500).json({ error: true, message: 'DB error' }) } })
- 检查Schema定义,确认
files字段类型与存储内容匹配,无多余的不可修改配置。
内容的提问来源于stack exchange,提问作者Sam Wagner
相关产品推荐
相关产品推荐

