如何在MongoDB的$group阶段生成固定长度的分类统计数组?
问题:统计2000-2010年间每年出版书籍总数及各分类数量
集合数据
假设MongoDB中存在books集合,数据插入语句如下:
db.books.insertMany([ { "name": "foo", "category": 0, publishedAt: ISODate("2008-09-14T00:00:00Z") }, { "name": "bar", "category": 1, publishedAt: ISODate("1945-08-17T00:00:00Z") }, { "name": "baz", "category": 1, publishedAt: ISODate("2002-03-01T00:00:00Z") }, { "name": "qux", "category": 2, publishedAt: ISODate("2002-01-21T00:00:00Z") }, { "name": "quux", "category": 4, publishedAt: ISODate("2018-04-18T00:00:00Z") }, ])
其中category为枚举整数,对应5个分类:0(Fiction)、1(Fantasy)、2(Classic)、3(Horror)、4(Comic)。
现有实现与当前输出
已完成年份分组及总数统计的聚合管道如下:
db.books.aggregate([ { $match: { publishedAt: { $gte: ISODate("2000-01-01T00:00:00Z"), $lt: ISODate("2011-01-01T00:00:00Z"), }, }, }, { $group: { _id: { $year: "$publishedAt", }, totalCount: { $count: {}, }, }, }, { $sort: { _id: 1, }, }, ]);
当前输出结果:
[ { _id: 2002, totalCount: 2, }, { _id: 2008, totalCount: 1, }, ]
需求补充
需要新增固定长度为5的categoryCount数组字段,数组下标对应category编号,值为该分类的书籍数量,期望输出如下:
[ { _id: 2002, totalCount: 2, categoryCount: [0, 1, 1, 0, 0], }, { _id: 2008, totalCount: 1, categoryCount: [1, 0, 0, 0, 0], }, ]
解决方案
修改聚合管道,在$group阶段分别统计每个分类的数量,再通过$project将这些统计值组合成目标数组:
db.books.aggregate([ { $match: { publishedAt: { $gte: ISODate("2000-01-01T00:00:00Z"), $lt: ISODate("2011-01-01T00:00:00Z"), }, }, }, { $group: { _id: { $year: "$publishedAt" }, totalCount: { $count: {} }, // 分别统计每个分类的数量 cat0: { $sum: { $cond: [{ $eq: ["$category", 0] }, 1, 0] } }, cat1: { $sum: { $cond: [{ $eq: ["$category", 1] }, 1, 0] } }, cat2: { $sum: { $cond: [{ $eq: ["$category", 2] }, 1, 0] } }, cat3: { $sum: { $cond: [{ $eq: ["$category", 3] }, 1, 0] } }, cat4: { $sum: { $cond: [{ $eq: ["$category", 4] }, 1, 0] } } }, }, { $project: { _id: 1, totalCount: 1, // 将分类统计值组合成数组 categoryCount: ["$cat0", "$cat1", "$cat2", "$cat3", "$cat4"] } }, { $sort: { _id: 1 }, }, ]);
说明
- $group阶段:通过
$cond判断每条文档的category值,匹配则计数1,否则0,再用$sum累加得到每个分类的年度总数。 - $project阶段:将每个分类的统计字段按顺序放入
categoryCount数组,确保下标与分类编号一一对应。 - 执行后即可得到符合需求的输出结果。
内容的提问来源于stack exchange,提问作者marknikky
相关产品推荐
相关产品推荐

