面试题答案
一键面试以下是使用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
}
}
]);
每一步聚合管道的逻辑及设计原因
-
$unwind阶段:
- 逻辑:使用
$unwind
操作符展开sub_transactions
数组,将每个文档中的sub_transactions
数组元素拆分成独立的文档。 - 设计原因:因为后续要对
sub_transactions
数组中的每个元素进行计算,展开后才能对每个sub_type
和sub_amount
进行独立处理,便于使用$sum
累加器函数。
- 逻辑:使用
-
$group阶段:
- 逻辑:使用
$group
操作符按_id: null
进行分组(因为我们只需要一个总的结果),并使用$sum
累加器函数分别计算income
类型的amount
总和(totalIncome
)、expense
类型的amount
总和(totalExpense
)、sub_type
为positive
的sub_amount
总和(totalPositiveSub
)以及sub_type
为negative
的sub_amount
总和(totalNegativeSub
)。这里使用$cond
操作符进行条件判断,当满足相应条件时才对amount
或sub_amount
进行累加,否则累加0。 - 设计原因:通过分组和累加,我们可以分别得到净收入计算所需的各项总和,为下一步计算净收入做好准备。
- 逻辑:使用
-
$project阶段:
- 逻辑:使用
$project
操作符计算净收入(netIncome
),通过$add
操作符将totalIncome
、totalPositiveSub
与totalNegativeSub
的差值以及totalExpense
的相反数相加得到净收入。同时,通过设置_id: 0
,不显示_id
字段。 - 设计原因:这一步将前面计算得到的各项总和进行最终的计算,得出净收入,并以合适的格式展示结果,不显示无用的
_id
字段。
- 逻辑:使用