web-dev-qa-db-ja.com

MongoDbで15分間隔でグループ結果

この構造のような「ステータス」コレクションがあります-

{
    _id: ObjectId("545a0b63b03dbcd1238b4567"),
    status: 1004,
    comment: "Rem dolor ipsam placeat omnis non. Aspernatur nobis qui nisi similique.",
    created_at: ISODate("2014-11-05T11:34:59.804Z")
},
{
    _id: ObjectId("545a0b66b03dbcd1238b4568"),
    status: 1001,
    comment: "Sint et eos vero ipsa voluptatem harum. Hic unde voluptatibus et blanditiis quod modi.",
    created_at: ISODate("2014-11-05T11:35:02.814Z")
}
....
....

そのコレクションから15分間隔でグループ化された結果を取得する必要があります。

40
Hein Zaw Htet

これを行うにはいくつかの方法があります。

1つは Date Aggregation Operators を使用して、ドキュメント内の「日付」値を分析できるようにします。具体的には、「グループ化」を主な目的としています。

db.collection.aggregate([
  { "$group": {
    "_id": {
      "year": { "$year": "$created_at" },
      "dayOfYear": { "$dayOfYear": "$created_at" },
      "hour": { "$hour": "$created_at" },
      "interval": {
        "$subtract": [ 
          { "$minute": "$created_at" },
          { "$mod": [{ "$minute": "$created_at"}, 15] }
        ]
      }
    }},
    "count": { "$sum": 1 }
  }}
])

2番目の方法は、日付オブジェクトが別の日付オブジェクトから減算(または他の直接的な数学演算)されるときのちょっとしたトリックを使用することです。したがって、エポック日付を使用するだけで、エポックミリ秒表現を取得できます。次に、間隔に日付計算を使用します。

db.collection.aggregate([
    { "$group": {
        "_id": {
            "$subtract": [
                { "$subtract": [ "$created_at", new Date("1970-01-01") ] },
                { "$mod": [ 
                    { "$subtract": [ "$created_at", new Date("1970-01-01") ] },
                    1000 * 60 * 15
                ]}
            ]
        },
        "count": { "$sum": 1 }
    }}
])

したがって、グループ化間隔に必要な出力形式の種類によって異なります。どちらも基本的に同じものを表し、コード内で「日付」オブジェクトとして再構築するのに十分なデータを持っています。

グループ化_idの後の「グループ化演算子」部分には、必要なものを何でも配置できます。私はあなたが本当にやりたいことに関するあなた自身からの実際の声明の代わりに、基本的な「カウント」の例を使用しています。


MongoDB 4.x以降

オリジナルの執筆以来、日付集計演算子にいくつかの追加がありましたが、BSON日付変換で行われる基本的な数学のトリックとは対照的に、MongoDB 4.0からは実際の「型の実際のキャスト」が行われます。

たとえば、ここで新しいヘルパーとして $toLong および $toDate を使用できます。

db.collection.aggregate([
  { "$group": {
    "_id": {
      "$toDate": {
        "$subtract": [
          { "$toLong": "$created_at" },
          { "$mod": [ { "$toLong": "$created_at" }, 1000 * 60 * 15 ] }
        ]
      }
    },
    "count": { "$sum": 1 }
  }}
])

これは少し短く、「エポック」値の外部BSON日付をパイプライン定義の定数として定義する必要がないため、すべての言語実装でかなり一貫しています。

これらは、型変換用の「ヘルパー」メソッドの2つにすぎず、すべて $convert メソッドに結び付けられています。これは、nullまたは変換エラー。

このようなキャストでは、主キーのDateからObjectId情報を取得することもできます。これは、「作成」日付の信頼できるソースであるためです。

db.collection.aggregate([
  { "$group": {
    "_id": {
      "$toDate": {
        "$subtract": [
          { "$toLong": { "$toDate": "$_id" }  },
          { "$mod": [ { "$toLong": { "$toDate": "$_id" } }, 1000 * 60 * 15 ] }
        ]
      }
    },
    "count": { "$sum": 1 }
  }}
])

したがって、この種の変換を使用した「キャストタイプ」は非常に強力なツールになります。

警告-ObjectId値は$toDate 変換を許可するデータの一部を構成する内部時間値に対してのみ。実際に挿入される「時間」は、おそらく使用中のドライバーに依存します。 precisionが必要な場合、ObjectId値に依存する代わりに、個別のBSON Dateフィールドを使用することをお勧めします。

105
Neil Lunn

私はここで他の答えが好きで、主に集計日付演算子の代わりに日付数学を使用するのが好きです。

ここで追加したいのは、結果として「数値」タイムスタンプではなく、このアプローチによって集約フレームワークからDateオブジェクトを返すこともできることです。 _$add_ を使用して、同じ原理に基づいた少し余分な計算を行います。

_db.collection.aggregate([
    { "$group": {
        "_id": {
            "$add": [
                { "$subtract": [
                    { "$subtract": [ "$current_date", new Date(0) ] },
                    { "$mod": [ 
                        { "$subtract": [ "$current_date", new Date(0) ] },
                        1000 * 60 * 15
                    ]}
                ] },
                new Date(0)
            ]
        },
        "count": { "$sum": 1 }
    }}
])
_

JavaScriptのDate(0)構文は、エポックからの0ミリ秒がエポックであるため、同じ「エポック」日付を短い形式で表します。しかし、主なポイントは、別のBSON日付オブジェクトへの「追加」が数値識別子で行われた場合、記述された条件の逆が真になり、最終結果は実際にはDateになるということです。

すべてのドライバーは、このアプローチによってネイティブのDate型をそれぞれの言語に返します。

15
Blakes Seven

Mongodb.version()<3.0のもう少し美しい

db.collection.aggregate([
    {$match: {created_at:{$exists:1}}},
    {$group: {
        _id: {$add:[
            {$dayOfYear: "$created_at" },
            {$multiply: [{$year: "$created_at"}, 1000]}
        ]},
        count: {$sum: 1 }
    }},
    {$sort:{_id:-1}}
])
7
Stierlitz

別の便利な方法:

db.collection.aggregate([
  {$group: {
    _id: { 
      overallTime: { 
        $dateToString: { format: "%Y-%m-%dT%H", date: "$created_at" } 
      },
      interval: { $trunc: { $divide: [{ $minute: "$created_at" }, 15 ]}}
    },
  }},
])

minhourday間隔の方が簡単です:

var format = "%Y-%m-%dT%H:%M"; // 1 min
var format = "%Y-%m-%dT%H"; // 1 hour
var format = "%Y-%m-%d"; // 1 day

db.collection.aggregate([
  {$group: {
    _id: { $dateToString: { format: format, date: "$created_at" } },
  }},
])
4
Sergey Reutskiy

https://stackoverflow.com/a/26814496/8474325 での@Neil Lunnの答えは、MongoDb 4.x上向きの場合は素晴らしいです。しかし、彼が集計にObjectIdを使用するコードには小さな間違いがあります。コードを機能させるには、行{ "$toDate": "_id" }{ "$toDate": "$_id" }に変更する必要があります。

これが修正されたコードです。

db.collection.aggregate([
    { "$group": {
      "_id": {
          "$toDate": {
              "$subtract": [
                  { "$toLong": { "$toDate": "$_id" }  },
                  { "$mod": [ { "$toLong": { "$toDate": "$_id" } }, 1000 * 60 * 15 ] }
              ]
          }
      },
      "count": { "$sum": 1 }
   }}
])
1
sanair96