mongodb 上的 multi sum/count(求和性别和所有结果的总和)
multi sum/count on mongodb (sum gender and total all results)
我有这个文件:
{gender:"male", ...},
{gender:"male", ...},
{gender:"female", ...},
{gender:"female", ...},
{gender:"female", ...},
所以,我需要检索像
{
total:5,
male:2,
female:3
}
我的实际查询(无工作):
db.collection.aggregate([
{
$match:{...}
},
{
$group:{
_id:"$_id",
gender:{
$push:"$gender"
},
total:{
$sum:1
}
}
},
{
$unwind:"$gender"
},
{
$group:{
_id:"$gender",
name:{
$addToSet:"$all"
},
"count":{
$sum:1
}
}
}
])
如何检索性别和总数的计数器?
谢谢
类似的东西就可以了:
db.collection.aggregate([
{$project: {
male: {$cond: [{$eq: ["$gender", "male"]}, 1, 0]},
female: {$cond: [{$eq: ["$gender", "female"]}, 1, 0]},
}},
{$group: { _id: null, male: {$sum: "$male"},
female: {$sum: "$female"},
total: {$sum: 1},
}},
])
根据您的示例生成:
{ "_id" : null, "male" : 2, "female" : 3, "total" : 5 }
关键思想是使用 conditional expression 将性别映射到 0 或 1。之后,您只需要对每个字段进行简单求和即可。
可以通过多种方式获得结果,但它确实有助于了解如何获得结果以及聚合管道正在做什么。
所以这里的一般情况是测试“性别”的值,然后决定是否为该性别累加总和。因此,可以使用 $eq
test in the $cond
operator. But the most efficient way is to process directly to $group
:
按逻辑分隔字段
var start = Date.now();
db.people.aggregate([
{ "$group": {
"_id": null,
"male": {
"$sum": {
"$cond": [
{ "$eq": ["male","$gender"] },
1,
0
]
}
},
"female": {
"$sum": {
"$cond": [
{ "$eq": ["female","$gender"] },
1,
0
]
}
},
"total": { "$sum": 1 }
}}
])
var end = Date.now();
end - start;
现在在我的小笔记本电脑上有一个相当均匀的随机“性别”样本,管道在 290ms
到 运行 左右一致,因为每个文档都会评估哪些字段在同时。
另一方面,如果您按照其他地方的建议在 $project
阶段写作:
var start = Date.now();
db.people.aggregate([
{ "$project": {
"male": {
"$cond": [
{ "$eq": ["male","$gender"] },
1,
0
]
},
"female": {
"$cond": [
{ "$eq": ["female","$gender"] },
1,
0
]
},
}},
{ "$group": {
"_id": null,
"male": { "$sum": "$male" },
"female": { "$sum": "$female" },
"total": { "$sum": 1 }
}}
])
var end = Date.now();
end - start;
然后平均结果在 460ms
到达 运行 管道,这接近“两倍”的时间。那么这是怎么回事?
基本上 $project
需要在集合中的每个文档“之前”将它们发送到 $group
阶段,因此这正是它所做的。在你要用它做任何其他事情之前,你有一个管道改变每个(测试中的 100000 个)文档的结构。
这就是能够“从逻辑上”看待这个问题的地方,并说 “等一下,我可以这样做的时候为什么要在那里这样做 这里,然后意识到所有逻辑都压缩到一个阶段。
这就是设计和优化的意义所在。所以如果你要学习,那么学习正确的方法是有帮助的。
样本生成:
var bulk = db.people.initializeOrderedBulkOp(),
count = 0,
gender = ["male","female"];
for ( var x=1; x<=100000; x++ ) {
bulk.insert({
"gender": gender[Math.floor(Math.random()*2)]
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.people.initializeOrderedBulkOp();
}
}
两条管道的结果:
{ "_id" : null, "male" : 50086, "female" : 49914, "total" : 100000 }
计时
主体中提供的“在客户端”时间当然包括客户端解释和执行的实际开销时间,以及在本地服务器上传输的时间。
我重新运行 分析了新 MongoDB 3.0.3 Ubuntu 15.04 VM(2GB 分配,分配了 4 个内核)上的日志酷睿 i7 笔记本电脑主机,8GB 和 Windows 7 64 位,我从来没有费心去覆盖。
服务器上的实际时间仅来自日志,平均每次执行 1000 次(预热):
single $group optimal: avg: 185ms min: 98ms max: 205ms
seprate $project: avg: 330ms min: 316ms max: 410ms
所以实际上“小”稍微接近“更差”几乎两倍的时间更接近。但这正是我对结果的期望。因此,这里总“成本”的近 50% 是将数据加载和处理到内存中的管道中。所以区别在于能够在加载和处理的同时减少结果。
我有这个文件:
{gender:"male", ...},
{gender:"male", ...},
{gender:"female", ...},
{gender:"female", ...},
{gender:"female", ...},
所以,我需要检索像
{
total:5,
male:2,
female:3
}
我的实际查询(无工作):
db.collection.aggregate([
{
$match:{...}
},
{
$group:{
_id:"$_id",
gender:{
$push:"$gender"
},
total:{
$sum:1
}
}
},
{
$unwind:"$gender"
},
{
$group:{
_id:"$gender",
name:{
$addToSet:"$all"
},
"count":{
$sum:1
}
}
}
])
如何检索性别和总数的计数器? 谢谢
类似的东西就可以了:
db.collection.aggregate([
{$project: {
male: {$cond: [{$eq: ["$gender", "male"]}, 1, 0]},
female: {$cond: [{$eq: ["$gender", "female"]}, 1, 0]},
}},
{$group: { _id: null, male: {$sum: "$male"},
female: {$sum: "$female"},
total: {$sum: 1},
}},
])
根据您的示例生成:
{ "_id" : null, "male" : 2, "female" : 3, "total" : 5 }
关键思想是使用 conditional expression 将性别映射到 0 或 1。之后,您只需要对每个字段进行简单求和即可。
可以通过多种方式获得结果,但它确实有助于了解如何获得结果以及聚合管道正在做什么。
所以这里的一般情况是测试“性别”的值,然后决定是否为该性别累加总和。因此,可以使用 $eq
test in the $cond
operator. But the most efficient way is to process directly to $group
:
var start = Date.now();
db.people.aggregate([
{ "$group": {
"_id": null,
"male": {
"$sum": {
"$cond": [
{ "$eq": ["male","$gender"] },
1,
0
]
}
},
"female": {
"$sum": {
"$cond": [
{ "$eq": ["female","$gender"] },
1,
0
]
}
},
"total": { "$sum": 1 }
}}
])
var end = Date.now();
end - start;
现在在我的小笔记本电脑上有一个相当均匀的随机“性别”样本,管道在 290ms
到 运行 左右一致,因为每个文档都会评估哪些字段在同时。
另一方面,如果您按照其他地方的建议在 $project
阶段写作:
var start = Date.now();
db.people.aggregate([
{ "$project": {
"male": {
"$cond": [
{ "$eq": ["male","$gender"] },
1,
0
]
},
"female": {
"$cond": [
{ "$eq": ["female","$gender"] },
1,
0
]
},
}},
{ "$group": {
"_id": null,
"male": { "$sum": "$male" },
"female": { "$sum": "$female" },
"total": { "$sum": 1 }
}}
])
var end = Date.now();
end - start;
然后平均结果在 460ms
到达 运行 管道,这接近“两倍”的时间。那么这是怎么回事?
基本上 $project
需要在集合中的每个文档“之前”将它们发送到 $group
阶段,因此这正是它所做的。在你要用它做任何其他事情之前,你有一个管道改变每个(测试中的 100000 个)文档的结构。
这就是能够“从逻辑上”看待这个问题的地方,并说 “等一下,我可以这样做的时候为什么要在那里这样做 这里,然后意识到所有逻辑都压缩到一个阶段。
这就是设计和优化的意义所在。所以如果你要学习,那么学习正确的方法是有帮助的。
样本生成:
var bulk = db.people.initializeOrderedBulkOp(),
count = 0,
gender = ["male","female"];
for ( var x=1; x<=100000; x++ ) {
bulk.insert({
"gender": gender[Math.floor(Math.random()*2)]
});
count++;
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.people.initializeOrderedBulkOp();
}
}
两条管道的结果:
{ "_id" : null, "male" : 50086, "female" : 49914, "total" : 100000 }
计时
主体中提供的“在客户端”时间当然包括客户端解释和执行的实际开销时间,以及在本地服务器上传输的时间。
我重新运行 分析了新 MongoDB 3.0.3 Ubuntu 15.04 VM(2GB 分配,分配了 4 个内核)上的日志酷睿 i7 笔记本电脑主机,8GB 和 Windows 7 64 位,我从来没有费心去覆盖。
服务器上的实际时间仅来自日志,平均每次执行 1000 次(预热):
single $group optimal: avg: 185ms min: 98ms max: 205ms
seprate $project: avg: 330ms min: 316ms max: 410ms
所以实际上“小”稍微接近“更差”几乎两倍的时间更接近。但这正是我对结果的期望。因此,这里总“成本”的近 50% 是将数据加载和处理到内存中的管道中。所以区别在于能够在加载和处理的同时减少结果。