Вложенная группа по функциям с MongoDB

#mongodb #mongoid #mongodb-ruby

#mongodb #mongoid #mongodb-ruby

Вопрос:

Я довольно новичок в MongoDB, давно работаю с MySQL и сталкиваюсь с небольшим препятствием.

Учитывая приведенные ниже примерные данные:
Количество команд в день заказа
100 4/1 50
100 4/1 40
200 4/2 50
100 4/2 20

Я пытаюсь найти среднее количество заказов команды в день. Я могу выполнить простой запрос, используя функцию MapReduce, чтобы создать группу с идентификатором команды и днем. Итак, теперь у меня есть:

Среднее количество команд за день
100 4/1 45
200 4/2 50
100 4/2 20

Теперь я пытаюсь свернуть эти данные, чтобы получить среднюю сумму заказа на команду в день, которая была бы:

Среднее количество дней
4/1 47,5
4/2 35

Я могу легко сделать это с MySQL, но у меня возникли проблемы с выяснением, как это сделать с MongoDB, не делая это вручную на стороне приложения, а не с помощью MongoDB.

Ответ №1:

Вы можете вычислить эти агрегаты либо с помощью map-reduce, либо с помощью функции group(). Я использую group (), потому что это немного проще и быстрее, однако вам следует использовать map-reduce, если вам нужно распределить запрос по разделенному кластеру.

Сначала загрузите данные:

 db.orders.insert( { teamid: 100, date: "4/1", amount: 50 })
db.orders.insert( { teamid: 100, date: "4/1", amount: 40 })
db.orders.insert( { teamid: 200, date: "4/2", amount: 50 })
db.orders.insert( { teamid: 100, date: "4/2", amount: 20 })
  

Для каждой команды в день:

 db.orders.group({
    key: { teamid: true, date: true },
    reduce: function(doc, out) { out.sum  = doc.amount; out.count  ; },
    initial: { sum: 0, count: 0 },
    finalize: function(out) { out.average = out.sum / out.count }
});
  

Чтобы свернуть ежедневные сводки, просто измените ключ:

 db.orders.group({
    key: { date: true },
    reduce: function(doc, out) { out.sum  = doc.amount; out.count  ; },
    initial: { sum: 0, count: 0 },
    finalize: function(out) { out.average = out.sum / out.count }
});
  

Комментарии:

1. Спасибо, Крис, но в вашем последнем примере не учитывается команда, имеющая 2 заказа в один и тот же день. Это будет считаться дважды с тем, как у вас это есть, что я могу сделать. Есть ли способ использовать какой-то тип хэш-данных, чтобы отслеживать, какой TeamID уже сделал заказ в тот день, чтобы я не пересчитывал команду дважды?

2. @james Не могли бы вы опубликовать эквивалентный SQL для нужного вам результата?

3. Привет, Крис, спасибо, что попробовали это, смотрите Мой ответ ниже на то, что я искал.

Ответ №2:

После небольшого исследования я смог найти решение. Я не удовлетворен тем, что это наиболее оптимизированное решение из-за того, что я новичок в MongoDB и думаю о сопоставлении / сокращении, поэтому, если у кого-то еще есть что-то лучше, пожалуйста, поправьте меня. В частности, я не смог получить длину объекта arr_team, поэтому мне пришлось увеличить счетчик.

уменьшить функцию:

 
    function(doc, prev) { 
      var retVal  = {team_count: 0, day_total: 0};

      if(!prev.arr_team[doc.team_id]) {
        prev.arr_team[doc.team_id] = 0;
        prev.team_count  ;
      }

      prev.arr_team[doc.team_id]  ;

      prev.order_count  ; 
      if(doc.total_amount)
         prev.total_amount  = doc.total_amount 

      return retVal;
    }
  

Завершить:

 
function(out) {
      out.avg_team_order_amount = out.total_amount/out.team_count;
    }