首页 > 解决方案 > 查找按今天、本周、本月分组的文档数

问题描述

我正在使用 Redash 执行数据可视化,并且我的文档具有如下结构:

{
"id" : "12345",
"created_at" : "07/06/19 08:53"
},
{
"id" : "123456",
"created_at" : "07/06/19 12:13" 
},
{
"id" : "123457",
"created_at" : "06/06/19 20:18"
}

我想要的结果是

{
"TimeRange" : "today",
"count" : "6"
},
{
"TimeRange" : "this Week",
"count" : 15
},
{
"TimeRange" : "this Month",
"count" : 40
}

我可以使用按日期分组

db.doc.aggregate([
     { $group: 
        { _id: { $dateToString: { format: "%Y-%m-%d", date: "$created_at" } }, count: { $sum: 1 } } }
])  

但是有什么方法最好使用聚合在指定的不同时间范围内显示结果?

标签: mongodbaggregation-framework

解决方案


这里的日期格式特别令人讨厌,因为缺少世纪和月份与日期的歧义。存储真正的日期时间会更好,这样日期操作就可以轻松应对它们。但是对于这个练习,让我们假设你不能。MongoDB 中的日期函数避免了假设,因此没有无世纪dateFromString格式组件,因此我们必须自己完成。好的。让我们从这组数据开始,类似于你的:

var r = [
         // Assume MM/DD/YY  where YY is 20YY
         // last week                                                           
         {"created_at" : "06/01/19 10:53"},

         // yesterday, this week                                                
         {"created_at" : "06/05/19 10:53"},

         // today                                                    
         {"created_at" : "06/06/19 08:53"},
         {"created_at" : "06/06/19 12:53"},
         {"created_at" : "06/06/19 18:33"},

         // tomorrow, this week                                                 
         {"created_at" : "06/07/19 19:33"},
         {"created_at" : "06/07/19 20:33"},

         // later, but this month                                               
         {"created_at" : "06/27/19 07:33"},
         {"created_at" : "06/27/19 08:33"},

         // next month                                                          
         {"created_at" : "07/04/19 11:33"},

         // next year                                                           
         {"created_at" : "07/04/20 11:33"}

    ];


为了弄清楚今天、本月等,我们需要在我们存储的数据之外设置一个目标日期。

today = new ISODate("2019-06-06");
target_week = 22;   // up to you to implement
target_mon = today.getMonth() + 1;   // MongoDB mon is 1-12, not 0-11
target_year = today.getFullYear();

这是一个解决方案。我们可以使用快捷方式并只使用日期字符串的片段,但我们将构建一个真实的日期,它可以很好地设置我们执行特定请求分组之外的其他事情。这意味着我们将在我们将它们放入日期之后将它们拉出日期。

db.foo.aggregate([

// Create a real date from the pieces.  Ignore time bits for now:               
{ $addFields: {"rd":
  {$dateFromParts : {
        "year": {$add: [2000,{$toInt: {$substr: ["$created_at",6,2]}} ] },
        "month":  {$toInt: {$substr: ["$created_at",0,2]}},
        "day":    {$toInt: {$substr: ["$created_at",3,2]}}
    }}
  }}

// Now use facet to act as a "multigroup":                                      
,{$facet: {
  "today": [
{$match: { "rd": today}}, {$count: "n"}
           ]

  ,"This week": [
{$project: { "w": {$week: "$rd"}, "y": {$year: "$rd"} }}
,{$match: { "w": target_week, "y": target_year}}
,{$count: "n"}
                 ]

  ,"This month": [
{$project: { "m": {$month: "$rd"}, "y": {$year: "$rd"} }}
,{$match: { "m": target_mon, "y": target_year}}
,{$count: "n"}
                  ]
    }}

  ,"This year": [
{$project: { "y": {$year: "$rd"} }}
,{$match: { "y": target_year}}
,{$count: "n"}
                  ]
    }}

]);

产生

{
    "today" : [
        {
            "n" : 3
        }
    ],
    "This week" : [
        {
            "n" : 6
        }
    ],
    "This month" : [
        {
            "n" : 9
        }
    ],
    "This year" : [
        {
            "n" : 10
        }
    ]
}

推荐阅读