Трубопровод агрегации MongoDB: использование цикла For - PullRequest
0 голосов
/ 15 апреля 2020

Мне интересно, есть ли у кого-то лучший способ запроса в MongoDB.

У меня есть коллекция, в которой хранится почасовая информация для идентификатора пользователя. В этой коллекции будет каждый документ для уникальной комбинации user_id и даты. Но user_id может иметь несколько записей с разными датами. Примеры записей документа приведены ниже

{ user_id : A,
  date.   : '2020-04-01'
 hourly_info : 
   { metric_one :
        { 0 : 4, 
          1: 7,
          2:8
          ..
          23: 43 }}}

Моя проблема: для заданного идентификатора пользователя и диапазона дат сгруппировать по идентификатору пользователя и рассчитать сумму metric_one для каждого часа. Пример вывода будет выглядеть следующим образом:

{"_id": "A", "0": 45, "1": 28, "2": 31, .... "23": 67}.

Мой запрос:

db.collection.aggregate([
      { '$match': {
           'user_id':"A",   
           'date':{'$lte':ISODate('2020-04-01'),     
           '$gte':ISODate('2020-04-08')   } 
      } }, 
      {'$group':{
            _id: '$user_id', 
           '0': { '$sum': '$hourly_info.metric_one.0'}, 
           '1': { '$sum': '$hourly_info.metric_one.1'}, 
           ... ,
           '23':{ '$sum': '$hourly_info.metric_one.23'}
      }}
]).pretty()

Буду очень признателен, если кто-нибудь скажет, есть ли лучший способ запроса, кроме суммирования для каждого часа с 0 до 23.

Есть ли лучший способ, которым я могу использовать для l oop, чтобы мне не приходилось писать одинаковые логики суммирования c для 0 до 23?

что-то вроде ниже

for (i = 0; i <= 23; i=i+1){ 
db.collection.aggregate([{
    '$match': {
      'user_id':"A",
      'sales_date':{
        '$lte':ISODate('2020-04-01'),
        '$gte':ISODate('2020-04-08')
      }
    }
}, {
        '$group': {
            '_id': '$user_id',
            {i}: {
                '$sum': '$hourly_info.metric_one.{i}'
            }
        }
     }
])

}

Заранее спасибо за помощь :)

1 Ответ

1 голос
/ 15 апреля 2020

Это можно сделать с агрегацией. Вы можете использовать $ objectToArray и unwind, чтобы разделить почасовые данные на отдельные документы, затем сгруппировать дважды - один раз, чтобы считать каждый час, и один раз, чтобы собрать их вместе для пользователя. Затем $ arrayToObject, чтобы снова сделать его объектом.

db.collection.aggregate([
      {'$match': {
           'user_id':"A",   
           'date':{'$gte':ISODate('2020-04-01'),     
                   '$lte':ISODate('2020-04-08')} 
      }}, 
      {'$addFields':{
           'hourly_info.metric_one': {$objectToArray: '$hourly_info.metric_one'}
      }},
      {'$unwind':'$hourly_info.metric_one'},
      {'$group':{
            _id: {
                  'user':'$user_id',
                  'hour':'$hourly_info.metric_one.k'
            },
           'total': { '$sum': '$hourly_info.metric_one.v'}
      }},
      {'$group':{
           '_id':'$_id.user',
           'hourly':{'$push':{'k':'$_id.hour','v':'$total'}}
      }},
      {'$addFields': {'hourly':{'$arrayToObject':'$hourly'}}}
])

Детская площадка

...