MongoDBコレクションのレコードを1時間ごとに集計しようとしています。集計フレームワークの組み込み日付演算子($ hour、$ monthなど)を使用できるように、タイムスタンプ(ミリ秒)として保存された日付をISODateに変換する必要があります。
レコードは次のように保存されます
{
"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"}
}
}
}
)
私はそれをする方法がないと思います。集約フレームワークはネイティブコードで記述されているためです。 V8エンジンを使用していません。したがって、JavaScriptのすべてがフレームワーク内で機能するわけではありません(そのため、集約フレームワークがはるかに高速に実行されます)。
Map/Reduceはこれを解決する方法ですが、集約フレームワークのパフォーマンスは間違いなくはるかに優れています。
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
}
実際には、次のような構文を使用して、ミリ秒の時間をゼロミリ秒の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"}
}
}
}
);
結果は次のとおりです(サンプルデータのエントリが1つあります)。
{
"result": [
{
"_id": ObjectId("532828ac338ed9c33aa8eca7"),
"date": {
"hour": 11
}
}
],
"ok": 1
}
有効な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
}
_
開始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 }
{$add: [new Date(0), "$time"]
}関数がstring type
ではなくISO date type
を返す場合にこれを使用します
私はそのオプションをすべて使用しますが、それでも失敗します。$project
からの新しい日付は、ISO('2000-11-2:xxxxxxx')
のようなstring type
ではなく'2000-11-2:xxxxxxx'
のようなdate type
を返すため、これを使用します。
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