MongoDB - агрегирование с вложенными объектами и изменяемыми ключами - PullRequest
0 голосов
/ 01 марта 2019

У меня есть документ, который описывает количество различных вещей, наблюдаемых камерой в течение 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 голосов
/ 03 марта 2019

Вы можете использовать стадию конвейера $facet для создания двух суб-конвейеров;один для segments и другой для counts.Эти под-конвейеры можно объединить, используя $zip, чтобы соединить их вместе, и $map, чтобы объединить каждый массив из 2 элементов, созданный из zip.Обратите внимание, что это будет работать корректно только в том случае, если суб-конвейеры выводят отсортированные массивы одинакового размера, поэтому мы группируем и сортируем по 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 .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...