首页 文章

需要在聚合中找到字段中最常出现的值

提问于
浏览
1

从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 回答

  • 2

    那么你不能只是"make up" . 作为 $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所以最大的"count"在每个分组日期的顶部 . 当你应用另一个刚刚应用于日期本身的 $group 时,最后使用$first,以便返回每个日期具有最大计数的类别 .

    不要被像 $max 这样的运营商诱惑,因为它们在这里不起作用 . 关键的区别在于"tied"与每个类别值产生的"record/document"的关联 . 所以它不是你想要的最大值"count"或最大"category"值,而是"produced"最大计数的类别值 . 因此,这里需要一个 $sort .

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

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

    • 它可能看起来有点干净,但是像 1000 * 60 * 60 这样的东西比 3600000 更能说明它的代码 . 相同的值,但一种形式表明它的时间单位一目了然 .

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

相关问题