Mongodb 使用mongo统计所有文档中的数组出现次数
我试图从一组文档中提取数据,这些文档看起来像:Mongodb 使用mongo统计所有文档中的数组出现次数,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,我试图从一组文档中提取数据,这些文档看起来像: [ { name: 'john', sex: 'male', hobbies: ['football', 'tennis', 'swimming'] }, { name: 'betty' sex: 'female', hobbies: ['football', 'tennis'] }, { name: 'frank' sex: 'male', hobbies
[
{
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 }
}
}
])
就我个人而言,我不太喜欢将“数据”转换为结果中的键名。聚合框架原则倾向于聚合,因为这种操作也不受支持 因此,个人偏好是将“数据”维护为“数据”,并接受处理后的输出实际上比一致的对象设计更好、更符合逻辑:
db.people.aggregate([
{“$组”:{
“_id”:“$sex”,
“嗜好”:{“$push”:“$cabiods”},
“总计”:{“$sum”:1}
}},
{“$unwind”:“$HAPPORIES”},
{“$unwind”:“$HAPPORIES”},
{“$组”:{
“_id”:{
“性别”:“$\u id”,
“嗜好”:“$嗜好”
},
“总计”:{“$first”:“$total”},
“hobbyCount”:{“$sum”:1}
}},
{“$组”:{
“\u id”:“$\u id.sex”,
“总计”:{“$first”:“$total”},
“爱好”:{
“$push”:{“name”:“$\u id.hobby”,“count”:“$hobbyCount”}
}
}}
])
这会产生如下结果:
[
{
“_id”:“女性”,
“总数”:1,
“爱好”:[
{
“姓名”:“网球”,
“计数”:1
},
{
“名称”:“足球”,
“计数”:1
}
]
},
{
“_id”:“男性”,
“总数”:2,
“爱好”:[
{
“名称”:“游泳”,
“计数”:1
},
{
“姓名”:“网球”,
“计数”:2
},
{
“名称”:“足球”,
“计数”:2
}
]
}
]
因此,最初的$group
计算每个“性别”的数量,并将爱好堆积成一个数组。然后,要将您的$diswind
反规范化两次以获得单个项目,$group
以获得每个性别下每个爱好的总数,最后为每个性别单独重新组合一个数组
这是相同的数据,它有一个一致的、有机的结构,易于处理,MongoDB和聚合框架非常乐意生成这个输出
如果您真的必须将数据转换为键的名称(我仍然建议您不要这样做,因为这在设计中不是一个好的模式),那么从最终状态执行这样的转换对于客户端代码处理来说是相当简单的。作为适用于shell的基本JavaScript示例:
var out=db.people.aggregate([
{“$组”:{
“_id”:“$sex”,
“嗜好”:{“$push”:“$cabiods”},
“总计”:{“$sum”:1}
}},
{“$unwind”:“$HAPPORIES”},
{“$unwind”:“$HAPPORIES”},
{“$组”:{
“_id”:{
“性别”:“$\u id”,
“嗜好”:“$嗜好”
},
“总计”:{“$first”:“$total”},
“hobbyCount”:{“$sum”:1}
}},
{“$组”:{
“\u id”:“$\u id.sex”,
“总计”:{“$first”:“$total”},
“爱好”:{
“$push”:{“name”:“$\u id.hobby”,“count”:“$hobbyCount”}
}
}}
]).toArray();
out.forEach(函数(doc){
var obj={};
排序(函数(a,b){返回a.count
然后,您基本上是将每个游标结果处理为所需的输出形式,这实际上不是服务器上真正需要的聚合函数:
{
“_id”:“女性”,
“总数”:1,
“爱好”:{
“网球”:1,
“足球”:1
}
}
{
“_id”:“男性”,
“总数”:2,
“爱好”:{
“网球”:2,
“足球”:2,
“游泳”:1
}
}
其中,根据需要将这种操作实现到游标结果的流处理中以进行转换也应该是相当繁琐的,因为它基本上是相同的逻辑
另一方面,您始终可以使用mapReduce在服务器上实现所有操作:
db.people.mapReduce(
函数(){
散发(
这个,性,
{
“总数”:1,
“嗜好”:这个。嗜好。地图(功能(键){
返回{“name”:键,“count”:1};
})
}
);
},
功能(键、值){
var obj={},
减少={
“总计”:0,
“爱好”:[]
};
values.forEach(函数(值){
减少的.total+=值.total;
价值。爱好。forEach(功能(爱好){
如果(!obj.hasOwnProperty(hobby.name))
obj[hobby.name]=0;
obj[hobby.name]+=hobby.count;
});
});
reduced.cabiods=Object.keys(obj).map(函数(键){
返回{“name”:key,“count”:obj[key]};
}).排序(功能(a、b){
返回a.count
mapReduce有自己独特的输出风格,但在积累和操作中使用相同的原则,即