我有一个 mongodb 集合,其中每个文档都有一些属性和 utc 时间戳。我需要从集合中提取数据并使用聚合框架,因为我使用集合中的数据在用户界面上显示一些图表。但是,我需要根据用户的时区进行聚合。假设我知道用户的时区(从浏览器或以其他方式传递请求),有没有办法使用聚合框架根据 [client's] 时区进行聚合?
3 回答
除了 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" } } })
上面建议的每种方法都可以正常工作,但是由于有一个新版本的 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"
,其中使用了上面定义的变量。
您所要求的内容目前正在MongoDB 问题 SERVER-6310中进行讨论。
我在讨论线程的链接中找到了这个。
这个问题对于任何按日期进行的分组都很常见,包括 SQL 数据库和 NoSQL 数据库。事实上,我最近在 RavenDB 中谈到了这个问题。这里有一个很好的问题描述和 RavenDB 解决方案。
MongoDB 问题讨论了一种解决方法,类似于我在上面的评论中描述的。您预先计算您感兴趣的当地时间,然后按这些时间分组。
用任何一种方法都很难覆盖世界上的每个时区。您应该确定一小部分对您的用户群有意义的目标区域,例如我在 RavenDB 文章中描述的每个办公室的方法。
更新:2017 年 7 月(版本 3.5.11)在 MongoDB 中解决了这个问题。 该解决方案在上面的第一个链接中进行了描述,但简而言之,他们为聚合表达式中的日期引入了一种新的对象格式:{ date: <dateExpression>, timezone: <tzExpression> }
允许您在聚合时指定要使用的时区。有关 Mongo 文档中的另一个示例,请参见此处。