MST
星途 面试题库

面试题:MongoDB内嵌文档与数组结合的高效聚合查询优化

还是针对students集合,现在需要通过聚合操作,统计出每个年龄段(以5岁为一个区间,如20 - 24,25 - 29等)中,平均成绩最高的课程及其平均成绩,同时要求结果按年龄段升序排列。写出优化后的聚合管道操作语句,并说明在性能优化方面采取了哪些策略。
40.9万 热度难度
数据库MongoDB

知识考点

AI 面试

面试题答案

一键面试

假设students集合中的文档结构大致如下:

{
    "name": "张三",
    "age": 22,
    "scores": {
        "math": 85,
        "english": 90,
        "chinese": 78
    }
}

聚合管道操作语句如下:

db.students.aggregate([
    // 阶段1:将成绩字段展开为单个文档
    {
        $unwind: {
            path: "$scores",
            includeArrayIndex: "course"
        }
    },
    // 阶段2:计算每个学生每个课程的年龄段
    {
        $addFields: {
            ageGroup: {
                $subtract: [
                    { $subtract: ["$age", { $mod: ["$age", 5] }] },
                    0
                ]
            }
        }
    },
    // 阶段3:按年龄段和课程分组,计算平均成绩
    {
        $group: {
            _id: { ageGroup: "$ageGroup", course: "$course" },
            averageScore: { $avg: "$scores" }
        }
    },
    // 阶段4:按年龄段和平均成绩降序排列
    {
        $sort: {
            "_id.ageGroup": 1,
            "averageScore": -1
        }
    },
    // 阶段5:每个年龄段内保留平均成绩最高的课程
    {
        $group: {
            _id: "$_id.ageGroup",
            course: { $first: "$_id.course" },
            averageScore: { $first: "$averageScore" }
        }
    },
    // 阶段6:按年龄段升序排列最终结果
    {
        $sort: {
            "_id": 1
        }
    },
    // 阶段7:格式化输出结果
    {
        $project: {
            ageGroup: "$_id",
            course: 1,
            averageScore: 1,
            _id: 0
        }
    }
]);

性能优化策略

  1. 尽早过滤和投影:在聚合操作开始阶段,尽量减少数据量。虽然本题中未涉及明确的过滤,但在实际场景中,如果有条件可以尽早通过$match进行过滤。
  2. 合理使用$unwind:在需要将数组展开时,$unwind操作是必要的,但要注意其性能影响。在本题中,展开成绩数组以方便后续计算平均成绩。
  3. 减少中间数据量:通过$group$sort等操作,在每个阶段尽量减少传递到下一个阶段的数据量。例如,在计算完平均成绩后,通过$sort和再次$group来保留每个年龄段平均成绩最高的课程,避免不必要的数据传递。
  4. 利用索引:如果在age字段上创建索引,可以加速按年龄段分组和排序的操作。例如,可以使用db.students.createIndex({ age: 1 })来创建索引。