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

MongoDB单聚合查询近5年每月可见Shop数量需求

问题

我有如下Shop集合的Schema定义:

var schema = new Schema({
    name: {
        type: String,
    },
    // 其他字段...
    created_at: {
        type: Date,
    },
    lost_at: {
        type: Date,
    },
});
mongoose.model("Shop", schema);

定义Shop仅在created_at至lost_at的时间段内为“可见”,需要生成如下格式的统计结果:

[{ _id: null, year: '2023', month: '2', count: 0 },
{ _id: null, year: '2023', month: '3', count: 3},
{ _id: null, year: '2023', month: '4', count: 8},
{ _id: null, year: '2023', month: '5', count: 16}
...]

要求:

  • 年份和月份需覆盖近5年的所有年月,即使对应年月无相关记录也要保留,count填0
  • count为对应月份的可见Shop数量
  • 之前通过Node.js任务用两次聚合实现,但现在需要单聚合查询方案用于MongoDB Charts展示

单聚合查询方案

以下是兼容MongoDB 5.0+的单聚合查询代码,会自动生成近5年的年月序列并统计对应月份的可见店铺数:

db.Shop.aggregate([
  // 阶段1:生成近5年的所有年月序列
  {
    $documents: (function() {
      const result = [];
      const now = new Date();
      // 从5年前的当月开始,生成连续60个月的年月数据
      for (let i = 0; i < 60; i++) {
        const date = new Date(now.getFullYear() - 5, now.getMonth(), 1);
        date.setMonth(date.getMonth() + i);
        result.push({
          year: date.getFullYear().toString(),
          month: (date.getMonth() + 1).toString()
        });
      }
      return result;
    })()
  },
  // 阶段2:左连接Shop集合,匹配当月可见的店铺
  {
    $lookup: {
      from: "Shop",
      let: {
        targetYear: { $toInt: "$year" },
        targetMonth: { $toInt: "$month" }
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $and: [
                // 店铺创建时间不晚于当月第一天(确保当月已存在)
                { $lte: [ "$created_at", { $dateFromParts: { year: "$$targetYear", month: "$$targetMonth", day: 1 } } ] },
                // 店铺未失效(lost_at为空)或失效时间不早于当月第一天
                { $or: [
                  { $gte: [ "$lost_at", { $dateFromParts: { year: "$$targetYear", month: "$$targetMonth", day: 1 } } ] },
                  { $eq: [ "$lost_at", null ] }
                ] }
              ]
            }
          }
        },
        { $count: "matched" }
      ],
      as: "shopCounts"
    }
  },
  // 阶段3:格式化结果,无匹配则count设为0
  {
    $project: {
      _id: null,
      year: "$year",
      month: "$month",
      count: {
        $ifNull: [ { $arrayElemAt: [ "$shopCounts.matched", 0 ] }, 0 ]
      }
    }
  },
  // 阶段4:按年月升序排序
  {
    $sort: {
      year: 1,
      month: 1
    }
  }
])

低版本MongoDB兼容方案(替换阶段1)

如果你的MongoDB版本低于5.0,不支持$documents,可以用以下代码替换阶段1,通过$range生成月份偏移量来生成年月序列:

// 替换原阶段1的代码
{
  $addFields: {
    monthOffsets: { $range: [0, 60] }
  }
},
{
  $unwind: "$monthOffsets"
},
{
  $project: {
    year: {
      $toString: {
        $year: {
          $dateAdd: {
            startDate: { $dateFromParts: { year: { $subtract: [ { $year: new Date() }, 5 ] }, month: { $month: new Date() }, day: 1 } },
            unit: "month",
            amount: "$monthOffsets"
          }
        }
      }
    },
    month: {
      $toString: {
        $month: {
          $dateAdd: {
            startDate: { $dateFromParts: { year: { $subtract: [ { $year: new Date() }, 5 ] }, month: { $month: new Date() }, day: 1 } },
            unit: "month",
            amount: "$monthOffsets"
          }
        }
      }
    }
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 16:05:28