【问题标题】:Convert date from milliseconds to ISODate object将日期从毫秒转换为 ISODate 对象
【发布时间】:2014-05-07 01:19:19
【问题描述】:

我正在尝试按小时聚合 MongoDB 集合中的记录,并且需要将存储为时间戳(毫秒)的日期转换为 ISODate,以便我可以使用聚合框架的内置日期运算符($hour、$month 等)

记录存储为

{ 
"data" : { "UserId" : "abc", "ProjId" : "xyz"}, 
"time" : NumberLong("1395140780706"),
"_id" : ObjectId("532828ac338ed9c33aa8eca7") 
} 

我正在尝试使用以下类型的聚合查询:

db.events.aggregate(
    { 
       $match : { 
         "time" : { $gte : 1395186209804, $lte : 1395192902825 } 
       } 
    }, 
    { 
       $project : {
         _id : "$_id", 
         dt : {$concat : (Date("$time")).toString()} // need to project as ISODate
       } 
    },
    // process records further in $project or $group clause
)

产生以下形式的结果:

{
    "result" : [
        { 
            "_id" : ObjectId("5328da21fd207d9c3567d3ec"), 
            "dt" : "Fri Mar 21 2014 17:35:46 GMT-0400 (EDT)" 
        }, 
        { 
            "_id" : ObjectId("5328da21fd207d9c3567d3ed"), 
            "dt" : "Fri Mar 21 2014 17:35:46 GMT-0400 (EDT)" 
        }, 
            ... 
} 

我想从日期中提取小时、日、月和年,但由于时间被预测为字符串,我无法使用聚合框架的内置日期运算符($hour 等)。

如何将时间从毫秒转换为 ISO 日期,以执行以下操作:

db.events.aggregate(
    {
        $match : { 
            "time" : { $gte : 1395186209804, $lte : 1395192902825 } 
        }
    },
    {
        $project : {
            _id : "$_id",
            dt : <ISO date from "$time">
        }
    },
    { 
        $project : {
            _id : "$_id",
            date : { 
                hour : {$hour : "$dt"} 
            }
        }
    }
)

【问题讨论】:

  • 您应该考虑使用 map/reduce 代替聚合

标签: javascript mongodb type-conversion aggregation-framework


【解决方案1】:

Mongo 4.0 开始,有一个新的 $toDate 聚合运算符可以从各种类型转换为日期(在本例中为 long):

// { time: NumberLong("1395140780706") }
db.collection.aggregate({ $set: { time: { $toDate: "$time" } } })
// { time: ISODate("2014-03-18T11:06:20.706Z") }

为了节省时间:

// { time: NumberLong("1395140780706") }
db.collection.aggregate({ $project: { hour: { $hour: { $toDate: "$time" } } } })
// { hour: 11 }

【讨论】:

    【解决方案2】:

    如果{$add: [new Date(0), "$time"]} 函数返回string type 而不是ISO date type,则使用此函数

    我使用了所有这些选项,但仍然失败,因为我从 $project 返回的新日期为 string type,如 '2000-11-2:xxxxxxx' 而不是 date type,如 ISO('2000-11-2:xxxxxxx'),对于任何与我有同样问题的人,请使用此选项。

    db.events.aggregate(
        {
            $project : {
                _id : "$_id",
                dt : {$add: [new Date(0), "$time"]}
            }
        },
        { 
            $project : {
                _id : "$_id",
                "year": { $substr: [ "$dt", 0, 4 ] },
                "month": { $substr: [ "$dt", 5, 2] },
                "day": { $substr: [ "$dt", 8, 2 ] }
            }
        }
    );
    

    结果是

     { _id: '59f940eaea87453b30f42cf5',
        year: '2017',
        month: '07',
        day: '04' 
    },
    

    你可以根据你想要subsetstring来获得小时或分钟,然后你可以根据相同的日期、月份或年份再次group

    【讨论】:

      【解决方案3】:

      要返回一个有效的 BSON 日期,您只需要使用 $add 运算符的小日期“数学”。您需要将new Date(0) 添加到时间戳。 new Date(0) 表示自 Unix 纪元(1970 年 1 月 1 日)以来的毫秒数,是 new Date("1970-01-01") 的简写。

      db.events.aggregate([
          { "$match": { "time": { "$gte" : 1395136209804, "$lte" : 1395192902825 } } },
          { "$project": { 
              "hour": { "$hour": { "$add": [ new Date(0), "$time" ] } }, 
              "day": { "$dayOfMonth":  { "$add": [ new Date(0), "$time" ] } },
              "month": { "$month": { "$add": [ new Date(0), "$time" ] } },
              "year": { "$year":  { "$add": [ new Date(0), "$time" ] } } 
          }} 
      ])
      

      产量:

      {
          "_id" : ObjectId("532828ac338ed9c33aa8eca7"),
          "hour" : 11,
          "day" : 18,
          "month" : 3,
          "year" : 2014
      }
      

      【讨论】:

        【解决方案4】:

        实际上,有可能,诀窍是使用类似于以下的语法将毫秒时间添加到零毫秒 Date() 对象:

        dt : {$add: [new Date(0), "$time"]}
        

        我从上面修改了您的聚合以产生结果:

        db.events.aggregate(
            {
                $project : {
                    _id : "$_id",
                    dt : {$add: [new Date(0), "$time"]}
                }
            },
            { 
                $project : {
                    _id : "$_id",
                    date : { 
                        hour : {$hour : "$dt"} 
                    }
                }
            }
        );
        

        结果是(带有您的样本数据的一项):

        {
          "result": [
            {
              "_id": ObjectId("532828ac338ed9c33aa8eca7"),
              "date": {
                "hour": 11
              }
            }
          ],
          "ok": 1
        }
        

        【讨论】:

          【解决方案5】:

          我认为没有办法做到这一点。因为聚合框架是用本机代码编写的。不使用V8引擎。因此,JavaScript 的所有内容都无法在框架内运行(这也是聚合框架运行得更快的原因)。
          Map/Reduce 是解决此问题的一种方法,但聚合框架的性能肯定要好得多。

          关于 Map/Reduce 性能,请阅读this thread

          另一种解决方法是从聚合框架中获取“原始”结果,将其放入 JSON 数组中。然后通过运行 JavaScript 进行转换。有点像:

          var results = db.events.aggregate(...);
          reasult.forEach(function(data) {
              data.date = new Date(data.dateInMillionSeconds);
              // date is now stored in the "date" property
          }
          

          【讨论】:

          • 感谢您的回复。我将尝试使用 part-Mongo/part-JS 解决方案。
          • 这已经有一段时间不正确了;见this other answer
          猜你喜欢
          • 2019-05-20
          • 1970-01-01
          • 2019-12-24
          • 1970-01-01
          • 2016-02-02
          • 1970-01-01
          • 2010-10-08
          • 1970-01-01
          相关资源
          最近更新 更多