【问题标题】:MongoDB aggregation pipeline with loop带循环的 MongoDB 聚合管道
【发布时间】:2014-10-29 10:59:01
【问题描述】:

下面有这个聚合管道代码,我希望一年中的每一天都运行它!基本上计算一年中每一天的最低、最高和平均温度(“TEMP”字段)。目前我正在调用这段代码 365 次,传递一天的开始日期和结束日期。

显然这是非常低效的。有没有办法在 mongo 中循环它,以便它更快,并返回一个包含 365 个平均值、365 个最小值和 365 个最大值或类似的东西的数组。我使用时区库来导出开始日期和结束日期。

collection.aggregate([
    {
    $match:{$and:[
        {"UID"  : uid},
        {"TEMP" :{$exists:true}}
        {"site" : "SITE123"},
        {"updatedAt": {$gte : new Date(START_DATE_ARG), $lte : new Date(END_DATE_ARG)} }
        ]}
    },

    { "$group": {
        "_id": "$UID",
        "avg": { $avg: $TEMP },
        "min": { $min: $TEMP },
        "max": { $max: $TEMP }
        }
    }
], function(err, result){
                if (err){
                     cb(1, err);
                }
                else{
                    cb(0, result);
                }
            });
});

数据集如下所示

....
{UID: "123", TEMP: 11, site: "SITE123", updatedAt: ISODate("2014-09-12T21:55:19.326Z")}
{UID: "123", TEMP: 10, site: "SITE123", updatedAt: ISODate("2014-09-12T21:55:20.491Z")}
....

有什么想法吗?也许我们可以在聚合管道中传递一年中所有日子的所有时间戳?

谢谢!!

【问题讨论】:

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


    【解决方案1】:

    既然您可以简单地将日期作为分组键的一部分,为什么还要每天运行它呢?这就是 date aggregation operators 存在的目的,因此您可以一次按整个时间段内的时间范围进行聚合,而无需循环:

    collection.aggregate([
        { "$match":{
            "UID": uid,
            "TEMP":{ "$exists": true }
            "site" : "SITE123",
            "updatedAt": {
                "$gte": new Date(START_DATE_ARG), 
                "$lte": new Date(END_DATE_ARG)
            }}
        }},
    
        { "$group": {
            "_id": { 
                "uid": "$UID",
                "year": { "$year": "$updatedAt" },
                "month": { "$month": "$updatedAt" },
                "day": { "$dayOfMonth" }
            },
            "avg": { "$avg": "$TEMP" },
            "min": { "$min": "$TEMP" },
            "max": { "$max": "$TEMP" }
        }}
    ])
    

    或者可能只是将日期压缩为时间戳值。日期对象的日期数学小技巧:

    collection.aggregate([
        { "$match":{
            "UID": uid,
            "TEMP":{ "$exists": true }
            "site" : "SITE123",
            "updatedAt": {
                "$gte": new Date(START_DATE_ARG), 
                "$lte": new Date(END_DATE_ARG)
            }}
        }},
    
        { "$group": {
            "_id": { 
                "uid": "$UID",
                "date": {
                    "$subtract": [
                        { "$subtract": [ "$updatedAt", new Date("1970-01-01") ] },
                        { "$mod": [
                            { "$subtract": [ "$updatedAt", new Date("1970-01-01") ] },
                            1000 * 60 * 60 * 24
                        ]}
                    ]
                }
            },
            "avg": { "$avg": "$TEMP" },
            "min": { "$min": "$TEMP" },
            "max": { "$max": "$TEMP" }
        }}
    ])
    

    当然,您的“日期范围”现在是所有您需要出现在结果中的日期,因此您打算循环的所有内容的开始日期和结束日期。在任何一种情况下都会进行分组以反映“一天”,但当然您可以将其更改为您想要的任何间隔。

    另请注意,您无需在此处使用$and。默认情况下,查询 MongoDB 的“和”条件。唯一需要该运算符的情况是针对同一字段上的多个条件,否则这些条件将不是有效的 JSON/BSON。

    【讨论】:

    • 嗨,尼尔,谢谢!时区会发生什么?如果我使用内置的 $dayOfMonth、$week 运算符,是否有调整时区的最佳做法?
    • 好吧确实时区功能还没有实现。跟踪:jira.mongodb.org/browse/SERVER-6310 我想即使在查询中添加偏移量也无济于事,因为夏令时等原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    • 2020-09-20
    • 2021-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多