MongoDB-聚合嵌套对象和可变键

时间:2019-03-01 01:57:09

标签: mongodb aggregation-framework

我有一个文档,描述了在15分钟内相机观察到的不同事物的计数。看起来像这样:

{
    "_id" : ObjectId("5b1a709a83552d002516ac19"),
    "start" : ISODate("2018-06-08T11:45:00.000Z"),
    "end" : ISODate("2018-06-08T12:00:00.000Z"),
    "recording" : ObjectId("5b1a654683552d002516ac16"),
    "data" : {
        "counts" : {
            "5b434d05da1f0e00252566be" : 12,
            "5b434d05da1f0e00252566cc" : 4,
            "5b434d05da1f0e00252566ca" : 1
        }
    }
}

data.counts对象内的键随每个文档更改,并引用以后获取的其他数据。 data.counts中的密钥数量不受限制(但通常约为20)

我正在尝试将所有这15分钟的文档汇总到每日汇总的文档中。

我现在有这个查询可以做到:

db.getCollection("segments").aggregate([
    {$match:{
       "recording": ObjectId("5bf7f68ad8293a00261dd83f")
    }}, 
    {$project:{
        "start": 1,
        "recording": 1,
        "data": 1
    }},
    {$group:{
        _id: { $dateToString: { format: "%Y-%m-%d", date: "$start" } },
        "segments": { $push: "$$ROOT" }
    }},
    {$sort: {_id: -1}},
]);

这将进行分组并返回数组中的所有segments

我还希望在data.counts内汇总信息,以便获得每日组中所有相同密钥的值之和。

这将使我免于在每个15分钟的段中使用相同的键对值求和的另一个服务循环。例如。查询将返回如下内容:

{
    "_id" : "2019-02-27",
    "counts" : {
        "5b434d05da1f0e00252566be" : 351,
        "5b434d05da1f0e00252566cc" : 194,
        "5b434d05da1f0e00252566ca" : 111
        ... any other keys that were found within a day
    }
}

如何修改已有的查询,或使用其他查询?

谢谢!

1 个答案:

答案 0 :(得分:1)

您可以使用$facet流水线阶段创建两个子流水线;一个用于segments,另一个用于counts。通过使用$zip将它们缝合在一起,并使用$map合并从zip生成的每个2元素数组,可以连接这些子管道。请注意,只有在子管道输出相同大小的排序数组时,此方法才能正常工作,这就是为什么我们在每个子管道中按start_date进行分组和排序。

以下是查询:

db.getCollection("segments").aggregate([{
    $match: {
        recording: ObjectId("5b1a654683552d002516ac16")
    }
}, {
    $project: {
        start: 1,
        recording: 1,
        data: 1,
        start_date: { $dateToString: { format: "%Y-%m-%d", date: "$start" }}
    }
}, {
    $facet: {
        segments_pipeline: [{
            $group: {
                _id: "$start_date",
                segments: {
                    $push: {
                        start: "$start",
                        recording: "$recording",
                        data: "$data"
                    }
                }
            }
        }, {
            $sort: {
                _id: -1
            }
        }],
        counts_pipeline: [{
            $project: {
                start_date: "$start_date",
                count: { $objectToArray: "$data.counts" }
            }
        }, {
            $unwind: "$count"
        }, {
            $group: {
                _id: {
                    start_date: "$start_date",
                    count_id: "$count.k"
                },
                count_sum: { $sum: "$count.v" }
            }
        }, {
            $group: {
                _id: "$_id.start_date",
                counts: {
                    $push: {
                        $arrayToObject: [[{
                            k: "$_id.count_id",
                            v: "$count_sum"
                        }]]
                    }
                }
            }
        }, {
            $project: {
                counts: { $mergeObjects: "$counts" }
            }
        }, {
            $sort: {
                _id: -1
            }
        }]
    }
}, {
    $project: {
        result: {
            $map: {
                input: { $zip: { inputs: ["$segments_pipeline", "$counts_pipeline"] }},
                in: { $mergeObjects: "$$this" }
            }
        }
    }
}, {
    $unwind: "$result"
}, {
    $replaceRoot: {
        newRoot: "$result"
    }
}])

在这里尝试:Mongoplayground