Подтвердить что ты не робот

Преобразование даты с миллисекунд в объект ISODate

Я пытаюсь агрегировать записи в коллекции 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)" 
        }, 
            ... 
} 

Я хочу извлечь час, день, месяц и год из даты, но так как время проецируется в виде строки, я не могу использовать встроенные операторы даты в агрегированной структуре ($ час и т.д.).

Как я могу преобразовать время из миллисекунд в дату 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"} 
            }
        }
    }
)
4b9b3361

Ответ 1

Я предполагаю, что нет способа сделать это. Поскольку структура агрегации написана в собственном коде. не используя двигатель V8. Таким образом, все JavaScript не будет работать в рамках (а также почему структура агрегации работает намного быстрее).
Map/Reduce - это способ работать над этим, но структура агрегации определенно имеет намного лучшую производительность.

О карте/Уменьшите производительность, прочитайте этот поток.

Еще один способ его решения - получить "исходный" результат из структуры агрегации, поместить его в массив 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
}

Ответ 2

На самом деле, возможно, трюк заключается в том, чтобы добавить миллисекундное время к объекту 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
}

Ответ 3

Чтобы вернуть действительную дату BSON, вам понадобится небольшая дата "математика", используя оператор $add. Вам нужно добавить new Date(0) в метку времени. new Date(0) представляет собой число миллисекунд с эпохи Unix (1 января 1970 г.) и является сокращением для 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

используйте эту функцию, если {$add: [new Date(0), "$time"]} возвращает string type не a ISO date type

Я использую всю эту опцию, но все еще не выполняю работу, потому что моя новая дата из $project возвращает string type как '2000-11-2:xxxxxxx' not 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' 
},

вы можете получить часы или минуты, если хотите, в зависимости от того, какой string вы хотите subset, тогда вы можете group снова в соответствии с той же датой, месяцем или годом

Ответ 5

Начиная с Mongo 4.0, появился новый оператор агрегации $toDate который может конвертировать из различных типов в дату (в $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 }