33

我有一个 mongodb 集合,其中每个文档都有一些属性和 utc 时间戳。我需要从集合中提取数据并使用聚合框架,因为我使用集合中的数据在用户界面上显示一些图表。但是,我需要根据用户的时区进行聚合。假设我知道用户的时区(从浏览器或以其他方式传递请求),有没有办法使用聚合框架根据 [client's] 时区进行聚合?

4

3 回答 3

37

除了 Matt Johnson 提到的 SERVER-6310 之外,另一种解决方法是使用$project运算符从 UTC 时区添加或减去,以将时间“转移”到正确的本地区域。事实证明,您可以以毫秒为单位增加或减少时间。

例如,假设我有一个名为orderTime. 我想查询 EDT。那是距 UTC 的 -4 小时。那是 4 * 60 * 60 * 1000 毫秒。

因此,我将编写以下投影以获取day_ordered所有记录的当地时间:

db.table.aggregate( 
    { $project : { orderTimeLocal : { $subtract : [ "$orderTime", 14400000] } } },
    { $project : { day_ordered : { $dayOfYear : "$orderTimeLocal" } } })
于 2013-08-20T19:37:43.850 回答
17

上面建议的每种方法都可以正常工作,但是由于有一个新版本的 mongodb,从 2.6 开始,您可以$let在聚合框架中使用,这将让您动态创建变量,从而避免$project在分组之前需要。现在您可以创建一个变量$let来保存本地化时间,并在$group运算符中使用它。

就像是:

db.test.aggregate([
   {$group: { 
        _id: { 
             $let: { 
                 vars: {  
                     local_time: { $subtract: ["$date", 10800000]} 
                 }, 
                 in: { 
                    $concat: [{$substr: [{$year: "$$local_time"}, 0, 4]}, 
                              "-", 
                              {$substr: [{$month: "$$local_time"}, 0, 2]}, 
                              "-", 
                              {$substr: [{$dayOfMonth: "$$local_time"}, 0, 2]}]
                 }
              }
         }, 
         count: {$sum: 1}
     }
 }])

请注意,您使用$let了块/变量的内部定义,并且该块/变量的值是子表达式的返回值"in",其中使用了上面定义的变量。

于 2014-04-23T01:17:46.440 回答
12

您所要求的内容目前正在MongoDB 问题 SERVER-6310中进行讨论。

我在讨论线程的链接中找到了这个。

这个问题对于任何按日期进行的分组都很常见,包括 SQL 数据库和 NoSQL 数据库。事实上,我最近在 RavenDB 中谈到了这个问题。这里有一个很好的问题描述和 RavenDB 解决方案。

MongoDB 问题讨论了一种解决方法,类似于我在上面的评论中描述的。您预先计算您感兴趣的当地时间,然后按这些时间分组。

用任何一种方法都很难覆盖世界上的每个时区。您应该确定一小部分对您的用户群有意义的目标区域,例如我在 RavenDB 文章中描述的每个办公室的方法。

更新:2017 年 7 月(版本 3.5.11)在 MongoDB 中解决了这个问题。 该解决方案在上面的第一个链接中进行了描述,但简而言之,他们为聚合表达式中的日期引入了一种新的对象格式:{ date: <dateExpression>, timezone: <tzExpression> }允许您在聚合时指定要使用的时区。有关 Mongo 文档中的另一个示例,请参见此处

于 2013-08-18T00:07:07.110 回答