Как суммировать много ключей в коллекции MongoDB

#mongodb #aggregation-framework

#mongodb #структура агрегации

Вопрос:

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

 {
    year: 1900,
    alabama: 145,
    arkansas: 103,
    // ... for all 50 US states
}
  

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

 {
    alabama: 1360492,
    arkansas: 598023,
    // ... and so on
}
  

Лучшее, что я смог придумать, — это агрегированный запрос с 50 терминами:

 db.sample.aggregate({$group: {
    _id: "",
    alabama: {$sum: "$alabama"},
    arkansas: {$sum: "$arkansas"},
    // ... and so on
}});
  

Есть ли какой-нибудь способ построения запроса, который позволяет мне просто предоставить массив имен полей, которые необходимо суммировать?

Ответ №1:

Я не знаю, как это сделать, используя структуру агрегации, но используя mapreduce, это довольно просто;

 > db.test.insert({ year: 1900, alabama: 145, arkansas: 103 })

> db.test.insert({ year: 1901, alabama: 100, arkansas: 77  })

// Map function, emits the state name and the value for each entry
> var m = function () { 
                        for(var key in this) {
                          if(this.hasOwnProperty(key) amp;amp; 
                             key != 'year' amp;amp; key!='_id')
                            emit(key, this[key])
                        }
                      }

// Reduce function, just sums up the score per state
> var r = function (state, score) { return Array.sum(score); }

// Run mapreduce with map function m, reduce function r, and output inline
> db.test.mapReduce(m, r, {out:{inline:1}})

{
"results" : [
    {
        "_id" : "alabama",
        "value" : 245
    },
    {
        "_id" : "arkansas",
        "value" : 180
    }
]
}
  

РЕДАКТИРОВАТЬ: чтобы иметь возможность использовать структуру агрегации, я не вижу способа без незначительного изменения модели данных, чтобы иметь возможность доступа к состоянию в качестве ключа;

 > db.test2.insert({ year: 1900, data:[{k:"alabama", v:145}, 
                                      {k:"arkansas", v:103}] } )

> db.test2.insert({ year: 1901, data:[{k:"alabama", v:100}, 
                                      {k:"arkansas", v:77}] } )

// Unwind data, and group it back together by key while summing the values;
> db.test2.aggregate({$unwind:"$data"},
                     {$group:{_id:"$data.k",total:{$sum: "$data.v"}}})

{ "_id" : "arkansas", "total" : 180 }
{ "_id" : "alabama", "total" : 245 }
  

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

1. Есть ли у вас какая-либо интуиция относительно того, как это будет сравниваться с точки зрения производительности со структурой агрегации?

2. @TomPanning Структура агрегации обычно работает быстрее, если вы можете написать эквивалентный запрос, однако в этом случае я не знаю способа сделать это с существующей моделью данных.

3. @TomPanning добавил способ использования агрегации при незначительном изменении модели данных.