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

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

Учитывая приведенные ниже примеры данных:
TeamID Day OrderAmount
100 4/1 50
100 4/1 40
200 4/2 50
100 4/2 20

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

TeamID Day AvgAmount
100 1/4 45
200 2/4 50
100 2/4 20

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

Day AvgAmount
4/1 47,5
4/2 35

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


person james    schedule 20.05.2011    source источник


Ответы (2)


Вы можете рассчитать эти агрегаты либо с помощью 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 }
});
person Chris Fulstow    schedule 21.05.2011
comment
Спасибо, Крис, но в вашем последнем примере не учитывалась команда, имеющая 2 заказа в один и тот же день. Это будет считаться дважды с тем, как у вас это есть, что я могу сделать. Есть ли способ использовать какой-то тип хеш-данных, чтобы отслеживать, какой TeamID уже сделал заказ в этот день, чтобы я не считал команду дважды? - person james; 21.05.2011
comment
@james Не могли бы вы опубликовать эквивалентный SQL для нужного вам результата? - person Chris Fulstow; 22.05.2011
comment
Привет, Крис, спасибо, что попробовал, см. мой ответ ниже на то, что я искал. - person james; 24.05.2011

После небольшого исследования я смог найти решение. Я не удовлетворен тем, что это наиболее оптимизированное решение из-за того, что я новичок в 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;
    }
person james    schedule 22.05.2011