如何用MongoDB/Mongoose.js按月份分组统计标签数量?
解决MongoDB按月份分组统计各标签数量的问题
你说得没错,确实得用聚合查询来实现这个按月份统计各标签数量的需求~你的现有代码已经走对了第一步,但还没处理标签的拆分和多标签统计的部分,我来帮你调整一下:
核心思路
要实现按月份统计每个标签的数量,需要先把tags数组拆分成单个标签条目,再关联获取标签名称(毕竟你要的是tag1、tag2这样的标签名而非ObjectId),之后按「月份+标签」分组计数,最后把同一个月的所有标签统计结果合并成一个文档。
完整聚合查询代码
const monthAbbrs = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; // 月份缩写数组,按需改成全称也可以 Post.aggregate([ // 1. 过滤时间范围(注意字段是date还是created_at,你的集合结构里是date,这里统一用date) { $match: { date: { $gt: oneYearFromNow, $lt: dateNow } } }, // 2. 拆分tags数组,每个标签对应一条记录 { $unwind: "$tags" }, // 3. 关联Tag集合,获取标签的名称(假设Tag集合有name字段,根据你的实际字段调整) { $lookup: { from: "tags", // Tag集合的名称 localField: "tags.id", foreignField: "_id", as: "tagInfo" } }, // 4. 把关联后的tagInfo数组拆成单个对象,方便后续使用 { $unwind: "$tagInfo" }, // 5. 先按「年份+月份+标签名」分组,统计每个月每个标签的数量 { $group: { _id: { year: { $year: "$date" }, month: { $month: "$date" }, tagName: "$tagInfo.name" }, count: { $sum: 1 } } }, // 6. 再按「年份+月份」分组,把同一个月的所有标签统计结果收集成数组 { $group: { _id: { year: "$_id.year", month: "$_id.month" }, tagCounts: { $push: { k: "$_id.tagName", v: "$count" } } } }, // 7. 把tagCounts数组转成键值对对象,同时处理月份显示 { $project: { _id: 0, month: { $arrayElemAt: [monthAbbrs, "$_id.month"] }, // 把标签计数数组转成对象,比如{tag1:5, tag2:80} tagCounts: { $arrayToObject: "$tagCounts" } } }, // 8. 把tagCounts里的字段展开到根文档(可选,如果你不想嵌套在tagCounts里的话) { $replaceRoot: { newRoot: { $mergeObjects: ["$tagCounts", { month: "$month" }] } } }, // 可选:按月份排序 { $sort: { "_id.year": 1, "_id.month": 1 } } ])
关键步骤说明
- $unwind:拆分
tags数组是核心,因为每个Post可能包含多个标签,只有拆分后才能单独统计每个标签的数量。 - $lookup:关联Tag集合是为了获取标签的名称,毕竟直接用ObjectId作为字段名没意义,如果你已经在Post的tags里存储了标签名称,这一步可以省略。
- 两次$group:第一次分组是统计「每个月每个标签」的数量,第二次是把同一个月的所有标签统计结果合并,方便转成你要的键值对格式。
- $arrayToObject + $replaceRoot:这两步是把标签计数的数组转成
tag1:5这样的字段格式,实现你想要的输出结构。
注意事项
- 确保Tag集合的名称和字段名(比如
name)和你的实际数据一致,否则需要调整$lookup的参数。 - 时间过滤部分,你的原代码里混用了
created_at和date,要和集合里的字段保持一致(你的集合结构里是date)。 - 月份字符串数组可以按需改成全称(比如你的原数组
monthStrings),只要对应索引正确就行。
内容的提问来源于stack exchange,提问作者saeede nikfardin
相关产品推荐
相关产品推荐

