2015-02-09 7 views
4

У меня есть коллекция со значениями стран массива, подобными этому. Я хочу суммировать ценности стран.Результаты группы mongoDB

{ 
    "_id": ObjectId("54cd5e7804f3b06c3c247428"), 
    "country_json": { 
    "AE": NumberLong("13"), 
    "RU": NumberLong("16"), 
    "BA": NumberLong("10"), 
    ... 
    } 
}, 
{ 
    "_id": ObjectId("54cd5e7804f3b06c3c247429"), 
    "country_json": { 
     "RU": NumberLong("12"), 
     "ES": NumberLong("28"), 
     "DE": NumberLong("16"), 
     "AU": NumberLong("44"), 
     ... 
    } 
} 

Как суммировать значения стран, чтобы получить такой результат?

{ 
    "AE": 13, 
    "RU": 28, 
    .. 
} 
+0

'country_json' - это не массив, как вы упомянули. Вы хотите сохранить его в виде массива документов или как показано в примере? – BatScream

+0

как показано на примере – amic

ответ

2

Это просто может быть сделано с помощью aggregation

> db.test.aggregate([ 
    {$project: { 
     RU: "$country_json.RU", 
     AE: "$country_json.AE", 
     BA: "$country_json.BA" 
    }}, 
    {$group: { 
     _id: null, 
     RU: {$sum: "$RU"}, 
     AE: {$sum: "$AE"}, 
     BA: {$sum: "$BA"} 
    } 
]) 

Выход:

{ 
    "_id" : null, 
    "RU" : NumberLong(28), 
    "AE" : NumberLong(13), 
    "BA" : NumberLong(10) 
} 
1

Это не очень хорошая структура документа, если вы собираетесь агрегировать статистику поперек " ключи ". На самом деле это не фанат «данных как ключевых имен», но главное, что он не «хорошо играет» со многими формами запросов MongoDB из-за того, что имена клавиш различны во всем мире.

Особенно со структурой агрегации, лучшая форма для хранения данных находится в пределах фактического массива, например:

{ 
    "_id": ObjectId("54cd5e7804f3b06c3c247428"), 
    "countries": [ 
     { "key": "AE", "value": NumberLong("13"), 
     { "key": "RU", "value": NumberLong("16"), 
     { "key": "BA", "value": NumberLong("10") 
    ] 
} 

С, что вы можете просто использовать агрегатные операции:

db.collection.aggregate([ 
    { "$unwind": "$countries" }, 
    { "$group": { 
     "_id": "$countries.key", 
     "value": { "$sum": "$countries.value" } 
    }} 
]) 

Который дал бы вам следующие результаты:

{ "_id": "AE", "value": NumberLong(13) }, 
{ "_id": "RU", "value": NumberLong(28) } 

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

без изменения структуры документа вы вынуждены использовать методы оценки JavaScript, чтобы пройти ключи ваших документов, потому что это единственный способ сделать это с MongoDB:

db.collection.mapReduce(
    function() { 
     var country = this.country_json; 
     Object.keys(country).forEach(function(key) { 
      emit(key, country[key]); 
     }); 
    }, 
    function(key,values) { 
     return values.reduce(function(p,v) { return NumberLong(p+v) }); 
    }, 
    { "out": { "inline": 1 } } 
) 

И что бы производят точно такой же результат, как показано на примере примера агрегации, но работают с текущей структурой документа. Разумеется, использование оценки JavaScript не так эффективно, как собственные методы, используемые структурой агрегации, поэтому он не будет работать.

Также обратите внимание на возможные проблемы здесь с «большими значениями» в ваших литых полях NumberLong, так как основная причина, по которой они представлены таким образом, что JavaScipt сам имеет ограничения на размер этого значения, чем может быть представлен. Вероятно, ваши значения просто тривиальны, но просто «бросаются» таким образом, но для достаточно больших чисел в соответствии с намерением, математика просто терпит неудачу.

Так что это хорошая идея рассмотреть возможность изменения структуры данных для упрощения. В качестве заключительной заметки вид вывода, который вы ожидали со всеми ключами в одном документе, аналогичен интуитивному счетчику, так как снова он требует перемещения ключей «хеша/карты», а не использования естественных итераторов массивов или курсоров.

Смежные вопросы