Mongo索引字段用于优化大数据的聚合

时间:2017-09-12 15:12:21

标签: mongodb

{
    "_id" : ObjectId("59b7d232cb4ddc345c3bcef4"),
    "user_device_id" : ObjectId("59b7d21017c8a62f3a40c0bf"),
    "generated_at" : ISODate("2017-09-12T12:24:48.182Z"),
    "game_id" : ObjectId("59b683d4dd30770001513c75"),
    "device_type" : "iPhone 8 android sdk",
    "session_duration": 1000, /* in milliseconds */
    "device_os" : "iOS android sdk",
    "event_type" : "level_increase" /* new_user, game_session, ....*/
} // events collection

{
    "_id" : ObjectId("59b7d21017c8a62f3a40c0bf"),
    "generated_at" : ISODate("2017-09-12T12:24:48.182Z"),
    "game_id" : ObjectId("59b683d4dd30770001513c75"),
    "device_type" : "iPhone 8 android sdk",
    "device_os" : "iOS android sdk"
} // user_devices collection

我有一个' 活动'具有类似上述结构的集合。我正在 mongo 作为大数据分析平台,以便在几毫秒内获得聚合查询的最佳速度,或者如果没有,那么在几秒钟内我应该将任何想法编入索引。文件总量可能约为10-100亿。

聚合查询基本上在两个日期之间,即每个游戏的用户保留,每个游戏的平均用户会话,每个游戏的总用户数,基于 device_type device_os 的查询游戏。

ATM我将日期字段编入索引。

以下是第1天用户保留查询的示例:

UserDevice.aggregate(
        [
            {$match: {generated_at: {$gte: first_date, $lt: end_date}, game_id: "some game_id"}},
            {
                $lookup: {
                    from: "events",
                    localField: "_id",
                    foreignField: "user_device_id",
                    as: "event_docs"
                }
            },
            {
                $group: {
                    _id: { day: { $dayOfMonth: {$add: ["$generated_at", 1000*3600*24*1]}}, month: {$month: {$add: ["$generated_at", 1000*3600*24*1]}},  year: { $year: {$add: ["$generated_at", 1000*3600*24*1]} } }, total_users: {$sum: 1},
                    returned_users: {
                        $sum: {
                            $cond: { if: { $eq: [
                                    {
                                        $filter: {
                                            input: "$event_docs",
                                            as: "ed",
                                            cond: {
                                                $and: [
                                                    {$eq: [{ $dayOfMonth: {$add: ["$generated_at", 1000*3600*24*1]}}, { $dayOfMonth: "$$ed.generated_at" }]},
                                                    {$eq: [{ $month: {$add: ["$generated_at", 1000*3600*24*1]}}, { $month: "$$ed.generated_at" }]},
                                                    {$eq: [{ $year: {$add: ["$generated_at", 1000*3600*24*1]}}, { $year: "$$ed.generated_at" }]},
                                                    {$ne: ["$$ed.event_type", "new_user"]}
                                                ]

                                            }
                                        }
                                    }, []
                                ]}, then: 0, else: 1
                            }
                        }
                    }
                }
            }, {
                $sort: {"_id.year": 1, "_id.month": 1, "_id.day": 1}
            }
            ]).exec(function(err, results) {
                if (err) throw err;
                var latency = Date.now() - startTime;

                console.log("RETENTION RESULTS", JSON.stringify(results), "| latency:", latency,"ms");
            });

Mongo版本: 3.4.7

1 个答案:

答案 0 :(得分:1)

首先,只使用MongoDB 3.2对聚合中的索引进行全部聚合。

  

在版本3.2中更改:从MongoDB 3.2开始,索引可以覆盖   聚合管道。在MongoDB 2.6和3.0中,索引无法覆盖   一个聚合管道,因为即使管道使用索引,   聚合仍然需要访问实际文档。   https://docs.mongodb.com/manual/core/aggregation-pipeline/#pipeline-operators-and-indexes

确保您拥有此版本或更新版本。 通常,您可以为查询的$match部分中的所有字段创建索引。

我还建议通过mongodb docs的'Aggregation Pipeline Optimization'部分。它可能会有所帮助 对于前者您可以使用explain选项查看如何执行查询,并了解是否可以优化查询。