MongoDB聚合:如何为addToSet字段添加条件实现数据统计
MongoDB聚合统计:每日文章总数及唯一置顶文章数
我需要统计每日符合条件的文章总数,以及当日所有置顶(isPinned == 1)文章的唯一articleID数量。由于数据量极大,不能使用$facet(会触发BSON大小限制)。我之前尝试的写法有问题,现提供示意代码、需求说明、期望输出和示例数据,寻求正确的聚合语句。
示意代码(不生效,仅作需求参考)
Collection.aggregate([ { $match: { date: { $lt: myDate }, article: { $in: articles } } }, { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, nbArticles: { $sum: 1 }, distinctPinnedArticles: { $cond: { if: { $eq: ['$isPinned', 1] }, then: { $addToSet: "$articleID" }, else: null } } } }, { }, { $project: { _id: 0, nbArticles: 1, distinctPinnedArticlesLength: { $size: { $setUnion: ["$distinctPinnedArticles"] } } } } ])
核心需求
- 先筛选出
date < myDate且article在指定列表内的所有文档,统计每日总数nbArticles - 在上述筛选结果中,额外统计每日
isPinned == 1的文档中唯一articleID的数量 - 不能使用
$facet,避免BSON超过16MB限制
期望输出格式
[ { nbArticles: 1000, distinctPinnedArticles: 500 }, { nbArticles: 600, distinctPinnedArticles: 300 }, { nbArticles: 1300, distinctPinnedArticles: 800 }, { nbArticles: 70, distinctPinnedArticles: 40 } ]
示例数据
[ { _id: ObjectId("6146b91a4e98146bfba070a9"), article: 'article1', articleID: 'article_1', isPinned: 0, date: ISODate("2022-02-15T18:24:37.000Z") }, { _id: ObjectId("6146b95c4e98146bfba070aa"), article: 'article2', articleID: 'article_2', isPinned: 0, date: ISODate("2021-12-01T10:45:22.000Z") }, { _id: ObjectId("6146b9834e98146bfba070ab"), article: 'article1', articleID: 'article_3', isPinned: 1, date: ISODate("2022-01-05T08:12:59.000Z") }, { _id: ObjectId("6146b9ae4e98146bfba070ac"), article: 'article2', articleID: 'article_4', isPinned: 0, date: ISODate("2021-11-10T14:30:45.000Z") }, { _id: ObjectId("6146b9d54e98146bfba070ad"), article: 'article1', articleID: 'article_5', isPinned: 1, date: ISODate("2022-03-01T09:51:02.000Z") }, { _id: ObjectId("6146ba014e98146bfba070ae"), article: 'article3', articleID: 'article_6', isPinned: 1, date: ISODate("2021-12-20T16:08:31.000Z") }, { _id: ObjectId("6146ba2b4e98146bfba070af"), article: 'article2', articleID: 'article_7', isPinned: 1, date: ISODate("2022-01-25T12:57:09.000Z") }, { _id: ObjectId("6146ba544e98146bfba070b0"), article: 'article3', articleID: 'article_8', isPinned: 1, date: ISODate("2022-02-10T17:39:18.000Z") }, { _id: ObjectId("6146ba7c4e98146bfba070b1"), article: 'article1', articleID: 'article_9', isPinned: 0, date: ISODate("2021-11-30T08:20:57.000Z") } ]
解决方案
可以通过在$group阶段同时收集所有置顶文章的articleID集合,再在$project阶段计算集合大小来实现,避免使用$facet。具体聚合语句如下:
Collection.aggregate([ // 第一步:筛选符合条件的文档 { $match: { date: { $lt: myDate }, article: { $in: articles } } }, // 第二步:按日期分组,统计总数+收集置顶articleID集合 { $group: { _id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, nbArticles: { $sum: 1 }, // 仅当isPinned为1时加入articleID,否则加入null($addToSet自动去重null) pinnedArticleIDs: { $addToSet: { $cond: { if: { $eq: ["$isPinned", 1] }, then: "$articleID", else: null } } } } }, // 第三步:过滤null并计算唯一置顶文章数 { $project: { _id: 0, nbArticles: 1, distinctPinnedArticles: { $size: { $filter: { input: "$pinnedArticleIDs", cond: { $ne: ["$$this", null] } } } } } } ])
语句解释
$match阶段:按需求过滤目标文档,减少后续计算的数据量。$group阶段:- 按格式化后的日期分组,确保每日数据聚合为一条。
nbArticles用$sum:1直接统计当日所有符合条件的文章总数。pinnedArticleIDs通过$addToSet收集置顶文章的articleID,非置顶文档会加入null,但$addToSet会自动去重,同一日期的null仅保留一个。
$project阶段:- 用
$filter移除集合中的null值,再用$size计算剩余元素的数量,得到当日唯一置顶文章的数量。
- 用
示例数据运行结果
针对提供的示例数据,运行后会得到以下结果:
[ { "nbArticles": 1, "distinctPinnedArticles": 0 }, // 2021-11-10 { "nbArticles": 1, "distinctPinnedArticles": 0 }, // 2021-11-30 { "nbArticles": 1, "distinctPinnedArticles": 0 }, // 2021-12-01 { "nbArticles": 1, "distinctPinnedArticles": 1 }, // 2021-12-20 { "nbArticles": 1, "distinctPinnedArticles": 1 }, // 2022-01-05 { "nbArticles": 1, "distinctPinnedArticles": 1 }, // 2022-01-25 { "nbArticles": 1, "distinctPinnedArticles": 1 }, // 2022-02-10 { "nbArticles": 1, "distinctPinnedArticles": 0 }, // 2022-02-15 { "nbArticles": 1, "distinctPinnedArticles": 1 } // 2022-03-01 ]
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

