1

现在我已经在$ 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我不我想要一个可靠的名字,所以我知道在哪里输出它在我的模板中,所以我知道_id:“$ age”不会给我我想要的东西,但是我不' t知道如何得到我想要的东西,或者)。

我唯一见过不止一件事,那是一个$匹配,一个$组和一个$项目。但是如果$ project意味着我不能使用$ sum或$ count,我可以做多个$组,如果可以的话,它有什么窍门?

+1

通过某种方式(第二部分),您似乎在寻找'$ cond',您可以使用它来对逻辑结果进行操作以生成年龄键。但在另一部分,你想要一个“多面的”结果,“性别”和“年龄”的结果在一个响应中。这在一个查询中实现并不是很实际(不完全不可能,但通常是这样),并且通过组合至少两个查询的结果来更好地实现。一个用于性别,一个用于年龄分组。 –

+0

当你说'查询'时,你的意思是两个电话吗?或者我做一个Person.aggregate,获取数据,保存到结果集,然后做第二个Person.aggregate? (我实际上并没有想到如何将数据保存到一个我然后执行的集合 - 而不仅仅是.find()。select()。exec() - 但理论上它似乎是可能的。 )或者你的意思是别的吗? – kl02

回答

1

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

整体案例在单次传递中并不真实,因为在分组中,“性别”和“年龄”的结果都是一样的。虽然“could”可以完成,但唯一的方法是基本上将数据中的所有数据进行累加,然后再次处理次级分组。这不是一个好主意,因为它在尝试保留数据时几乎总是会打破16MB的实际BSON限制。所以通常需要更好的方法。因此,在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这里默认的执行将揭开序幕的任务并行运行,这样既可以在同一时间在服务器上运行。输入数组中的每个管道都将传递给聚合方法,聚合方法将返回结果并将输出数组组合到最终结果中。

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

这不仅方便,而且并行执行使得用于返回结果的聚合方法更节省时间和减少征税(如果不能击败不可能的话)。

+0

ohhhhh [灯泡继续],然后我会像处理函数一样处理函数(管道,回调函数)(function(err,result),并从那里执行通常的try/catch和res.send(回调)?另一件事:把$ match部分放在最上面,以确保我只为每个集合获得正确的userId,或者是否在每个$ group内部都行? – kl02

+0

ahaha无视第一个问题,只是意识到我没有滚动所有的在这个例子里面,我看到了其余的部分。:) – kl02

+0

无视第二部分,我想到了$ match部分。非常感谢你指点我正确的方向! – kl02