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

如何在MongoDB中通过单查询/聚合实现多条件多维度计数

MongoDB Single Aggregation Query for Multi-Time-Dimension & Status User Counts

嘿,这个需求完全可以用MongoDB的聚合管道一次性搞定,不用跑多次查询!下面我会一步步拆解逻辑,最后给出完整的查询代码,刚好能输出你想要的嵌套格式。

核心思路

我们会通过以下几个聚合阶段来实现:

  1. 过滤目标状态:先筛选出状态为pending、completed、open的用户文档,排除无关数据。
  2. 标记时间维度:给每个文档添加一个time_dimension字段,判断它属于today/yesterday/last_week/last_month中的哪一类。
  3. 分组统计数量:按时间维度+状态组合分组,统计每组的用户数。
  4. 重塑结果结构:将统计结果转换成你期望的嵌套键值对格式。

完整聚合查询代码

db.users.aggregate([
  // 阶段1:过滤出目标状态的用户
  {
    $match: {
      status: { $in: ["pending", "completed", "open"] }
    }
  },
  // 阶段2:标记每个文档所属的时间维度
  {
    $addFields: {
      time_dimension: {
        $switch: {
          branches: [
            // 今日:从今天0点到明天0点
            {
              case: {
                $and: [
                  { $gte: ["$createdAt", { $startOfDay: new Date() }] },
                  { $lt: ["$createdAt", { $add: [{ $startOfDay: new Date() }, 86400000] }] }
                ]
              },
              then: "today"
            },
            // 昨日:昨天0点到今天0点
            {
              case: {
                $and: [
                  { $gte: ["$createdAt", { $subtract: [{ $startOfDay: new Date() }, 86400000] }] },
                  { $lt: ["$createdAt", { $startOfDay: new Date() }] }
                ]
              },
              then: "yesterday"
            },
            // 上周:ISO标准的上周(周一到周日)
            {
              case: {
                $and: [
                  { $eq: [{ $isoWeek: "$createdAt" }, { $subtract: [{ $isoWeek: new Date() }, 1] }] },
                  { $eq: [{ $isoYear: "$createdAt" }, { $isoYear: new Date() }] }
                ]
              },
              then: "last_week"
            },
            // 上月:处理跨年情况(比如1月的上月是去年12月)
            {
              case: {
                $let: {
                  vars: {
                    currentMonth: { $month: new Date() },
                    currentYear: { $year: new Date() },
                    docMonth: { $month: "$createdAt" },
                    docYear: { $year: "$createdAt" }
                  },
                  in: {
                    $or: [
                      // 非1月的情况:上月是当前月-1,年份相同
                      {
                        $and: [
                          { $ne: ["$$currentMonth", 1] },
                          { $eq: ["$$docMonth", { $subtract: ["$$currentMonth", 1] }] },
                          { $eq: ["$$docYear", "$$currentYear"] }
                        ]
                      },
                      // 1月的情况:上月是12月,年份是当前年-1
                      {
                        $and: [
                          { $eq: ["$$currentMonth", 1] },
                          { $eq: ["$$docMonth", 12] },
                          { $eq: ["$$docYear", { $subtract: ["$$currentYear", 1] }] }
                        ]
                      }
                    ]
                  }
                }
              },
              then: "last_month"
            }
          ],
          // 不属于以上维度的文档标记为other(可选,你可以根据需求调整)
          default: "other"
        }
      }
    }
  },
  // 阶段3:按时间维度+状态分组统计数量
  {
    $group: {
      _id: {
        time_dimension: "$time_dimension",
        status: "$status"
      },
      count: { $sum: 1 }
    }
  },
  // 阶段4:将同一时间维度的状态统计合并为对象
  {
    $group: {
      _id: "$_id.time_dimension",
      stats: {
        $push: {
          k: "$_id.status",
          v: "$count"
        }
      }
    }
  },
  // 阶段5:将stats数组转为键值对对象
  {
    $project: {
      _id: 0,
      dimension: "$_id",
      data: { $arrayToObject: "$stats" }
    }
  },
  // 阶段6:将所有维度的结果合并为顶层对象
  {
    $group: {
      _id: null,
      result: {
        $push: {
          k: "$dimension",
          v: "$data"
        }
      }
    }
  },
  // 阶段7:输出最终格式
  {
    $replaceRoot: {
      newRoot: { $arrayToObject: "$result" }
    }
  }
])

预期结果格式

执行后你会得到类似这样的输出:

{
  "today": {
    "pending": 10,
    "completed": 25,
    "open": 18
  },
  "yesterday": {
    "pending": 8,
    "completed": 30,
    "open": 15
  },
  "last_week": {
    "pending": 45,
    "completed": 120,
    "open": 88
  },
  "last_month": {
    "pending": 160,
    "completed": 420,
    "open": 310
  },
  "other": {
    // 不属于以上维度的统计(如果有的话)
  }
}

注意事项

  • 假设你的用户文档中存储时间的字段是createdAt,如果是其他字段(比如updatedAt),记得替换成对应的字段名。
  • 时间维度的判断逻辑可以根据你的业务需求调整(比如上周的定义是自然周还是过去7天?上面用的是ISO标准周,你可以改成过去7天的范围)。
  • 如果某个时间维度下没有对应状态的用户,该状态不会出现在结果中,你可以根据需要添加默认值(比如用$mergeObjects结合默认对象来补全)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:45:32