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

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"] } }
        }
    }
])

核心需求

  1. 先筛选出date < myDate且article在指定列表内的所有文档,统计每日总数nbArticles
  2. 在上述筛选结果中,额外统计每日isPinned == 1的文档中唯一articleID的数量
  3. 不能使用$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] }
                    }
                }
            }
        }
    }
])

语句解释

  1. $match阶段:按需求过滤目标文档,减少后续计算的数据量。
  2. $group阶段:
    • 按格式化后的日期分组,确保每日数据聚合为一条。
    • nbArticles用$sum:1直接统计当日所有符合条件的文章总数。
    • pinnedArticleIDs通过$addToSet收集置顶文章的articleID,非置顶文档会加入null,但$addToSet会自动去重,同一日期的null仅保留一个。
  3. $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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 00:08:12