Преобразование даты с миллисекунд в объект 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"}
}
}
}
)
Ответы
Ответ 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 }