Объединить после $unwind для свертки временных рядов в Mongodb

У меня есть коллекция документов временных рядов, которые следуют схеме:

{
    "_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
    }
}

Я не знаю, как этого добиться.

Конечная цель — создать представление с этим, чтобы сводные средние значения можно было получать по запросу без необходимости запускать запрос как код из службы. Я не уверен, отличается ли синтаксис для создания представления от синтаксиса агрегированного запроса, я считаю, что можно создать представление на основе конвейера агрегации.

3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
1
0
326
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам нужно преобразовать поля _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" }
        }
    }
])

Потрясающий! В конце концов я тоже придумал что-то, что работает после некоторой двойной группировки по сообщениям. Я искал что-то вроде $dateFromParts. В конце концов я заставил его работать, усекая строку даты... что мне не очень нравится, но я думаю, что это помогает при агрегировании поля. Я предпочитаю $dateFromParts. Просто вопрос от незнания: я так понимаю 1-я группа собирает все записи тегов, а 2-я группа рекомбинирует пейлоад из ключей и агрегацию. Есть ли возможность сделать 2 группы всего за одну? Что-то с $mergeObjects может быть?

diegoruizbarbero 22.05.2019 20:33

@diegoruizbarbero они должны быть отдельными, так как первый - вычислить средние значения, а второй - собрать их в один массив - это невозможно сделать за один шаг.

mickl 22.05.2019 21:07

Другие вопросы по теме