既然我已经度过了一个周末在$ 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个以上电话只是为了获得所有Person年龄组的信息。我希望输出看起来像这样:
[
{"_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我不需要ID 1517191919或其他任何东西,我想要一个可靠的名称,所以我知道所以我确实知道_id:“ $ age”不会给我想要的东西,但是我也不知道如何得到想要的东西。)
我唯一一次看到不止一件事的地方是:$ match,$ group和$ project。但是,如果$ project表示我不能使用$ sum或$ count,那么我可以执行多个$ groups,如果可以的话,它的窍门是什么?
最佳答案
至于在不同年龄组中生成结果的情况,聚合框架的$cond
运算符可以在这里提供帮助。作为三元运算符,它接受逻辑结果(如果condition),并且可以在true
(则)或false
(否则)的地方返回值。在不同年龄段的情况下,您将“嵌套”处于else
条件的呼叫以满足每个范围,直到逻辑上用尽为止。
总体而言,一次通过并不能将“性别”和“年龄”的结果都分组在一起,实际上并不可行。虽然“可以”完成,但唯一的方法基本上是将所有数据累积在数组中,然后再对其进行处理以进行后续分组。这不是一个好主意,因为在尝试保留数据时,它几乎总是会打破实际的BSON限制16MB。因此,通常需要更好的方法。
这样,在API支持的地方(您在nodejs下,就可以了),那么通常最好分别运行每个查询并合并结果。节点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
将启动并行运行的任务,因此这两个任务可以同时在服务器上运行。输入数组中的每个管道都将传递给Aggregation方法,该方法随后将返回结果并将输出数组合并为最终结果。最终结果不仅是将结果正确地键入年龄组,而且两个结果集似乎在相同的组合响应中,不需要其他任何工作来合并内容。
这不仅方便,而且并行执行使时间效率更高,并且对用于返回结果的聚合方法的负担也更少(如果不能克服的话)。