分组结果mongoDB



我有一个数组countries值的集合,如下所示。我想总结一下各国的价值观。

{
  "_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,
   ..  
}

这可以简单地使用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) 
}

如果您打算像这样在"键"之间聚合统计信息,那么这不是一个很好的文档结构。无论如何,我并不喜欢"数据作为关键字名称",但主要的一点是,由于关键字名称在任何地方都不一样,它在许多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的方式表示的主要原因是JavaScipt本身对该值的大小有限制。很可能你的值只是微不足道的,但只是以这种方式"投射",但对于符合意图的足够大的数字,那么数学运算就会失败。

因此,考虑改变这些数据的结构以使事情变得更容易,通常是个好主意。最后要注意的是,对于单个文档中的所有键,您所期望的输出类型同样是反直觉的,因为它需要遍历"hash/map"的键,而不是使用数组或游标的自然迭代器。

最新更新