Mongoose调用posts.save()报错is not a function的解决方法
报错核心原因
你遇到的posts.save is not a function报错由两个问题直接导致:
Post.find()返回值是匹配到的Mongoose文档实例组成的数组,数组类型本身没有挂载Mongoose文档的save()方法,只有数组内的单个Post文档实例可以调用save()- 代码中使用
forEach搭配async/await的写法存在异步时序问题:forEach不会等待内部异步操作执行完成,即使修正了save的调用对象,也会出现Post关联引用还没更新完、就提前执行Tag删除逻辑的问题。
可行修复方案
方案1:修正单文档遍历逻辑
在原有写法基础上调整,适合需要对每个Post文档做额外校验/处理的场景:
async deleteTag(root, { id }, context) { // 优先校验待删除Tag是否存在,不存在直接返回,避免无效查询 const tag = await Tag.findById(id); if(!tag){ const error = new Error('Tag not found!'); error.code = 404; throw error; } // 只查询tags字段包含当前待删除Tag id的Post,避免拉取全表无效数据 const posts = await Post.find({ tags: id }); // 用for...of替代forEach,保证异步操作按预期执行 for (const post of posts) { post.tags.pull(id); await post.save(); // 对单个文档实例调用save方法 } await Tag.findByIdAndRemove(id); return true; }
方案2:数据库批量更新(推荐,性能最优)
如果不需要对单个Post做额外处理,直接调用MongoDB原生的批量更新能力,不需要把所有Post文档加载到内存,数据量越大性能优势越明显:
async deleteTag(root, { id }, context) { const tag = await Tag.findById(id); if(!tag){ const error = new Error('Tag not found!'); error.code = 404; throw error; } // 单条语句完成所有关联Post的引用清理 await Post.updateMany( { tags: id }, { $pull: { tags: id } } ); await Tag.findByIdAndRemove(id); return true; }
最佳实践建议
- 关联数据清理场景优先使用数据库层面的批量操作(如
updateMany、bulkWrite),避免全量查询文档到内存后循环处理,减少数据库请求次数、降低内存占用 - 不要在
forEach、map这类不支持异步等待的遍历方法中直接写await,处理串行异步循环优先用for...of,处理无依赖的并行异步任务可以用Promise.all - 做关联查询时务必提前加过滤条件,比如本场景只需要查询关联了待删除Tag的Post即可,禁止无过滤条件的全表查询
- 如果业务对数据一致性要求高,可以给「清理Post关联引用」+「删除Tag」两个操作加MongoDB事务,保证两个操作要么同时成功、要么同时回滚,避免中途报错导致的数据不一致问题
内容的提问来源于stack exchange,提问作者palnic
相关产品推荐
相关产品推荐

