MongoDB多$group聚合查询:按学生、模块、考试整理成绩
MongoDB聚合管道优化:按学生维度整理成绩
数据库Schema
Module: { code_module: String, designation_module: String, année: Number, coéfficient: Number, listEpreuves: [{ code_epreuve: String, nature_epreuve: String, Resultat: [{ student_lastname: String, student_firstname: String, note: Number }] }] }
需求目标
按学生维度整理各模块下的考试成绩,输出示例如下:
"john smith": { "biologie": { "epreuve1": 12, "epreuve2": 13, "epreuve3": 7 }, "physique": { "epreuve1": 13, "epreuve2": 18 }, "Maths": { "epreuve1": 17, "epreuve2": 10, "epreuve3": 7.5 } }
现有问题聚合管道
你当前的聚合管道存在字段不匹配、嵌套数组处理不当等问题,原代码如下:
Moddulle.aggregate([ { $sort: { "listEpreuves.resultat.nom_etudiant": 1, "listEpreuves.resultat.prenom_etudiant": 1 } }, { $group: { _id: { nom: "$listEpreuves.resultat.nom_etudiant", prenom: "$listEpreuves.resultat.prenom_etudiant" }, modules: { $push: { code_moddulle: "$code_moddulle", designation_moddulle: "$designation_moddulle", epreuves: "$listEpreuves" } } } }, { $project: { _id: 0, nom: "$_id.nom", prenom: "$_id.prenom", modules: 1 } } ])
问题分析
- 字段名称不匹配:Schema中字段为
student_lastname/student_firstname,但代码中误用nom_etudiant/prenom_etudiant;集合名Module被错写为Moddulle,code_module错写为code_moddulle,导致数据无法正确关联。 - 嵌套数组未展开:
listEpreuves和Resultat都是嵌套数组,直接分组会将整个数组作为整体处理,无法拆分到单个学生、单个考试的维度。 - 分组逻辑错误:用数组字段作为分组
_id,无法生成唯一有效的分组键,导致结果不符合预期。
正确的聚合管道
Module.aggregate([ // 展开listEpreuves数组,将每个考试拆分为单独文档 { $unwind: "$listEpreuves" }, // 展开Resultat数组,将每个学生的成绩拆分为单独文档 { $unwind: "$listEpreuves.Resultat" }, // 按学生姓名分组,收集该学生所有成绩记录 { $group: { _id: { lastname: "$listEpreuves.Resultat.student_lastname", firstname: "$listEpreuves.Resultat.student_firstname" }, studentGrades: { $push: { module: "$designation_module", epreuve: "$listEpreuves.code_epreuve", note: "$listEpreuves.Resultat.note" } } } }, // 整理成绩结构,按模块分类 { $project: { _id: 0, studentName: { $concat: ["$_id.firstname", " ", "$_id.lastname"] }, grades: { $arrayToObject: { $map: { input: { $setUnion: ["$studentGrades.module"] }, as: "module", in: { k: "$$module", v: { $arrayToObject: { $map: { input: { $filter: { input: "$studentGrades", cond: { $eq: ["$$this.module", "$$module"] } } }, as: "grade", in: { k: "$$grade.epreuve", v: "$$grade.note" } } } } } } } } } }, // 将学生姓名设为顶级键,匹配目标输出格式 { $replaceRoot: { newRoot: { $arrayToObject: [[{ k: "$studentName", v: "$grades" }]] } } } ])
管道步骤说明
- $unwind:两次展开嵌套数组,确保每条文档对应单个学生的单个考试成绩,为后续分组提供基础。
- $group:按学生姓名分组,统一收集该学生的所有成绩记录,包含模块、考试编号和分数信息。
- $project:
- 拼接学生姓与名为完整姓名;
- 通过
$setUnion提取唯一模块列表,再用$map和$arrayToObject将每个模块下的考试成绩转换为键值对结构。
- $replaceRoot:将学生姓名作为顶级键,输出完全符合需求的格式。
内容的提问来源于stack exchange,提问作者Mouni19
相关产品推荐
相关产品推荐

