首页 文章

日期问题 - 尝试通过PHP在Highcharts中绘制MongoDB数据

提问于
浏览
1

我在MongoDB中有这些数据:

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

我希望白天将其分组并将其提供给Highcharts,以显示每天的平均温度 . 像这样:http://jsfiddle.net/tw7n6wxb/3/

使用MongoDB Aggregation Pipeline,我能够根据其他一些示例和这篇伟大的帖子进行分组: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系列将值对的数组作为输入:示例:data:[[5,2],[6,3],[8,2]] . 每对上的第一个值是X值,该值必须是一个数字(当X轴配置为datetime时,X值以毫秒为单位) .

我遇到的问题是MongoDB将日期作为MongoDate对象返回,其中包含两个值,'sec'和'usec',而Highcharts期待一个数字 .

无论如何将MongoDate对象转换为管道中的整数?以$项目为例?我正在使用PHP,但我想避免在应用程序中进行后处理(如PHP日期格式化) .

或者,关于如何解决这个问题的任何其他想法?

谢谢,

1 回答

  • 2

    您似乎只想要从结果返回的时间戳值 . 确实有一种简单的方法可以在聚合框架中执行此操作而不使用date aggregation operators . 您可以使用基本的"date math",并使用可用于从日期对象中提取"timestamp"值并操纵它的技巧:

    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”的“纪元”日期,您可以将日期对象的“纪元时间戳”值作为数字 .

    然后通过从一天中的毫秒中减去模数(或余数)来应用基本日期数学 . 这会“舍入”该值以表示记录条目的“日期” .

    我喜欢发布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"
                            ]
                        }
                    }
                }
            }
        }}
    ])
    

    所以这真的很狡猾 . 完成初始"grouping"以确定每天的平均值后,您每天在结果文档中获得两个字段 _idavg . $map运算符在这里做的是将数组作为输入(在这种情况下,只是一个带有一对值的编号模板来标识位置)并处理每个元素以返回一个等于原始元素的数组 .

    这里的$cond运算符允许您查看该数组的当前元素的值,并使用当前文档中存在的另一个值来查看"swap it" . 因此,对于每个文档,结果包含一些配对数组,如:

    [ 1409346800000, 12 ]
    

    然后发生的一切都是将所有结果推送到一个文档中,其中“data”数组如下所示:

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

    现在,您在该结果中的数据元素是一个表示所需点的数组对数组 .

    当然,像 $map 这样的运营商只能从MongoDB 2.6及以后版本获得,所以如果你有可用的那么你可以使用它们,但是只需用类似的"map"操作处理代码中的结果:

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

    因此,无论您采用哪种方式,这都归结为数组操作,但日期操作技巧也可以为您节省一些工作,以获得结果作为预期的时间戳值 .

相关问题