Mongodb提高聚合分组性能

时间:2020-09-22 15:10:41

标签: mongodb grouping aggregation query-performance

我在聚合管道上遇到性能问题和内存限制。 我的目标是从“平铺”集合中提取文档,每个图块存储一天中每一分钟的时间序列值,并将其转换为每分钟时间戳键的矩阵单元列表。

这是切片文档的结构:

{
    loc: [lng, lat],
    hash: [string],
    start: [stamp],
    hours: {
        0: {
            mins: {
                0: [value],
                1: [value],
                ..., 
                59: [value]
            }
        },
        1: {
            mins: {
                0: [value],
                1: [value],
                ..., 
                59: [value]
            }
        },
        ...,
        23: {
            mins: {
                0: [value],
                1: [value],
                ..., 
                59: [value]
            }
        }
    }
}

这是我希望获得的矩阵列表:

 {
    1588550400000:  {
        {
            loc: [lng, lat],
            val: [value]
        },
        {
            loc: [lng, lat],
            val: [value]
        },
        ...
    },
    1588550460000:  {
        {
            loc: [lng, lat],
            val: [value]
        },
        {
            loc: [lng, lat],
            val: [value]
        },
        ...
    },
    ...
}

我首先必须提取匹配地理区域和时间范围的文档。
然后我想我应该进行投影,尝试将每个时间序列分解为单独的条目。
空序列有时会出现在这里,这是一个小的条件挑战,它们会作为 null 作为 hours 条目存储在tile文档中。

所以我终于想出了以下管道:

db.getCollection("tiles")
    .aggregate([
        {
            $match: {
                loc: {
                    $geoWithin: {
                        $geometry: {
                            type: "Polygon",
                            coordinates: [[
                                [1.1471651891249255, 43.41692803575214],
                                [1.1471651891249255, 43.776544207540915],
                                [1.6436790011343436, 43.776544207540915],
                                [1.6436790011343436, 43.41692803575214],
                                [1.1471651891249255, 43.41692803575214]
                            ]]
                        }
                    }
                },
                start: {
                    $gte: 1588550400000,                // 2020-04-05
                    $lt: 1588636800000
                }
            }
        },
        {
            $project: {
                loc: "$loc",
                times: {
                    $cond: {
                        if: {$eq: ["$hours", null]},
                        then: {             // Handle a null time series for this cell
                            $reduce: {
                                input: {
                                    $map: {
                                        input: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23],
                                        as: "h",
                                        in: {
                                            $map: {
                                                input: [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59],
                                                as: "m",
                                                in: {
                                                    k: {
                                                        $sum: [
                                                            1588550400000,
                                                            { $multiply: [3600000, { $toInt: "$$h" }]},
                                                            { $multiply: [60000, { $toInt: "$$m" }] }
                                                        ]
                                                    },
                                                    l: "$loc",
                                                    v: 0,
                                                }
                                            }
                                        }
                                    }
                                },
                                initialValue: [],
                                in: {
                                    $concatArrays: [ "$$value", "$$this" ]
                                }
                            }

                        },
                        else: {
                            $reduce: {
                                input: {
                                    $map: {
                                        input: {
                                            "$objectToArray": "$hours",
                                        },
                                        as: "h",
                                        in: {
                                            $map: {
                                                input: {
                                                    "$objectToArray": "$$h.mins",
                                                },
                                                as: "m",
                                                in: {
                                                    k: {
                                                        $sum: [
                                                            1588550400000,
                                                            { $multiply: [3600000, { $toInt: "$$h.k" }]},
                                                            { $multiply: [60000, { $toInt: "$$m.k" }] }
                                                        ]
                                                    },
                                                    l: "$loc",
                                                    v: "$$m.v",
                                                }
                                            }
                                        }
                                    }
                                },
                                initialValue: [],
                                in: {
                                    $concatArrays: ["$$value", "$$this"]
                                }
                            }
                        }
                    }
                }
            }
        },
        { $unwind: "$times" },
        { $group: {
            _id: "$times.k",
            grid: {
                $push: {
                    loc: "$times.l",
                    val: "$times.v"
                }
            }
        } },
        { $sort: { _id: 1 } }
    ],
    {allowDiskUse: true});

但是我似乎在展开 group 阶段达到了极限。
有没有更好的方法来进行这种转换?
我应该在 project / map 阶段尝试 group 吗?
我的最后一个问题是如何简化最后一个管道输出,该输出看起来比我预期的目标结构要复杂一些:

{
    1588550400000:  {
        _id: 1588550400000,
        grid: [
            {
                loc: [lng, lat],
                val: [value]
            },
            {
                loc: [lng, lat],
                val: [value]
            },
            ...
        ]
    },
    1588550460000:  {
        _id: 1588550460000,
        grid: [
            {
                loc: [lng, lat],
                val: [value]
            },
            {
                loc: [lng, lat],
                val: [value]
            },
            ...
        ]
    },
    ...
}

我应该使用 replaceRoot 吗?

感谢您的帮助!

0 个答案:

没有答案