2014-10-01 34 views
1

我有這樣的數據在MongoDB中到密謀在Highcharts MongoDB的數據:問題與日期 - 試圖通過PHP

{ 
    "_id": ObjectId("542bxxxxxxxxxxxx"), 
    "TEMP_C": 13, 
    "time": ISODate("2014-08-21T05:30:00Z") 
} 

我想白天組,並將其喂Highcharts,顯示每天的溫度平均。 像這樣:http://jsfiddle.net/tw7n6wxb/3/

使用MongoDB的聚合管道,我能夠做的分組,根據其他一些例子和這個偉大的職位:http://www.kamsky.org/stupid-tricks-with-mongodb/stupid-date-tricks-with-aggregation-framework

方的問題:爲什麼是迄今爲止在這麼複雜的組MongoDB的???最令人討厭的部分是在將日期對象分成'$ dayOfMonth','$ month'和'$ year'後重新編寫日期對象。 有沒有更簡單的方法來做到這一點?

無論如何,我得到這部分工作(我認爲)。這是結果:

{ 
    "_id" : {"sec":1409346800,"usec":0}, 
    "avg" : 12 
}, 
{ 
    "_id" : {"sec":1409356800,"usec":0}, 
    "avg" : 15 
}, 

但是,Highcharts系列採取值對作爲輸入的數組: 實施例:數據:[[5,2],[6,3],[8,2]。 每對上的第一個值是X值,該值必須是一個數字(當X軸配置爲日期時間時,X值以毫秒爲單位)。

我遇到的問題是,MongoDB將日期作爲MongoDate對象返回,其中有'sec'和'usec'兩個值,而Highcharts期待一個數字。

有沒有辦法將MongoDate對象轉換爲管道中的整數?例如使用$項目? 我使用PHP,但我想避免應用程序中的後期處理(如PHP日期格式化)。

或者,有關如何解決這個問題的任何其他想法?

謝謝,

回答

2

你似乎只是想從結果返回的時間戳值。確實有一種簡單的方法可以在不使用date aggregation operators的情況下在聚合框架中執行此操作。您可以使用基本的「日期的功能」,而是和與可用於提取日期對象的「時間戳」值,並操縱它一招:

db.collection.aggregate([ 
    { "$group": { 
     "_id": { 
      "$subtract": [ 
       { "$subtract": [ "$time", new Date("1970-01-01") ] }, 
       { "$mod": [ 
        { "$subtract": [ "$time", new Date("1970-01-01") ] }, 
        1000 * 60 * 60 * 24  
       ]} 
      ] 
     }, 
     "avg": { "$avg": "$TEMP_C" } 
    }} 
]) 

所以基本的「絕招」也就是當從另一個(或類似操作)中減去一個日期對象,則返回的結果是兩個時間差的「毫秒」的數字。因此,通過使用「1970-01-01」的「epoch」日期,您可以將日期對象的「epoch timestamp」值作爲數字。

然後,通過從這個值中減去一天中的毫秒數模(或餘數)來應用基本日期數學。這會「舍入」該值以表示記錄條目的「日期」。

我喜歡張貼JSON,因爲它解析無處不在,但在更多的PHP的方式,則是這樣的:

$collection->aggregate(array(
    array('$group' => array(
     '_id' => array(
      '$subtract' => array(
       array('$subtract' => array( 
        '$time', new MongoDate(strtotime("1970-01-01 00:00:00")) 
       )), 
       array( '$mod' => array(
        array('$subtract' => array( 
         '$time', new MongoDate(strtotime("1970-01-01 00:00:00")) 
        )), 
        1000 * 60 * 60 * 24  
       )) 
      ) 
     ), 
     "avg" => array('$avg' => '$TEMP_C') 
    )) 
)) 

因此比使用日期匯聚運營商獲得您想要的結果一點清潔劑。當然,這仍然不是「一路」如何讓數據呈現在您可以在客戶端使用的位置。

這裏要做的真實情況是操縱結果,以便獲得所需的輸出格式。這可能是更適合您的服務器代碼做處理返回的響應之前,但如果你有MongoDB的2.6或更大的則是「可能」做到這一點聚合管道本身:

db.collection.aggregate([ 
    { "$group": { 
     "_id": { 
      "$subtract": [ 
       { "$subtract": [ "$time", new Date("1970-01-01") ] }, 
       { "$mod": [ 
        { "$subtract": [ "$time", new Date("1970-01-01") ] }, 
        1000 * 60 * 60 * 24  
       ]} 
      ] 
     }, 
     "avg": { "$avg": "$TEMP_C" } 
    }}, 
    { "$group": { 
     "_id": null, 
     "data": { 
      "$push": { 
       "$map": { 
        "input": { "$literal": [ 1,2 ] }, 
        "as": "el", 
        "in": { 
         "$cond": [ 
          { "$eq": [ "$$el", 1 ] }, 
          "$$_id", 
          "$avg" 
         ] 
        } 
       } 
      } 
     } 
    }} 
]) 

所以這真的很狡猾。在完成初始「分組」以確定每天的平均值後,您每天會在結果文檔中獲得兩個字段,分別爲_idavg。這裏的$map運算符是以數組和輸入爲輸入(在這種情況下,只是一個帶有一對數值的編號模板來標識位置),並處理每個元素以返回一個與原始數據相同的數組。

這裏的$cond運算符允許您查看該數組的當前元素的值,並將其與當前文檔中存在的另一個值「交換」。因此,對於每個文檔,結果包含的東西,就像是一個配對數組:

[ 1409346800000, 12 ] 

然後,所有發生的一切結果如下推入一個「數據」陣列中的單個文件顯示:

{ "_id": null, "data": [ [..,..], [..,..], (...) ] } 

現在,您在那一個結果中的數據元素是一個代表您想要的點的數組對。

當然雖然,像$map運營商只能從MongoDB的2.6及以後的,所以如果你有一個可用的,那麼你可以使用它們,但否則只好處理代碼中的結果具有相似的「地圖」操作:

function my_combine($v) { 
    return array($v["_id"],$v["avg"]) 
} 

$newresult = array_map("my_combine", $result) 

所以這真的歸結爲數組操作,無論你使用哪種方法,但日期操作技巧也應該爲您節省一些工作,以獲得結果作爲預期的時間戳值。

+0

感謝您的詳細解答。確實很有幫助。第一部分,關於日期 - 時間的數學運作完美。正如你所說,訣竅是減去日期返回毫秒(不是日期)。我想我會將新日期('1970 ...')聲明爲全局或常量,以便在其他情況下使用它。根據第二部分(組和地圖),它確實不是必需的。我認爲這太過分了。 $ group by _id,$ project('_id'=> 0,0 =>'$ _id',1 =>'$ avg')結束了在管道中添加了兩個階段 - 刪除陣列中的鍵名對於Highcharts來說已經足夠了;) – 2014-10-03 19:45:42

+0

@s_mdq Well Overkill是我基本上描述它的方式,而PHP array_map正在做同樣的事情以實質上刪除鍵。真的只是在那裏表明,它可以基本上完​​成。 – 2014-10-04 00:23:17