Mongodb使用多个值的计数查询聚合

时间:2015-11-23 00:19:21

标签: ruby-on-rails mongodb mongoid mongodb-query aggregation-framework

我在我的一个rails应用程序中使用Mongoid来获取mongodb

class Tracking
  include Mongoid::Document
  include Mongoid::Timestamps

  field :article_id,      type: String
  field :action,          type: String # like | comment
  field :actor_gender,    type: String # male | female | unknown

  field :city,            type: String
  field :state,           type: String
  field :country,         type: String
end

在这里,我想以这种表格形式获取记录,

article_id | state | male_like_count | female_like_count | unknown_gender_like_count | date

juhkwu2367 | California | 21 | 7  | 1 | 11-20-2015
juhkwu2367 | New York   | 62 | 23 | 3 | 11-20-2015
juhkwu2367 | Vermont    | 48 | 27 | 3 | 11-20-2015
juhkwu2367 | California | 21 | 7  | 1 | 11-21-2015
juhkwu2367 | New York   | 62 | 23 | 3 | 11-21-2015
juhkwu2367 | Vermont    | 48 | 27 | 3 | 11-21-2015

此处查询的输入为:

article_id 
country
date range (from and to)
action (is `like` in this scenario)
sort_by [ date | state | male_like_count | female_like_count ]

这是我正在尝试的,通过在https://docs.mongodb.org/v3.0/reference/operator/aggregation/group/

引用一个例子
db.trackings.aggregate(
   [
      {
        $group : {
           _id : { month: { $month: "$created_at" }, day: { $dayOfMonth: "$created_at" }, year: { $year: "$created_at" }, article_id:  "$article_id", state: "$state", country: "$country"},
           article_id: "$article_id",
           country: ??,
           state: "$state",
           male_like_count: { $sum:  ?? } },
           female_like_count: { $sum:  ?? } },
           unknown_gender_like_count: { $sum:  ?? } },
           date: ??
        }
      }
   ]
)

那么我应该在??的地方放置什么来比较按性别分列的数量以及如何为sorting_option添加条款?

1 个答案:

答案 0 :(得分:1)

您主要是在寻找$cond运算符来评估条件并返回特定计数器是否应该递增,但是此处还缺少一些其他聚合概念:

db.trackings.aggregate([
    { "$match": {
        "created_at": { "$gte": startDate, "$lt": endDate },
        "country": "US",
        "action": "like"
    }},
    { "$group": {
        "_id": { 
            "date": {
                "month": { "$month": "$created_at" }, 
                "day": { "$dayOfMonth": "$created_at" },
                "year": { "$year": "$created_at" }
            },
            "article_id":  "$article_id", 
            "state": "$state"
        },
        "male_like_count": { 
            "$sum": {
                "$cond": [
                    { "$eq": [ "$gender", "male" ] }                            
                    1,
                    0
                ]
            }
        },
        "female_like_count": { 
            "$sum": {
                "$cond": [
                    { "$eq": [ "$gender", "female" ] }                            
                    1,
                    0
                ]
            }
        },
        "unknown_like_count": { 
            "$sum": {
                "$cond": [
                    { "$eq": [ "$gender", "unknown" ] }                            
                    1,
                    0
                ]
            }
        }
      }},
      { "$sort": {
        "_id.date.year": 1,
        "_id.date.month": 1,
        "_id.date.day": 1,
        "_id.article_id": 1,
        "_id.state": 1,
        "male_like_count": 1,
        "female_like_count": 1
      }}
   ]
)

首先,您基本上想要$match,这就是为聚合管道提供“查询”条件的方式。它基本上可以是任何管道阶段,但是当首先使用它时,它将过滤在以下操作中考虑的输入。在这种情况下,所需的日期范围以及国家/地区,以及删除任何非“喜欢”的内容,因为您不担心这些数字。

然后,所有项目都按_id中相应的“键”进行分组。这可以用作复合字段,主要是因为所有这些字段值都被视为分组键的一部分,也适用于一个小组织。

您似乎也在您的输出中询问_id以外的“不同字段”。不要那样做。数据已经存在,因此复制它没有意义。您可以通过$first作为聚合运算符在_id之外生成相同的内容,或者您​​甚至可以在管道末尾使用$project阶段来重命名字段。但是你最好放弃你认为自己需要的习惯,因为这只会花费时间和空间来获得回应。

如果有的话,你似乎比其他任何事情更接近“漂亮约会”。我个人更喜欢使用“日期数学”进行大多数操作,因此适用于mongoid的更改列表将是:

Tracking.collection.aggregate([
    { "$match" => {
        "created_at" => { "$gte" => startDate, "$lt" => endDate },
        "country" => "US",
        "action" => "like"
    }},
    { "$group" => {
        "_id" => { 
            "date" => {
                "$add" => [
                    { "$subtract" => [
                        { "$subtract" => [ "$created_at", Time.at(0).utc.to_datetime ] },
                        { "$mod" => [
                            { "$subtract" => [ "$created_at", Time.at(0).utc.to_datetime ] },
                            1000 * 60 * 60 * 24
                        ]}
                    ]},
                    Time.at(0).utc.to_datetime
                ]
            },
            "article_id" =>  "$article_id", 
            "state" => "$state"
        },
        "male_like_count" => { 
            "$sum" => {
                "$cond" => [
                    { "$eq" => [ "$gender", "male" ] }                            
                    1,
                    0
                ]
            }
        },
        "female_like_count" => { 
            "$sum" => {
                "$cond" => [
                    { "$eq" => [ "$gender", "female" ] }                            
                    1,
                    0
                ]
            }
        },
        "unknown_like_count" => { 
            "$sum" => {
                "$cond" => [
                    { "$eq" =>[ "$gender", "unknown" ] }                            
                    1,
                    0
                ]
            }
        }
      }},
      { "$sort" => {
        "_id.date" => 1,
        "_id.article_id" => 1,
        "_id.state" => 1,
        "male_like_count" => 1,
        "female_like_count" => 1
      }}
])

这实际上只是让DateTime对象适合用作与纪元日期对应的驱动程序参数并运行各种操作。处理$subtract一个BSON日期和另一个BSON日期将产生一个数值,随后可以使用应用的数学舍入到当天。然后,当使用带有数字时间戳值的$add到BSON日期(再次表示纪元)时,结果再次是BSON Date对象,当然还有调整后的舍入值。

然后,这只是将$sort再次应用为聚合管道阶段,与外部修饰符相对应。与$match原则非常相似,聚合管道可以在任何地方排序,但最后总是处理最终结果。