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

Express+Mongoose实现MongoDB月度数据统计与占比计算优化

需求说明

我的MongoDB数据库中有如下格式的数据:

{ id: 'ran1', code: 'ABC1', createdAt: 'Sep 1 2022', count: 5 } 
{ id: 'ran2', code: 'ABC1', createdAt: 'Sep 2 2022', count: 3 } 
{ id: 'ran3', code: 'ABC2', createdAt: 'Sep 1 2022', count: 2 } 
{ id: 'ran4', code: 'ABC1', createdAt: 'Oct 1 2022', count: 1 } 
{ id: 'ran5', code: 'ABC1', createdAt: 'Oct 2 2022', count: 2 } 
{ id: 'ran6', code: 'ABC2', createdAt: 'Oct 1 2022', count: 1 }

我需要筛选出10月的所有数据,同时计算每个code的当月总count,以及占比公式:(当月总count - 上月总count)/当月总count * 100,期望输出格式如下:

{code: 'ABC1', totalCount: 3 , percent: (3-8)/3 * 100 } 
{code: 'ABC2', totalCount: 1, percent: -100}

目前我通过两次聚合查询再做映射匹配的方式实现,但觉得有更优方案,现有代码如下:

const { filterDate, shop } = req.query;
const splittedFilter = filterDate.split("-");

const query = {
  shopUrl: { $regex: shop, $options: "i" },
  createdAt: {
    $gte: new Date(splittedFilter[0]),
    $lte: new Date(splittedFilter[1]),
  },
};

const currentCodes = await BlockedCode.aggregate([
  {
    $match: query,
  },
  {
    $group: {
      _id: "$discountCode",
      totalCount: { $sum: "$count" },
    },
  },
]);
const prevQuery = {
  shopUrl: { $regex: shop, $options: "i" },
  createdAt: {
    $gte: new Date(splittedFilter[2]),
    $lte: new Date(splittedFilter[3]),
  },
};
const previousCodes = await BlockedCode.aggregate([
  {
    $match: prevQuery,
  },
  {
    $group: {
      _id: "$discountCode",
      totalCount: { $sum: "$count" },
    },
  },
]);

const result = currentCodes.map((code) => {
  const foundPrevCode = previousCodes.find((i) => i._id === code._id);

  if (foundPrevCode?._id) {
    const prevCount = foundPrevCode?.totalCount;
    const currCount = code?.totalCount;
    const difference = currCount - prevCount;
    const percentage = (difference / currCount) * 100;
    return { ...code, percentage };
  } else {
    return { ...code, percentage: 100 };
  }
});
优化方案:单次聚合管道实现

可以通过一次聚合查询完成所有计算,避免两次数据库请求和客户端侧的匹配逻辑,具体实现步骤如下:

  1. 匹配目标月份及上月数据:筛选出当前月和上月的所有符合条件的数据
  2. 按code分组,区分当月和上月的count总和:分组后用条件累加分别计算当月和上月的总count
  3. 过滤当月有效数据:仅保留当月有count的code
  4. 计算占比并格式化输出:基于分组结果计算占比,同时处理上月无数据的情况

具体聚合代码如下:

const { filterDate, shop } = req.query;
const splittedFilter = filterDate.split("-");

// 解析当前月和上月的日期范围
const currStart = new Date(splittedFilter[0]);
const currEnd = new Date(splittedFilter[1]);
const prevStart = new Date(splittedFilter[2]);
const prevEnd = new Date(splittedFilter[3]);

const result = await BlockedCode.aggregate([
  // 第一步:匹配当前月和上月的所有数据
  {
    $match: {
      shopUrl: { $regex: shop, $options: "i" },
      createdAt: {
        $gte: prevStart,
        $lte: currEnd
      }
    }
  },
  // 第二步:按code分组,分别计算当月和上月的总count
  {
    $group: {
      _id: "$code", // 注意:需与数据库实际字段名一致,原代码用的是$discountCode
      totalCount: {
        $sum: {
          $cond: [
            { $and: [
              { $gte: ["$createdAt", currStart] },
              { $lte: ["$createdAt", currEnd] }
            ] },
            "$count",
            0
          ]
        }
      },
      prevTotalCount: {
        $sum: {
          $cond: [
            { $and: [
              { $gte: ["$createdAt", prevStart] },
              { $lte: ["$createdAt", prevEnd] }
            ] },
            "$count",
            0
          ]
        }
      }
    }
  },
  // 第三步:过滤出当月有数据的code
  {
    $match: {
      totalCount: { $gt: 0 }
    }
  },
  // 第四步:计算占比并格式化输出字段
  {
    $project: {
      _id: 0,
      code: "$_id",
      totalCount: 1,
      percent: {
        $cond: [
          { $eq: ["$prevTotalCount", 0] },
          100, // 上月无数据时占比为100%
          { $multiply: [ { $divide: [ { $subtract: ["$totalCount", "$prevTotalCount"] }, "$totalCount" ] }, 100 ] }
        ]
      }
    }
  }
]);

优化点说明

  • 减少数据库请求:从两次聚合查询缩减为一次,降低网络开销和数据库负载
  • 逻辑内聚:所有计算逻辑在数据库侧完成,避免客户端侧的数组遍历匹配,数据量越大性能提升越明显
  • 容错一致:通过$cond处理上月无数据的场景,返回100%占比,与原业务逻辑完全对齐

内容的提问来源于stack exchange,提问作者Shahamar Rahman Himel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 04:01:41