需要在汇总

时间:2015-11-14 12:08:19

标签: mongodb mongodb-query aggregation-framework

从MongoDB聚合返回每小时的单个记录,我还需要知道字段中的“模式”或最常出现的值。

到目前为止,我已经选择了两个日期之间的记录集,并且每小时返回一条记录,包括字段值的平均值。但我还需要最频繁的类别,其中类别编号字段包含1,2,3或4。

var myName = "CollectionName"
//schema for mongoose
var mySchema = new Schema({
    dt: Date,
    value: Number,
    category: Number
});

var myDB = mongoose.createConnection('mongodb://localhost:27017/MYDB');

myDBObj = myDB.model(myName, evalSchema, myName);

以下$ group中的日期数学创建当天每小时的记录,$ avg平均价格字段....

但我不知道如何在类别字段中返回最频繁出现的1,2,3或4 ...没有$ mode聚合运算符,我收到错误"exception: unknown group operator '$mode'"

myDBObj.aggregate([
        {
            $match: { "dt": { $gt: new Date("October 13, 2010 12:00:00"), $lt: new Date("November 13, 2010 12:00:00") } }
        },{
            $group:  {
            "_id": {
                "dt": {
                    "$add": [
                        {
                            "$subtract": [
                                { "$subtract": ["$dt", new Date(0)] },
                                {
                                    "$mod": [
                                        { "$subtract": ["$dt", new Date(0)] },
                                        3600000//1000 * 60 * 60
                                    ]
                                }
                            ]
                        },
                        new Date(0)
                    ]
                }
            }, 
            "price": { "$avg": "$price" },
            "category" : { "$mode" : "$category"}
        }
        }], function (err, data) { if (err) { return next(err); } res.json(data); });

有没有办法返回字段中包含的最常见值?

我是否需要使用map-reduce功能?我如何将它们与上面的每小时聚合结合起来?谢谢你的帮助。

1 个答案:

答案 0 :(得分:2)

嗯,你不能只是“弥补”。 $mode运算符不是攻击运算符,您可以使用的唯一内容是those that actually exist

因此,为了在最多发生的分组时间段内返回类别值,必须首先对每个值进行分组并返回出现次数。然后,您可以按该计数对这些结果进行排序,并返回在该时间段内记录最高计数的类别值:

    // Filter dates
    { "$match": { 
        "dt": { 
            "$gt": new Date("October 13, 2010 12:00:00"), 
            "$lt": new Date("November 13, 2010 12:00:00")
        } 
    }},

    // Group by hour and category, with avg and count
    { "$group": {
        "_id": {
            "dt": {
                "$add": [
                    {
                        "$subtract": [
                            { "$subtract": ["$dt", new Date(0)] },
                            {
                                "$mod": [
                                    { "$subtract": ["$dt", new Date(0)] },
                                    3600000//1000 * 60 * 60
                                ]
                            }
                        ]
                    },
                    new Date(0)
                ]
            },
            "category": "$category"
        }, 
        "price": { "$avg": "$price" },
        "count": { "$sum": 1 }
    }},
    // Sort on date and count
    { "$sort": { "_id.dt": 1, "count": -1 }},

    // Group on just the date, keeping the avg and the first category
    { "$group": {
        "_id": "$_id.dt",
        "price": { "$avg": "$price"}
        "category": { "$first": "$_id.category" }
    }}

在日期和类别上都$group,并通过$sum保留类别计数。然后你$sort所以最大的“计数”在每个分组日期的顶部。当您应用刚刚应用于日期本身的另一个$group时,最后使用$first,以便返回每个日期具有最大计数的类别。

不要被像$max这样的运营商诱惑,因为它们在这里不起作用。关键的区别在于对每个类别值产生的“记录/文档”的“绑定”关联。因此,它不是您想要的最大“计数”或最大“类别”值,而是“产生”最大计数的类别值。因此,此处需要$sort

最后你应该“破坏”一些习惯:

  • 除非您确实知道自己在做什么,否则请勿使用非UTC格式的日期实例数据作为输入。日期将始终转换为UTC,因此至少在测试列表中,您应该习惯于指定日期值。

  • 从另一个角度来看,它可能看起来更清晰,但像1000 * 60 * 60之类的东西比3600000更能解释它所做的事情。相同的值,但是一种形式表明它的时间单位一目了然。

  • 当只有一个值时,复合_id也会混淆问题。因此,如果这是唯一存在的值,则访问_id.dt几乎没有意义。什么时候只有_id内的单个属性,那就没关系了。但是单个值应该仅仅分配给_id。没有什么可以获得,单身就很清楚了。