You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB:如何计算studentMarkDetails数组中指定考试的科目总分

解决MongoDB中计算指定考试类型成绩总和的问题

需求与问题

需要从studentMarkDetails数组中计算指定考试类型(如quaterly)下所有科目的成绩总和,此前使用$unwind操作符时返回0条记录,期望输出格式如下:

{
    "_id": ObjectId("636efe231eeef2f46a31d7f4"),
    "sName": "Somu",
    "class": "tenth",
    "year": 2003,
    "examType": "quaterly",
    "total_marks": 300
}

对应的文档结构:

{
    "_id": ObjectId("636efe231eeef2f46a31d7f4"),
    "sName": "Somu",
    "class": "tenth",
    "year": 2003,
    "studentMarkDetails": [ 
        {
            "examType": "quaterly",
            "marks": {
                "Eng": 55,
                "Tel": 45,
                "Mat": 75,
                "Sec": 43,
                "Soc": 65
            }
        }, 
        {
            "examType": "halfyearly",
            "marks": {
                "Eng": 56,
                "Tel": 76,
                "Mat": 89,
                "Sec": 34,
                "Soc": 76
            }
        }, 
        {
            "examType": "final",
            "marks": {
                "Eng": 89,
                "Tel": 78,
                "Mat": 91,
                "Sec": 95,
                "Soc": 87
            }
        }
    ]
}

解决方案

方案一:无需$unwind的高效实现(推荐)

直接通过$filter筛选目标考试类型的数组元素,再用$reduce计算成绩总和,避免展开数组带来的性能损耗:

db.students.aggregate([
    // 可选:如果需要限定特定文档,比如指定_id,添加此$match阶段
    // { $match: { _id: ObjectId("636efe231eeef2f46a31d7f4") } },
    {
        $addFields: {
            targetExam: {
                $first: {
                    $filter: {
                        input: "$studentMarkDetails",
                        cond: { $eq: ["$$this.examType", "quaterly"] }
                    }
                }
            }
        }
    },
    {
        $addFields: {
            total_marks: {
                $reduce: {
                    input: { $objectToArray: "$targetExam.marks" },
                    initialValue: 0,
                    in: { $add: ["$$value", "$$this.v"] }
                }
            },
            examType: "$targetExam.examType"
        }
    },
    {
        $project: {
            sName: 1,
            class: 1,
            year: 1,
            examType: 1,
            total_marks: 1
        }
    }
])

方案二:正确使用$unwind的实现

如果一定要用$unwind,需先筛选出目标考试类型的数组元素再展开,避免无效数据被展开后过滤导致无结果:

db.students.aggregate([
    // 先筛选包含目标考试类型的文档,减少后续处理数据量
    { $match: { "studentMarkDetails.examType": "quaterly" } },
    {
        $addFields: {
            studentMarkDetails: {
                $filter: {
                    input: "$studentMarkDetails",
                    cond: { $eq: ["$$this.examType", "quaterly"] }
                }
            }
        }
    },
    // 此时数组仅含目标元素,展开后不会出现无关数据
    { $unwind: "$studentMarkDetails" },
    {
        $addFields: {
            total_marks: {
                $reduce: {
                    input: { $objectToArray: "$studentMarkDetails.marks" },
                    initialValue: 0,
                    in: { $add: ["$$value", "$$this.v"] }
                }
            },
            examType: "$studentMarkDetails.examType"
        }
    },
    {
        $project: {
            sName: 1,
            class: 1,
            year: 1,
            examType: 1,
            total_marks: 1
        }
    }
])

原问题原因分析

之前使用$unwind返回0条记录,通常是以下两种情况:

  1. 先执行$unwind再用$match筛选examType,若原文档中无匹配元素,展开后筛选会过滤掉所有数据;
  2. 筛选条件拼写错误(比如将文档中的quaterly误写为quarterly)。

内容的提问来源于stack exchange,提问作者Somasekhar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 10:25:22