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

MongoDB跨Transaction与Category集合构建聚合管道:获取支出类Top3分类占比

MongoDB交易分类占比统计聚合方案

需求

  • 基于MongoDB的Transaction和Category集合构建聚合管道
  • Transaction集合字段:amount(金额)、categoryID(分类ID)、description(描述)
  • Category集合字段:type(分类类型)、icon(图标)、color(颜色)
  • 核心要求:筛选出分类类型为Expense的交易,计算占比最高的3个分类及「其他」分类的金额占比,且金额仅存储在Transaction集合中

首次尝试(不符合金额存储要求)

最初直接基于Category集合聚合,但忽略了金额仅存在于Transaction集合的要求,逻辑错误:

Category.aggregate([
{
    $match: {
        type: 'expense'
    }
},
{
    $group: {
        _id: "$name",
        amount: { $sum: "$amount" }
    }
},
{
    $group: {
        _id: null,
        totalExpense: { $sum: "$amount" },
        categories: {
            $push: {
                name: "$_id",
                amount: "$amount"
            }
        }
    }
},
{
    $project: {
        _id: 0,
        categories: {
            $map: {
                input: "$categories",
                as: "category",
                in: {
                    name: "$$category.name",
                    percent: { $multiply: [{ $divide: ["$$category.amount", "$totalExpense"] }, 100] }
                }
            }
        }
    }
},
{
    $unwind: "$categories"
},
{
    $sort: { "categories.percent": -1 }
},
{
    $limit: 3
}
])

二次尝试(返回空数组)

改为基于Transaction集合关联聚合,但因逻辑错误返回空数组,主要问题:

  1. 按category.type分组会将所有Expense交易归为同一组,无法统计单个分类的占比
  2. 在$project阶段错误使用聚合操作符$sum,该阶段不支持此类计算
Transaction.aggregate([
// 关联Transaction与Category集合
{
  $lookup: {
    from: 'Category',
    localField: 'categoryID',
    foreignField: '_id',
    as: 'category',
  },
},
// 展开category数组
{
  $unwind: '$category',
},
// 筛选分类类型为Expense的交易
{
  $match: {
    'category.type': 'Expense',
  },
},
// 按分类类型分组并计算占比(逻辑错误)
{
  $group: {
    _id: '$category.type',
    total: { $sum: '$amount' },
    count: { $sum: 1 },
  },
},
{
  $project: {
    _id: 0,
    category: '$_id',
    percentage: {
      $multiply: [{ $divide: ['$count', { $sum: '$count' }] }, 100],
    },
  },
},
// 按占比降序排序
{
  $sort: { percentage: -1 },
},
// 取前3个分类
{
  $limit: 3,
},
// 将剩余分类合并为其他(逻辑错误)
{
  $group: {
    _id: null,
    top3: { $push: '$$ROOT' },
    others: { $sum: { $subtract: [100, { $sum: '$top3.percentage' }] } },
  },
},
{
  $project: {
    top3: 1,
    others: { category: 'Others', percentage: '$others' },
  },
},
])

最终可行聚合管道

以下管道满足所有需求,关键步骤包括:转换categoryID类型确保关联成功、按分类名称分组计算总金额、排序后取前3并计算「其他」分类的占比:

Transaction.aggregate([
{
  $match: {
    userID: { $eq: UserID },
    type: 'Expense',
  },
},
// 将categoryID转换为ObjectId,确保与Category集合的_id类型匹配
{
  $addFields: { categoryID: { $toObjectId: '$categoryID' } },
},
// 关联Category集合获取分类信息
{
  $lookup: {
    from: 'categories',
    localField: 'categoryID',
    foreignField: '_id',
    as: 'category_info',
  },
},
// 展开分类信息数组
{
  $unwind: '$category_info',
},
// 按分类名称分组,计算每个分类的总交易金额
{
  $group: {
    _id: '$category_info.name',
    amount: { $sum: '$amount' },
  },
},
// 按总金额降序排序
{
  $sort: {
    amount: -1,
  },
},
// 计算所有Expense交易的总金额,并保存各分类数据
{
  $group: {
    _id: null,
    total: { $sum: '$amount' },
    data: { $push: '$$ROOT' },
  },
},
// 生成前3个分类的占比数据,以及「其他」分类的占比
{
  $project: {
    results: {
      $map: {
        input: {
          $slice: ['$data', 3],
        },
        in: {
          category: '$$this._id',
          percentage: {
            $round: {
              $multiply: [{ $divide: ['$$this.amount', '$total'] }, 100],
            },
          },
        },
      },
    },
    others: {
      $cond: {
        if: { $gt: [{ $size: '$data' }, 3] },
        then: {
          amount: {
            $subtract: [
              '$total',
              {
                $sum: {
                  $slice: ['$data.amount', 3],
                },
              },
            ],
          },
          percentage: {
            $round: {
              $multiply: [
                {
                  $divide: [
                    {
                      $subtract: [
                        '$total',
                        { $sum: { $slice: ['$data.amount', 3] } },
                      ],
                    },
                    '$total',
                  ],
                },
                100,
              ],
            },
          },
        },
        else: {
          amount: null,
          percentage: null,
        },
      },
    },
  },
},
])

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 17:45:29