在总输出中组合不同的分组总计

现在我已经在$ project,aggregate()和$ group这个周末敲了一下头,现在是时候让我再次投入到你的仁慈之中。 我正在尝试拨打电话,回复用户的总数,按性别分组(这是比较容易的部分),并根据年龄段进行分组(这是击败了我)。

我得到它与一个小组合作:

Person.aggregate([ { $match: { user_id: id } }, { $group: { _id: '$gender', total: { $sum: 1 } } } ]) .exec(function(err, result) { etc... 

从那以后,它会给我多less男人,有多less女人在一个不错的输出中。 但是,如果我join第二组,似乎跳过第一个,扔第二组:

  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。 最重要的是,在如何构build一个多请求返回的地方我找不到任何例子。 这只是“这是一件事”,但我不想为了得到所有人的年龄组而打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是除了实际年龄以外的东西,这是没有道理的,我不想要一个1517191919或任何其他ID,我想要一个可靠的名字,所以我知道所以我知道_id:“$ age”不会给我想要的东西,但我也不知道如何得到我想要的东西。)

唯一一次我看到不止一件事情,那就是$匹配,$组合和$项目。 但是,如果$ project意味着我不能使用$ sum或$ count,我可以做多个$组,如果可以的话,有什么诀窍呢?

至于在不同年龄组中生成结果的情况,聚合框架的$cond操作符可以在这里帮助。 作为一个三元运算符,它需要一个逻辑结果(如果条件),并且可以返回true (then)的值,否则返回false (else)。 在不同年龄组的情况下,您可以将else电话“嵌套”到else条件,以满足每个范围,直到逻辑耗尽。

总的来说,在一次通过中,对于“性别”和“年龄”的分组结果,并不是真正的实际可行的。 当“可以”完成时,唯一的方法是基本上将所有数据累加在数组中,然后再次处理次级分组。 这不是一个好主意,因为在尝试保留数据时,几乎总是会打破实用的16MB的BSON限制。 所以通常需要更好的方法。

因此,在API支持的地方(你在nodejs下,所以它是这样的),那么通常最好分别运行每个查询并合并结果。 节点async库只有这样的function:

 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的默认执行将启动并行运行的任务,因此两者可以同时在服务器上运行。 input数组中的每个pipe道将被传递给聚合方法,聚合方法将返回结果并将最终结果中的输出数组组合在一起。

最终的结果不仅仅是对年龄段的结果很好,而且两个结果集看起来是相同的组合响应,不需要其他工作来合并内容。

这不仅方便,而且并行执行使得这种方法更加省时,而且对用于返回结果的聚合方法征税(如果不是不可能的话)要less得多。