2012-10-20 59 views
5

我有一个简单的DB布局是这样的:计算数量和平均使用MongoDB的聚集

client 
    id 
    sex (male/female) 
    birthday (date)  

client 
    id 
    sex (male/female) 
    birthday (date) 

(...) 

我想写输出多少男性和女性客户我有一个集合命令,我也喜欢输出男性和女性的平均年龄,不确定我可以在同一个命令中执行此操作,还是需要2个单独的命令?

// Count of males/females, average age 
Clients.aggregate({ 
    $project : {"sex"  : 1, 
      "sexCount" : 1, 
      "birthday" : 1, 
      "avgAge" : 1 
       } 
    }, 
    { 
     $match: {"sex": {$exists: true}} 
    }, 
    { 
     $group: { 
        _id  : "$sex", 
      sexCount : { $sum: 1 }, 
      avgAge : { $avg: "$birthday" }, 
      } 
    }, 
    { $sort: { _id: 1 } } 
    , function(err, sex_dbres) { 
      if (err) 
       throw err; 
      else{ 
       (...) 
      } 
     });   

通过上面我得到男性/女性的计数的代码,但avgAge当属0任何想法?

非常感谢

+0

请注意,您不需要在第一步中投射字段sexCount或avgAge,因为这些字段是您将在$ group步骤中计算的字段。 –

回答

4

日期对象不能是“平均”,但数字可以。您可以将日期转换为时间戳值,然后从中找到平均值。但仍然不是平均年龄,您需要从聚合函数外的当前日期中减去结果。

另一种选择是假设年龄可以仅使用年份的一部分来计算(即如果我出生于2000年12月1日,在今天的报告中我将是12岁,而不是11岁)。在这种情况下,您可以使用date operators来提取年份值。

$project : {"sex"  : 1, 
      "sexCount" : 1, 
      "year" : {$year: "$birthday"}, 
      } 
}, 
$project : {"sex"  : 1, 
      "sexCount" : 1, 
      "age" : {$subtract: [2012, '$year']}, 
      } 
}, 
+0

谢谢。刚刚意识到生日存储为字符串(“Sat May 1982 1982 00:00:00 GMT + 0200”),这使事情变得更加困难。有可能把它作为一个数字吗?我试过做一个substr来获取年份,但之后我很难将其转换为数字,然后执行您提出的$减法。 如果这很困难,那么我想我可以将该字段变成日期。 –

+0

转换不是聚合框架的一部分,我想你可能需要使用MapReduce,你可以在其中编写任意的JavaScript代码,或者运行数据库并转换所有日期。 – Dmitry

+0

谢谢!我将这一年提取到一个单独的领域,现在可以轻松地完成平均。 –

6

答案就会简单得多,如果你是在原始文档中存储的年龄(如梅德贴,你可能只是做你的$group步直avgAge:{$avg:"$age"}

聚合框架是非常漂亮的,但并有许多很酷的运营商允许你来计算这个丢失的年龄字段“对飞”

我要聚集的每一步存储在一个变量,以便更容易看到发生了什么事情。

today = new Date(); 
// split today and bday into numerical year and numerical day-of-the-year 
project1= { 
    "$project" : { 
     "sex" : 1, 
     "todayYear" : { 
      "$year" : today 
     }, 
     "todayDay" : { 
      "$dayOfYear" : today 
     }, 
     "by" : { 
      "$year" : "$bday" 
     }, 
     "bd" : { 
      "$dayOfYear" : "$bday" 
     } 
    } 
}; 
// calculate age in days by subtracting bday in days from today in days 
project2 = { 
    "$project" : { 
     "sex" : 1, 
     "age" : { 
      "$subtract" : [ 
       { 
        "$add" : [ 
         { 
          "$multiply" : [ 
           "$todayYear", 
           365 
          ] 
         }, 
         "$todayDay" 
        ] 
       }, 
       { 
        "$add" : [ 
         { 
          "$multiply" : [ 
           "$by", 
           365 
          ] 
         }, 
         "$bd" 
        ] 
       } 
      ] 
     } 
    } 
}; 
// sum up for each sex the count and compute avg age (in days) 
group = { 
    "$group" : { 
     "_id" : "$sex", 
     "total" : { 
      "$sum" : 1 
     }, 
     "avgAge" : { 
      "$avg" : "$age" 
     } 
    } 
}; 
// divide days by 365 to get age in years. 
project3 = { 
    "$project" : { 
     "_id" : 0, 
     "sex" : "$_id", 
     "total" : 1, 
     "averageAge" : { 
      "$divide" : [ 
       "$avgAge", 
       365 
      ] 
     } 
    } 
}; 

现在你可以运行聚集:

> db.client.find({},{_id:0}) 
{ "sex" : "male", "bday" : ISODate("2000-02-02T08:00:00Z") } 
{ "sex" : "male", "bday" : ISODate("1987-02-02T08:00:00Z") } 
{ "sex" : "female", "bday" : ISODate("1989-02-02T08:00:00Z") } 
{ "sex" : "female", "bday" : ISODate("1993-11-02T08:00:00Z") } 
> db.client.aggregate([ project1, project2, group, project3 ]) 
{ 
    "result" : [ 
     { 
      "sex" : "female", 
      "total" : 2, 
      "averageAge" : 21.34109589041096 
     }, 
     { 
      "sex" : "male", 
      "total" : 2, 
      "averageAge" : 19.215068493150685 
     } 
    ], 
    "ok" : 1 
} 
> 

的原因,这并不简单,目前聚合框架不支持的日期直接扣除。请投票给https://jira.mongodb.org/browse/SERVER-6239这是针对下一个主要版本 - 一旦实施它应该允许直接减去日期(尽管您仍然需要将其转换为适当的粒度,在这种情况下可能年)。

+0

当然,另一种做法可能是将bday转换为天,获取组中的平均值和最终项目计算时间从今天的天数减去天数除以365后的天数。 –

+0

感谢Asya,类似的方法如上。 –