现在我已经花了一个周末在 $project、aggregate() 和 $group 上敲敲打打,现在是时候让我自己接受新一轮的怜悯了。我正在尝试调用电话,获取用户的总数,按性别分组(这是更容易的部分)并按年龄范围分组(这让我很沮丧)。
我让它与一组一起工作:
Person.aggregate([
{
$match: {
user_id: id
}
},
{
$group: {
_id: '$gender',
total: { $sum: 1 }
}
}
])
.exec(function(err, result) {
etc...
由此,它会在一个漂亮的 json 输出中给出有多少男性、多少女性。但如果我添加第二组,它似乎会跳过第一组,并对第二组发出嘶嘶声:
Person.aggregate([
{
$match: {
user_id: id
}
},
{
$group: {
_id: '$gender',
total: { $sum: 1 }
},
$group: {
_id: '$age',
age: { $gte: 21 },
age: { $lte: 30 },
total: { $sum: 1 }
}
}
])
.exec(function(err, result) {
etc...
它不喜欢 $gte 或 $lte。如果我将其切换到 $project,那么它将执行 gte/lte,但抛出适合 $sum 或 $count 的信息。最重要的是,我在任何地方都找不到任何有关如何构造多请求返回的示例。这只是“这是一件事”,但我不想仅仅为了获取所有人员年龄组而调用 12 个以上的电话。我希望输出看起来像这样:
[
{"_id":"male","total":49},
{"_id":"woman","total":42},
{"_id":"age0_10", "total": 1},
{"_id":"age11_20", "total": 5},
{"_id":"age21_30", "total": 15}
]
(我不知道如何使年龄的 _id 成为实际年龄以外的东西,这没有意义,b/c 我不想要 1517191919 的 id 或其他什么,我想要一个可靠的名称所以我知道在模板中的哪里输出它。所以我知道 _id: "$age"不会给我我想要的东西,但我也不知道如何得到我想要的东西。)
我唯一一次看到不止一件事,那就是 $match、$group 和 $project。但是如果 $project 意味着我不能使用 $sum 或 $count,我可以做多个 $groups,如果可以的话,有什么技巧呢?
最佳答案
对于不同年龄段的结果,$cond
聚合框架的运算符可以在这里提供帮助。作为三元运算符,它接受逻辑结果( if 条件 ),并且可以返回一个值 where true
( then ) 或 else where false
( else )。对于不同年龄组的情况,您可以将调用“嵌套”在 else
条件中以满足每个范围,直到逻辑上耗尽为止。
整体情况在分组中同时包含“性别”和“年龄”的结果并不实际。虽然“可以”做到这一点,但唯一的方法基本上是累积数组中的所有数据,并为后续分组再次计算出来。这不是一个好主意,因为在尝试保留数据时,它几乎总是会打破 16MB 的实际 BSON 限制。因此通常需要更好的方法。
因此,如果 API 支持(您在 Nodejs 下,所以它支持),那么通常最好单独运行每个查询并组合结果。 Node async
库有这样的功能:
async.concat(
[
// Gender aggregator
[
{ "$group": {
"_id": "$gender",
"total": { "$sum": 1 }
}}
],
// Age aggregator
[
{ "$group": {
"_id": {
"$cond": {
"if": { "$lte": [ "$age", 10 ] },
"then": "age_0_10",
"else": {
"$cond": {
"if": { "$lte": [ "$age", 20 ] },
"then": "age_11_20",
"else": {
"$cond": {
"if": { "$lte": [ "$age", 30 ] },
"then": "age_21_30",
"else": "age_over_30"
}
}
}
}
}
},
"total": { "$sum": 1 }
}}
]
],
function(pipeline,callback) {
Person.aggregate(pipeline,callback);
},
function(err,results) {
if (err) throw err;
console.log(results);
}
);
默认执行async.concat
这里将启动任务并行运行,因此两者可以同时在服务器上运行。输入数组中的每个管道都将传递给聚合方法,该方法将返回结果并将输出数组合并到最终结果中。
最终结果不仅是您得到了与年龄组完美匹配的结果,而且两个结果集似乎处于相同的组合响应中,无需进行其他工作即可合并内容。
这不仅方便,而且并行执行使得时间效率更高,并且在用于返回结果的聚合方法上减少了负担(如果不是不可能的话)。
关于node.js - 将不同分组总计合并到总输出中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34279871/