Mongoose/MongoDB:如何在自引用结构中统计开发人员下属数量
问题描述
我有一个员工(Employees)集合,采用员工与经理自引用的结构,通过_id和managerIDs字段关联,经理本身也是员工。
员工文档示例:
{ "_id": "61b9f07300127afb99f8c1ea", "title": "Developer", "firstName": "Jack", "lastName": "Strauss", "managerIDs": [ "61cedf84800749316306c6da" ], "deptID": "61b9f073267500832f5d94d0" }, { "_id": "61cedf84800749316306c6da", "title": "Sr. Developer", "firstName": "Richard", "lastName": "Haris", "managerIDs": null, "deptID": "61b9f073267500832f5d94d0" }, { "_id": "61cedf17800749316306c6cf", "title": "Manager App Development", "firstName": "Arnold", "lastName": "Cliff", "deptID": "61b9f073267500832f5d94d0" }, { "_id": "61d4503e1223496ab8a5ae3c", "title": "Developer", "firstName": "Andrew", "lastName": "Turner", "managerIDs": [ "61cedf17800749316306c6cf", "61cedf84800749316306c6da" ], "deptID": "61b9f073267500832f5d94d0" }
规则说明:
- 员工可无、1个或多个经理(因此使用数组存储
managerIDs)。 managerIDs字段可为null、undefined、单个ID或多个ID数组。- Developer(开发人员)不能担任经理,不会被纳入经理列表,且只有开发人员会被管理。
- 所有非开发人员均为经理,需统计其管理的开发人员数量(即其ID出现在开发人员的
managerIDs数组中的次数)。
我需要编写查询来列出所有经理的姓名、职位及管理的开发人员数量,尝试过MongoDB聚合的$lookup但未成功,请问如何在MongoDB或Mongoose中编写该查询?
解决方案
可以通过MongoDB的聚合管道实现,核心思路是先筛选出所有经理,再关联统计每个经理对应的开发人员数量,具体实现如下:
MongoDB聚合查询代码
db.employees.aggregate([ // 筛选出所有经理(排除职位含"Developer"的员工) { $match: { title: { $not: { $regex: /Developer/i } } } }, // 关联匹配当前经理管理的开发人员 { $lookup: { from: "employees", let: { managerId: "$_id" }, pipeline: [ { $match: { $expr: { $and: [ // 只匹配开发人员 { $regexMatch: { input: "$title", regex: /Developer/i } }, // 处理managerIDs为null/undefined的情况,确保匹配逻辑不报错 { $in: ["$$managerId", { $ifNull: ["$managerIDs", []] }] } ] } } } ], as: "managedDevelopers" } }, // 整理输出字段并统计管理人数 { $project: { _id: 0, fullName: { $concat: ["$firstName", " ", "$lastName"] }, title: 1, managedCount: { $size: "$managedDevelopers" } } } ])
代码说明:
- $match阶段:通过不区分大小写的正则匹配,排除所有职位包含"Developer"的员工,只保留经理。
- $lookup阶段:使用子管道关联员工集合,精准筛选出职位为开发人员且
managerIDs包含当前经理ID的记录,用$ifNull处理managerIDs为空的场景,避免查询报错。 - $project阶段:拼接经理的全名,用
$size获取关联到的开发人员数组长度(即管理人数),并整理输出所需字段。
Mongoose中的写法
假设你的Mongoose模型名为Employee,写法与原生MongoDB聚合基本一致:
const managers = await Employee.aggregate([ { $match: { title: { $not: { $regex: /Developer/i } } } }, { $lookup: { from: "employees", // 对应Mongoose模型的集合名,通常为小写复数形式 let: { managerId: "$_id" }, pipeline: [ { $match: { $expr: { $and: [ { $regexMatch: { input: "$title", regex: /Developer/i } }, { $in: ["$$managerId", { $ifNull: ["$managerIDs", []] }] } ] } } } ], as: "managedDevelopers" } }, { $project: { _id: 0, fullName: { $concat: ["$firstName", " ", "$lastName"] }, title: 1, managedCount: { $size: "$managedDevelopers" } } } ]); console.log(managers);
测试结果
针对示例数据,查询输出结果为:
[ { "title": "Manager App Development", "fullName": "Arnold Cliff", "managedCount": 1 } ]
(注:Richard的职位是Sr. Developer,属于开发人员范畴,不会被纳入经理列表;Arnold作为经理,管理了Andrew这1名开发人员)
内容的提问来源于stack exchange,提问作者SJaka
相关产品推荐
相关产品推荐

