У меня есть коллекция документов временных рядов, которые следуют схеме:
{
"_id" : ObjectId("5ce523fb3e9e92609c54747b"),
"received" : ISODate("2018-06-01T00:00:00.000Z"),
"payload" : {
"tag1" : 0.0,
"tag2" : 0.0,
"tag3" : 0.0,
...
"xyz": 0.0
}
}
Полученные метки времени соответствуют стандарту ISO8601, а значения внутри полезной нагрузки удваиваются. У меня нет контроля над схемой документов. Я получаю 1 документ в минуту, количество полей внутри полезной нагрузки во времени может меняться, как и литералы ключей (имена тегов).
По сути, мне нужно делать сводки по времени (ежечасно, ежедневно, еженедельно и т. д.), показывающие среднее значение каждого заданного тега за временной интервал.
Посмотрев документацию и связанные сообщения (например, https://www.mongodb.com/blog/post/time-series-data-and-mongodb-part-3--querying-analyzing-and-presenting-timeseries-data), я считаю, что это возможно.
Я считаю, что мне может понадобиться сделать что-то вроде unwind
полезной нагрузки, а затем применить группировку агрегации по k, v, но это самое дальнее, что у меня есть:
db.my_data.aggregate([
{"$project": {
"year": {"$year": "$received"}, "month": {"$month": "$received"}, "dayOfMonth": {"$dayOfMonth": "$received"}, "hour": {"$hour": "$received"},
"p": {"$objectToArray": "$payload"}}
},
{"$unwind": "$p"},
{"$group": {
_id: {
year: "$year",
month: "$month",
dayOfMonth: "$dayOfMonth",
hour: "$hour",
tag: "$p.k"
},
"t_avg": {$avg: "$p.v"},
}
},
])
Однако в результате я получаю кучу «раскрученных» записей, столько же тегов внутри полезной нагрузки:
{
"_id" : {
"year" : 2018,
"month" : 6,
"dayOfMonth" : 1,
"hour" : 0,
"tag" : "tag1"
},
"t_avg" : 13.1261633627836
},
...
Это не то, что мне нужно. Крайне важно, чтобы прокручиваемые по времени записи были в том же формате, что и исходные, а именно: _id
, received
и payload
, поэтому полученные средние значения по тегу с течением времени должны быть объединены в аналогичный объект полезной нагрузки.
{ // assuming hour 5th
"_id" : ObjectId("..."),
"received" : ISODate("2018-06-01T00:05:00.000Z"),
"payload" : {
"tag1" : avg for the hour,
"tag2" : avg for the hour,
"tag3" : avg for the hour,
...
"xyz": avg for the hour
}
}
Я не знаю, как этого добиться.
Конечная цель — создать представление с этим, чтобы сводные средние значения можно было получать по запросу без необходимости запускать запрос как код из службы. Я не уверен, отличается ли синтаксис для создания представления от синтаксиса агрегированного запроса, я считаю, что можно создать представление на основе конвейера агрегации.
Вам нужно преобразовать поля _id
в дату (используя $dateFromParts) и использовать $массивобжект, чтобы получить payload
, построенный из ваших динамически сгенерированных значений, попробуйте:
db.my_data.aggregate([
{"$project": {
"year": {"$year": "$received"}, "month": {"$month": "$received"}, "dayOfMonth": {"$dayOfMonth": "$received"}, "hour": {"$hour": "$received"},
"p": {"$objectToArray": "$payload"}}
},
{"$unwind": "$p"},
{"$group": {
_id: {
year: "$year",
month: "$month",
dayOfMonth: "$dayOfMonth",
hour: "$hour",
tag: "$p.k"
},
"t_avg": {$avg: "$p.v"},
}
},
{
$group: {
_id: { $dateFromParts: { year: "$_id.year", month: "$_id.month", day: "$_id.dayOfMonth", hour: "$_id.hour" } },
payload: { $push: { k: "$_id.tag", v: "$t_avg" } }
}
},
{
$project: {
_id: 1,
payload: { $arrayToObject: "$payload" }
}
}
])
@diegoruizbarbero они должны быть отдельными, так как первый - вычислить средние значения, а второй - собрать их в один массив - это невозможно сделать за один шаг.
Потрясающий! В конце концов я тоже придумал что-то, что работает после некоторой двойной группировки по сообщениям. Я искал что-то вроде $dateFromParts. В конце концов я заставил его работать, усекая строку даты... что мне не очень нравится, но я думаю, что это помогает при агрегировании поля. Я предпочитаю $dateFromParts. Просто вопрос от незнания: я так понимаю 1-я группа собирает все записи тегов, а 2-я группа рекомбинирует пейлоад из ключей и агрегацию. Есть ли возможность сделать 2 группы всего за одну? Что-то с
$mergeObjects
может быть?