Mongoose pre find钩子无法调用aggregate方法的解决方案
问题根因
pre('find')钩子的执行上下文this是Mongoose的Query实例,本身没有挂载aggregate方法;另外find和aggregate是Mongoose中完全独立的两条查询执行链路,就算你在钩子里强行触发聚合查询,也不会替换原本待执行的find逻辑,根本达不到自动追加关联查询的效果。
可行解决方案
以下两种方案都满足「无需单独编写聚合调用、无需新增额外接口」的诉求,按需选择即可:
方案1:使用内置populate替代手写$lookup(优先推荐,适配绝大多数关联场景)
你当前写的$lookup属于最基础的单字段关联查询,和Mongoose内置的populate能力完全匹配,不需要走聚合链路,直接在pre find钩子里给当前查询链式追加populate配置即可,所有走find的请求都会自动关联查询分类数据:
// 注意不要在这个中间件里滥用async/await打断查询链路 schema.pre("find", function () { // 最简写法,自动关联categories字段对应的分类文档 this.populate("categories"); });
如果需要对关联结果做字段裁剪、条件过滤,直接给populate传配置项即可,效果和自定义$lookup完全一致:
schema.pre("find", function () { this.populate({ path: "categories", select: "name icon", // 只返回分类的名称、图标字段,默认会带_id match: { isDeleted: false } // 只关联未被删除的分类 }); });
方案2:覆盖原生find方法(需要复杂聚合逻辑时使用)
如果后续你还要在关联逻辑里增加分组计算、多表级联、字段聚合等populate实现不了的逻辑,可以直接在schema上重写原生find方法,把聚合逻辑封装在方法内部,外部所有调用Model.find()的业务代码不需要做任何修改,完全无感知:
// 先缓存原生find方法的引用,避免彻底丢失原生能力 const nativeFind = schema.statics.find; schema.statics.find = async function (filter = {}, projection = {}, options = {}) { const agg = this.aggregate([ // 先匹配find传入的查询条件,和原生find行为对齐 { $match: filter }, // 你需要的关联查询逻辑 { $lookup: { from: "categories", localField: "categories", foreignField: "_id", as: "categories" } }, // 可以在此处继续追加其他聚合阶段,比如$project、$unwind、$group等 ]); // 对齐原生find的投影、排序、分页参数逻辑 if (Object.keys(projection).length) agg.project(projection); if (options.sort) agg.sort(options.sort); if (options.skip) agg.skip(options.skip); if (options.limit) agg.limit(options.limit); return agg.exec(); };
提示:如果你的业务里还用到了
findOne、findById这类衍生查询方法,用同样的逻辑做覆盖即可。
内容的提问来源于stack exchange,提问作者Nazehs
相关产品推荐
相关产品推荐

