MST
星途 面试题库

面试题:MongoDB $sum累加器函数在嵌套聚合及条件累加场景下的应用

存在集合transactions,文档结构为{transaction_id: String, type: String, amount: Number, sub_transactions: [{sub_type: String, sub_amount: Number}]}。其中type有'income'和'expense'两种类型,sub_type有'positive'和'negative'两种类型。要求编写一个聚合查询,计算总的净收入(净收入 = 所有income类型的amount之和 - 所有expense类型的amount之和 + 所有sub_type为'positive'的sub_amount之和 - 所有sub_type为'negative'的sub_amount之和),请使用$sum累加器函数完成此复杂聚合操作,并详细解释每一步聚合管道的逻辑及为何这样设计。
18.3万 热度难度
数据库MongoDB

知识考点

AI 面试

面试题答案

一键面试

以下是使用MongoDB的聚合框架来完成此计算的聚合查询:

db.transactions.aggregate([
    // 步骤1:展开sub_transactions数组
    {
        $unwind: "$sub_transactions"
    },
    // 步骤2:使用$group进行分组并计算总和
    {
        $group: {
            _id: null,
            totalIncome: {
                $sum: {
                    $cond: [
                        { $eq: ["$type", "income"] },
                        "$amount",
                        0
                    ]
                }
            },
            totalExpense: {
                $sum: {
                    $cond: [
                        { $eq: ["$type", "expense"] },
                        "$amount",
                        0
                    ]
                }
            },
            totalPositiveSub: {
                $sum: {
                    $cond: [
                        { $eq: ["$sub_transactions.sub_type", "positive"] },
                        "$sub_transactions.sub_amount",
                        0
                    ]
                }
            },
            totalNegativeSub: {
                $sum: {
                    $cond: [
                        { $eq: ["$sub_transactions.sub_type", "negative"] },
                        "$sub_transactions.sub_amount",
                        0
                    ]
                }
            }
        }
    },
    // 步骤3:计算净收入
    {
        $project: {
            netIncome: {
                $add: [
                    "$totalIncome",
                    { $subtract: ["$totalPositiveSub", "$totalNegativeSub"] },
                    { $multiply: [-1, "$totalExpense"] }
                ]
            },
            _id: 0
        }
    }
]);

每一步聚合管道的逻辑及设计原因

  1. $unwind阶段

    • 逻辑:使用$unwind操作符展开sub_transactions数组,将每个文档中的sub_transactions数组元素拆分成独立的文档。
    • 设计原因:因为后续要对sub_transactions数组中的每个元素进行计算,展开后才能对每个sub_typesub_amount进行独立处理,便于使用$sum累加器函数。
  2. $group阶段

    • 逻辑:使用$group操作符按_id: null进行分组(因为我们只需要一个总的结果),并使用$sum累加器函数分别计算income类型的amount总和(totalIncome)、expense类型的amount总和(totalExpense)、sub_typepositivesub_amount总和(totalPositiveSub)以及sub_typenegativesub_amount总和(totalNegativeSub)。这里使用$cond操作符进行条件判断,当满足相应条件时才对amountsub_amount进行累加,否则累加0。
    • 设计原因:通过分组和累加,我们可以分别得到净收入计算所需的各项总和,为下一步计算净收入做好准备。
  3. $project阶段

    • 逻辑:使用$project操作符计算净收入(netIncome),通过$add操作符将totalIncometotalPositiveSubtotalNegativeSub的差值以及totalExpense的相反数相加得到净收入。同时,通过设置_id: 0,不显示_id字段。
    • 设计原因:这一步将前面计算得到的各项总和进行最终的计算,得出净收入,并以合适的格式展示结果,不显示无用的_id字段。