2013-08-17 27 views
27

我有一個mongodb集合,其中每個文檔都有一些屬性和一個utc時間戳。我需要從集合中提取數據並使用聚合框架,因爲我使用集合中的數據在用戶界面上顯示一些圖表。但是,我需要根據用戶的時區進行聚合。假設我知道用戶的時區(通過瀏覽器的請求或其他方式傳遞),是否有任何方法使用聚合框架根據[客戶端]時區進行聚合?如何使用mongod在utc中存儲日期時處理時區問題?

+0

你可以更清楚你想做什麼,你爲什麼不能只是轉換到客戶端的時區後聚合?你總是可以使用mapreduce而不是聚合框架 - 它會比較慢,但會允許你需要做任何形式的臨時計算 – Mason

+0

實際上,我需要根據他/她的時區爲用戶生成每週報告。對於報告,我需要使用聚合框架。問題是,我應該在聚合時考慮用戶的時區。我不能在聚合後這樣做,否則會導致錯誤的結果。映射reduce是一個選項,但我需要這個按需,我在查找StackOverflow時發現它不應該用於查詢。我希望會有某種方式。 – Hrishi

+0

我不知道MongoDB能夠很好地回答,但我相信這種方法與我在RavenDB中描述的類似(這裏是http://ravendb.net/kb/61/working-with-date-and -time-in-ravendb#時區轉換),再次在['Foo_ByDate_MultiZone'索引](https://github.com/mj1856/RavenDB-NodaTime/wiki/Indexing-and-Querying)。如果Mongo允許類似的東西,你可以在Map中做這件事,就像我在Raven做的一樣。 –

回答

8

您要求的是目前正在討論的MongoDB issue SERVER-6310

我在a discussion thread的鏈接中找到此。

這個問題對於按日期進行分組是很常見的,包括SQL數據庫和NoSQL數據庫。事實上,我最近在RavenDB上討論過這個問題。有一個很好的問題描述和一個RavenDB解決方案here

MongoDB問題討論了一種解決方法,它與我在上面評論中描述的類似。您可以預先計算您感興趣的當地時間,然後將其分組。

用這兩種方法都難以覆蓋世界上的每個時區。您應該決定一小部分對您的用戶羣有意義的目標區域,例如我在RavenDB文章中描述的按部門辦法。

更新:此問題已於2017年7月在MongoDB中解決(版本3.5.11)。該解決方案在上面的第一個鏈接中進行了描述,但簡而言之,它們爲彙總表達式中的日期引入了新的對象格式:{ date: <dateExpression>, timezone: <tzExpression> },允許您指定聚合時使用的時區。有關Mongo文檔中的另一個示例,請參見here

+0

謝謝。 Hope MongoDB將來會提供這樣的選項。 – Hrishi

+0

@Matt,是否有任何解決方案可用於此最新的mongodb 2.6.x – Rams

+0

@rams - 我沒有檢查,但問題仍然標記爲打開/未解決。 –

35

除了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" } } }) 
+1

謝謝Astral。你的回答是完美的。我真的不想使用地圖縮減只是爲了做一個簡單的時區調整。 – cfchris

+0

這個答案很有意義。如果你想查詢包含EST和EDT的「東部時間」,你會怎麼做? – Joe

+0

根據解釋,不是'「$ lastActivity」'而不是答案中的'「$ orderTime」'? – digitalextremist

16

每個方法上面建議的工作完全正常,但因爲有一個新版本的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",其中上述定義的VARS是的返回值用過的。

+1

在涉及夏令時的情況下,這也不起作用,因爲您認爲可以減去固定時間,根據當地時間是否在夏令時中,可能會有一小時不同。 –

1

我在mongoose plugin中發現了一個解決方案來規範存儲日期的時區。

相關問題