2015-12-04 41 views
4

我试着去拉的文档的集合,它看起来像数据:计数阵列出现在所有文件与蒙戈

[ 
    { 
    name: 'john', 
    sex: 'male', 
    hobbies: ['football', 'tennis', 'swimming'] 
    }, 
    { 
    name: 'betty' 
    sex: 'female', 
    hobbies: ['football', 'tennis'] 
    }, 
    { 
    name: 'frank' 
    sex: 'male', 
    hobbies: ['football', 'tennis'] 
    } 
] 

我试图使用聚合框架来呈现数据,按性别划分,计数最常见的爱好。结果应该看起来像。

{ _id: 'male', 
    total: 2, 
    hobbies: { 
    football: 2, 
    tennis: 2, 
    swimming: 1 
    } 
}, 
{ _id: 'female', 
    total: 1, 
    hobbies: { 
     football: 1, 
     tennis: 1 
    } 
} 

到目前为止,我可以得到总每个性别的,但我不知道如何我能用放松身心得到爱好阵列的总和。

我迄今为止代码:

collection.aggregate([ 
     { 
      $group: { 
       _id: '$sex', 
       total: { $sum: 1 } 
      } 
     } 
    ]) 

回答

7

个人我不转化“数据”作为结果的键名的大风扇。由于这种操作也不被支持,所以汇总框架原理趋于集中。

所以个人的偏好是保持“数据”为“数据”,并接受处理后的输出实际上是更好的,更合乎逻辑的一致目标设计:

db.people.aggregate([ 
    { "$group": { 
     "_id": "$sex", 
     "hobbies": { "$push": "$hobbies" }, 
     "total": { "$sum": 1 } 
    }}, 
    { "$unwind": "$hobbies" }, 
    { "$unwind": "$hobbies" }, 
    { "$group": { 
     "_id": { 
      "sex": "$_id", 
      "hobby": "$hobbies" 
     }, 
     "total": { "$first": "$total" }, 
     "hobbyCount": { "$sum": 1 } 
    }}, 
    { "$group": { 
     "_id": "$_id.sex", 
     "total": { "$first": "$total" }, 
     "hobbies": { 
      "$push": { "name": "$_id.hobby", "count": "$hobbyCount" } 
     } 
    }} 
]) 

其产生的结果是这样的:

[ 
    { 
      "_id" : "female", 
      "total" : 1, 
      "hobbies" : [ 
       { 
        "name" : "tennis", 
        "count" : 1 
       }, 
       { 
        "name" : "football", 
        "count" : 1 
       } 
      ] 
    }, 
    { 
     "_id" : "male", 
     "total" : 2, 
     "hobbies" : [ 
      { 
       "name" : "swimming", 
       "count" : 1 
      }, 
      { 
       "name" : "tennis", 
       "count" : 2 
      }, 
      { 
       "name" : "football", 
       "count" : 2 
      } 
     ] 
    } 
] 

因此,最初的$group确实每“性别”的计数和爱好堆叠成一个数组的数组。然后,将您的$unwind取消归一化以得到单数项目$group以获得每种性别下每项业余爱好的总数,并最终重新组合每个性别的阵列。

这是相同的数据,它具有一致和有机的结构,易于处理,而且MongoDB和聚合框架在生成此输出时非常高兴。

如果您确实必须将您的数据转换为密钥名称(并且我仍然建议您不要这样做,因为它不是一个好的模式),那么从最终状态进行这样的转换对客户来说是相当微不足道的代码处理。作为合适的用于壳的碱性JavaScript示例:

var out = db.people.aggregate([ 
    { "$group": { 
     "_id": "$sex", 
     "hobbies": { "$push": "$hobbies" }, 
     "total": { "$sum": 1 } 
    }}, 
    { "$unwind": "$hobbies" }, 
    { "$unwind": "$hobbies" }, 
    { "$group": { 
     "_id": { 
      "sex": "$_id", 
      "hobby": "$hobbies" 
     }, 
     "total": { "$first": "$total" }, 
     "hobbyCount": { "$sum": 1 } 
    }}, 
    { "$group": { 
     "_id": "$_id.sex", 
     "total": { "$first": "$total" }, 
     "hobbies": { 
      "$push": { "name": "$_id.hobby", "count": "$hobbyCount" } 
     } 
    }} 
]).toArray(); 

out.forEach(function(doc) { 
    var obj = {}; 
    doc.hobbies.sort(function(a,b) { return a.count < b.count }); 
    doc.hobbies.forEach(function(hobby) { 
     obj[hobby.name] = hobby.count; 
    }); 
    doc.hobbies = obj; 
    printjson(doc); 
}); 

然后你基本上处理每个游标结果为所需的输出形式,这实际上是不是真正需要在服务器上反正聚合函数:

{ 
    "_id" : "female", 
    "total" : 1, 
    "hobbies" : { 
     "tennis" : 1, 
     "football" : 1 
    } 
} 
{ 
    "_id" : "male", 
    "total" : 2, 
    "hobbies" : { 
     "tennis" : 2, 
     "football" : 2, 
     "swimming" : 1 
    } 
} 

在哪里也应该是相当繁琐实现那种操纵入游标结果变换分析根据需要,流处理,因为它基本上是相同的逻辑。

在另一方面,你总是可以实现所有服务器上使用MapReduce的替代操纵:

db.people.mapReduce(
    function() { 
     emit(
      this.sex, 
      { 
       "total": 1, 
       "hobbies": this.hobbies.map(function(key) { 
        return { "name": key, "count": 1 }; 
       }) 
      } 
     ); 
    }, 
    function(key,values) { 
     var obj = {}, 
      reduced = { 
       "total": 0, 
       "hobbies": [] 
      }; 

     values.forEach(function(value) { 
      reduced.total += value.total; 
      value.hobbies.forEach(function(hobby) { 
       if (!obj.hasOwnProperty(hobby.name)) 
        obj[hobby.name] = 0; 
       obj[hobby.name] += hobby.count; 
      }); 
     }); 

     reduced.hobbies = Object.keys(obj).map(function(key) { 
      return { "name": key, "count": obj[key] }; 
     }).sort(function(a,b) { 
      return a.count < b.count; 
     }); 

     return reduced; 
    }, 
    { 
     "out": { "inline": 1 }, 
     "finalize": function(key,value) { 
      var obj = {}; 
      value.hobbies.forEach(function(hobby) { 
       obj[hobby.name] = hobby.count; 
      }); 
      value.hobbies = obj; 
      return value; 
     } 
    } 
) 

哪里的MapReduce有它输出的独特的风格,但同样的原则在积累和操作使用如果不是有可能作为有效的聚合框架可以这样做:

"results" : [ 
     { 
      "_id" : "female", 
      "value" : { 
       "total" : 1, 
       "hobbies" : { 
        "football" : 1, 
        "tennis" : 1 
       } 
      } 
     }, 
     { 
      "_id" : "male", 
      "value" : { 
       "total" : 2, 
       "hobbies" : { 
        "football" : 2, 
        "tennis" : 2, 
        "swimming" : 1 
       } 
      } 
     } 
    ] 

在一天结束的时候,我还是说,处理的第一种形式是最有效的,并提供了我的脑海里最自然的和一致的th的工作e数据输出,甚至没有尝试将数据点转换为密钥的名称。最好考虑遵循这种模式,但如果你真的必须这样做,那么就有办法通过各种处理方法来将结果操纵成所需的形式。

+0

只是想为你的答案放弃一个'谢谢'...虽然我没有问这个问题:)我帮了我很多! – chrisdennig