#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;
}