首页 文章

MongoDB聚合框架 - 如何执行多个$ group查询

提问于
浏览
0

我有以下MongoDB聚合查询,它查找指定月份内的所有记录,$按天分组记录,然后返回每天的平均价格 . 我还希望返回整个月的平均价格 . 我可以通过使用多个$组来实现这一点,如果是这样,怎么做?

PriceHourly.aggregate([
                { $match: { date: { $gt: start, $lt: end } } },
                { $group: { 
                    _id: "$day", 
                    price: { $avg: '$price' },
                    system_demand: { $avg: '$system_demand'}
                }}
        ], function(err, results){

                results.forEach(function(r) {
                    r.price          = Helpers.round_price(r.price);
                    r.system_demand  = Helpers.round_price(r.system_demand);
                });
                console.log("Results Length: "+results.length, results);
                res.jsonp(results);
        }); // PriceHourly();

这是我的模型:

// Model
var PriceHourlySchema = new Schema({
    created: {
        type: Date,
        default: Date.now
    },
    day: {
        type: String,
        required: true,
        trim: true
    },
    hour: {
        type: String,
        required: true,
        trim: true
    },
    price: {
        type: Number,
        required: true
    },
    date: {
        type: Date,
        required: true
    }
}, 
{ 
    autoIndex: true 
});

1 回答

  • 1

    简短的回答是“仅仅扩展您的日期范围以包括一个月内的所有日期有什么问题?”,因此您只需更改即可获得结果 .

    你能“嵌套”分组阶段吗?是的,您可以向管道添加其他阶段,这就是管道的用途 . 因此,如果您首先想要每天“平均”,然后在一个月的所有日子里取平均值,您可以这样形成:

    PriceHourly.aggregate([
       { "$match": { 
           "date": { 
               "$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
           }  
       }},
       { "$group": { 
           "_id": "$day", 
           "price": { "$avg": "$price" },
           "system_demand": { "$avg": "$system_demand" }
       }},
       { "$group": { 
           "_id": null, 
           "price": { "$avg": "$price" },
           "system_demand": { "$avg": "$system_demand" }
       }}
    ])
    

    即使这可能是相当多余的,因为可以说这可以用一个单一的群体陈述来完成 .

    但是这个架构有更长的评论 . 除了获得平均值或模式意图包含的内容之外,您实际上并没有说明您正在做的事情的大部分目的 . 所以我想描述一些可能有点不同的东西 .

    假设您有一个包含“产品”的集合,“键入”“当前价格”和“时间戳”作为“价格”“更改”的日期 . 我们称之为“PriceChange” . 因此,每次发生此事件时,都会创建一个新文档 .

    {
        "product": "ABC",
        "type": 2,
        "price": 110,
        "timestamp": ISODate("2014-04-01T00:08:38.360Z")
    }
    

    这可能会在一小时,一天或任何情况下多次改变 .

    因此,如果您对本月每件产品的“平均”价格感兴趣,您可以这样做:

    PriceChange.aggregate([
        { "$match": {
            "timestamp": { 
                "$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
            }
        }},
        { "$group": {
            "_id": "$product",
            "price_avg": { "$avg": "$price" }
        }}
    ])
    

    此外,如果没有任何其他字段,您可以获得每月每个产品的平均价格:

    PriceChange.aggregate([
        { "$match": {
            "timestamp": { 
                "$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
            }
        }},
        { "$group": {
            "_id": {
                "day": { "$dayOfMonth": "$timestamp" },
                "product": "$product"
            },
            "price_avg": { "$avg": "$price" }
        }}
    ])
    

    或者你甚至可以在一整年内获得每月的 last 价格:

    PriceChange.aggregate([
        { "$match": {
            "timestamp": { 
                "$gte": new Date("2013-01-01"), "$lt": new Date("2014-01-01")
            }
        }},
        { "$group": {
            "_id": {
                "date": {
                    "year": { "$year" : "$timestamp" },
                    "month": { "$month": "$timestamp" }
                },
                "product": "$product"
            },
            "price_last": { "$last": "$price" }
        }}
    ])
    

    所以这些是你可以使用Date Aggregation Operators中的构建来实现各种结果的一些事情 . 这些甚至可以帮助收集这些信息,以便写入新的"pre-aggregated"集合,用于更快的分析 .

    我想有一种方法可以使用mapReduce将“运行”平均值与所有价格相结合 . 再次从我的样本:

    PriceHourly.mapReduce(
        function () {
            emit( this.timestamp.getDate(), this.price )
        },
        function (key, values) {
            var sum = 0;
            values.forEach(function(value) {
                sum += value;
            });
            return ( sum / values.length );
        },
        { 
            "query": {
                "timestamp": {
                    "$gte": new Date("2014-03-01"), "$lt": new Date("2014-04-01")
                }
            },
            "out": { "inline": 1 },
            "scope": { "running": 0, "counter": 0 },
            "finalize": function(key,value) {
                running += value;
                counter++;
                return { "dayAvg": value, "monthAvg": running / counter };
            }
        }
    )
    

    这将返回这样的事情:

    {
        "results" : [
            {
                "_id" : 1,
                "value" : {
                    "dayAvg" : 105,
                    "monthAvg" : 105
                }
            },
            {
                "_id" : 2,
                "value" : {
                    "dayAvg" : 110,
                    "monthAvg" : 107.5
               }
            }
        ],
    }
    

    但是,如果您希望看到日和月的离散值,那么如果不运行单独的查询则不可能 .

相关问题