【问题标题】:Mongodb - query today's totals, week's totals, and month's totals in one queryMongodb - 一次查询今天的总数,一周的总数,一个月的总数
【发布时间】:2016-01-30 05:40:48
【问题描述】:

我的数据库中有如下所示的对象:

{
    "_id": ObjectId("563f8c320ef987c122aeeb4a"),
    "num": 1515,
    "createdAt": ISODate("2015-10-29T21:14:26.477Z"),
}

我想编写一个聚合,按特定 ID 对所有内容进行分组,并对今天、本周和本月的总数进行汇总,然后在一个查询中完成。我为此任务编写了三个单独的查询,但我想知道是否可以提高效率并在一个查询中完成。

编辑

有人提到 mapReduce 作为解决方案。这似乎是一个有前途的解决方案,但我无法从一个简单的查询中得到任何返回。以下是我尝试过的:

    var o = {};
    o.map = function () { emit( this.num, this.createdAt ) }
    o.reduce = function (k, vals) { return vals }
    o.query = {
        _id: req.user._id
    }
    Submission.mapReduce(o, function (err, results) {
        console.log(results)
    })

控制台记录一个空数组。我也尝试将_id 转换为猫鼬对象ID,但它仍然返回一个空数组。

【问题讨论】:

  • 您必须编写一个 map reduce 查询来实现这一点。
  • @Jaco 我尝试了 map reduce,但没有看到正确的结果。你熟悉在 mongoosejs 中使用 map reduce 吗?
  • 查看 Blakes 的回答,这是一种更优雅的方式来实现您想要的,而无需使用 map reduce 查询。

标签: node.js mongodb mongodb-query aggregation-framework


【解决方案1】:

这实际上是您期望输出看起来像什么的问题,因为任何聚合结果本质上都需要在最低级别分组,然后逐渐以更高的“粒度”分组,直到最大级别(“月”)是到达。这种暗示数据最终按“月”分组,除非您以其他方式对其进行分解。

本质上,渐进式$group

db.collection.aggregate([
    // First total per day. Rounding dates with math here
    { "$group": {
        "_id": {
            "$add": [
                { "$subtract": [
                    { "$subtract": [ "$createdAt", new Date(0) ] },
                    { "$mod": [
                        { "$subtract": [ "$createdAt", new Date(0) ] },
                        1000 * 60 * 60 * 24
                    ]}                        
                ]},
                new Date(0)
            ]
        },
        "week": { "$first": { "$week": "$createdAt" } },
        "month": { "$first": { "$month": "$createdAt" } },
        "total": { "$sum": "$num" }
    }},

    // Then group by week
    { "$group": {
        "_id": "$week",
        "month": { "$first": "$month" },
        "days": {
            "$push": {
                "day": "$_id",
                "total": "$total"
            }
        },
        "total": { "$sum": "$total" }
    }},

    // Then group by month
    { "$group": {
        "_id": "$month",
        "weeks": {
            "$push": {
                "week": "$_id",
                "total": "$total",
                "days": "$days"
            }
        },
        "total": { "$sum": "$total" }
    }}
])

因此,每天汇总的第一个级别之后的每个级别都会被逐步推入数组内容,因为它是“向上取整”值,然后总数也会在该级别求和。

如果您想要一个更平坦的输出,每天一条记录包含它的每周和每月总计以及当天总计,那么只需将两个 $unwind 语句附加到管道的末尾:

{ "$unwind": "$weeks" },
{ "$unwind": "$weeks.days" }

如果必须的话,还可以选择 $project 将“带点”字段输出到更扁平和可读的内容。

如果您使用它跨越“年”,那么至少从“周”级别在分组键中包含这样的操作,这样您就不可能组合来自不同年份的数据并且它们是分开的。

在舍入日期时使用"date math" 方法也是我自己的普遍偏好,因为它返回一个Date 对象,但在“天”之外的其他级别使用,您可以交替使用@987654323 @ 代替。

不需要mapReduce,因为这是相当直观的,并且一个月中的天数是有限的,这意味着在聚合时在内容中嵌套数组时的 BSON 限制不会被破坏。

【讨论】:

  • 这太完美了!而且真的很简单!谢谢
猜你喜欢
  • 2021-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多