如何在Mongoose中使用pre中间件实现多级关联记录级联删除
你原来的实现存在两个核心问题:1. 没有等待异步删除操作完成就调用next(),大概率会出现数据不一致;2. 缺少Agreement的关联删除逻辑。以下是可直接运行的完善方案:
方案1:分层配置中间件(推荐)
该方案逻辑解耦,后续单独删除Room时也会自动清理关联的Agreement,可维护性更强。
第一步:给RoomSchema添加deleteMany前置钩子
RoomSchema.pre("deleteMany", async function (next) { // 获取当前要删除的所有Room ID列表 const roomIds = await Room.find(this.getFilter()).distinct("_id").exec(); // 批量删除这些Room关联的所有Agreement await Agreement.deleteMany({ room: { $in: roomIds } }).exec(); next(); });
第二步:修改BuildingSchema的deleteOne前置钩子,增加异步等待
BuildingSchema.pre("deleteOne", async function (next) { // 兼容query级别和document级别的deleteOne调用 const buildingId = this.getFilter ? this.getFilter()._id : this._id; // 触发Room的deleteMany,自动执行上面对应的Agreement删除逻辑 await Room.deleteMany({ building: buildingId }).exec(); next(); });
方案2:单中间件一次性处理
适合不需要单独处理Room删除逻辑的场景,所有关联清理逻辑都收敛在Building的删除钩子中:
BuildingSchema.pre("deleteOne", async function (next) { const buildingId = this.getFilter ? this.getFilter()._id : this._id; // 1. 查询该建筑下所有房间ID const roomIds = await Room.find({ building: buildingId }).distinct("_id").exec(); // 2. 删除这些房间关联的所有合同 await Agreement.deleteMany({ room: { $in: roomIds } }).exec(); // 3. 删除所有关联房间 await Room.deleteMany({ building: buildingId }).exec(); next(); });
额外适配说明
如果业务中还会用到findOneAndDelete、deleteMany等方法删除Building,需要把钩子的触发事件扩展为对应事件列表,同时适配多建筑删除的逻辑:
BuildingSchema.pre(["deleteOne", "findOneAndDelete", "deleteMany"], async function (next) { // 处理批量删除Building的场景 if (this.op === "deleteMany") { const buildingIds = await Building.find(this.getFilter()).distinct("_id").exec(); const roomIds = await Room.find({ building: { $in: buildingIds } }).distinct("_id").exec(); await Agreement.deleteMany({ room: { $in: roomIds } }).exec(); await Room.deleteMany({ building: { $in: buildingIds } }).exec(); } else { // 单个删除逻辑 const buildingId = this.getFilter ? this.getFilter()._id : this._id; const roomIds = await Room.find({ building: buildingId }).distinct("_id").exec(); await Agreement.deleteMany({ room: { $in: roomIds } }).exec(); await Room.deleteMany({ building: buildingId }).exec(); } next(); });
内容的提问来源于stack exchange,提问作者Justin Timbersaw
相关产品推荐
相关产品推荐

