sql >> データベース >  >> NoSQL >> MongoDB

MongoDB:集約フレームワーク:グループ化IDごとに最終日付のドキュメントを取得します

    あなたの質問に直接答えるには、はい、それが最も効率的な方法です。しかし、なぜそうなのかを明確にする必要があると思います。

    別の方法で提案されたように、人々が注目しているのは、 $groupに渡す前に結果を「並べ替える」ことです。 ステージと彼らが見ているのは「タイムスタンプ」値​​であるため、すべてが「タイムスタンプ」の順序になっていることを確認する必要があります。したがって、次の形式になります。

    db.temperature.aggregate([
        { "$sort": { "station": 1, "dt": -1 } },
        { "$group": {
            "_id": "$station", 
            "result": { "$first":"$dt"}, "t": {"$first":"$t"} 
        }}
    ])
    

    そして、前述のように、ソートを効率的にするために、もちろんインデックスにそれを反映させる必要があります。

    しかし、これが本当のポイントです。他の人が見落としているように思われるのは(自分自身がそうでない場合)、このデータはすべてすでに挿入されている可能性が高いということです。 時間順に、各読み取り値が追加として記録されます。

    つまり、これの美しさは_idです。 フィールド(デフォルトのObjectId )はすでに「タイムスタンプ」の順序になっています。これは、実際には時間値が含まれているため、ステートメントが可能になるためです。

    db.temperature.aggregate([
        { "$group": {
            "_id": "$station", 
            "result": { "$last":"$dt"}, "t": {"$last":"$t"} 
        }}
    ])
    

    そしてそれはです もっと早く。なんで?インデックス(呼び出す追加のコード)を選択する必要はありません。また、ドキュメントに加えてインデックスを「ロード」する必要もありません。

    ドキュメントが正常であることはすでにわかっています(_idによる) )したがって、 $last 境界は完全に有効です。とにかくすべてをスキャンしています。また、_idでクエリを「範囲」にすることもできます。 2つの日付の間で同等に有効な値。

    ここで言う唯一の本当のことは、「現実の世界」の使用法では、 $matchを使用する方が実用的かもしれないということです。 「最初」と「最後」の_idを取得するのではなく、この種の累積を行う場合の日付の範囲の間 「範囲」または実際の使用法に類似したものを定義するための値。

    では、これの証拠はどこにありますか?再現はかなり簡単なので、サンプルデータを生成するだけで再現できました:

    var stations = [ 
        "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL",
        "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA",
        "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE",
        "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK",
        "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT",
        "VA", "WA", "WV", "WI", "WY"
    ];
    
    
    for ( i=0; i<200000; i++ ) {
    
        var station = stations[Math.floor(Math.random()*stations.length)];
        var t = Math.floor(Math.random() * ( 96 - 50 + 1 )) +50;
        dt = new Date();
    
        db.temperatures.insert({
            station: station,
            t: t,
            dt: dt
        });
    
    }
    

    私のハードウェア(スピニーディスクを備えた8GBラップトップ、これは恒星ではありませんが、確かに適切です)で、ステートメントの各形式を実行すると、インデックスとソート(ソートステートメントと同じインデックス上のキー)を使用したバージョンで顕著な一時停止が明確に示されます。ほんの少しの一時停止ですが、違いは気付くほど重要です。

    Explainの出力(バージョン2.6以降、または実際には2.4.9にありますが、文書化されていません)を見ても、その違いを確認できますが、 $sort インデックスが存在するために最適化されており、インデックスを選択してからインデックス付きエントリをロードするのにかかる時間は表示されます。 「対象」のすべてのフィールドを含める インデックスクエリは違いはありません。

    また、レコードの場合、純粋に日付にインデックスを付け、日付値で並べ替えるだけでも同じ結果が得られます。おそらくわずかに高速ですが、並べ替えなしの自然なインデックス形式よりも低速です。

    最初ので楽しく「範囲を広げる」ことができる限り および最後 _id 値の場合、挿入順序で自然インデックスを使用することが実際にこれを行う最も効率的な方法であることは事実です。実際のマイレージは、これが実用的かどうかによって異なる場合があり、インデックスを実装して日付に並べ替える方が便利な場合があります。

    ただし、_idの使用に満足している場合 「最後の」_id以上の範囲 クエリで、結果と一緒に値を取得するために、おそらく1つの調整を行って、実際にその情報を保存して、後続のクエリで使用できるようにします。

    db.temperature.aggregate([
        // Get documents "greater than" the "highest" _id value found last time
        { "$match": {
            "_id": { "$gt":  ObjectId("536076603e70a99790b7845d") }
        }},
    
        // Do the grouping with addition of the returned field
        { "$group": {
            "_id": "$station", 
            "result": { "$last":"$dt"},
            "t": {"$last":"$t"},
            "lastDoc": { "$last": "$_id" } 
        }}
    ])
    

    そして、実際にそのような結果を「フォロー」している場合は、ObjectIdの最大値を決定できます。 結果から、次のクエリで使用します。

    とにかく、それで遊んで楽しんでください、しかし再びはい、この場合、そのクエリは最速の方法です。



    1. 複数の基準に一致するドキュメントを見つける方法

    2. StackExchange.RedisとAzureRedisは、使用できないほど遅いか、タイムアウトエラーをスローします

    3. マングースを介してアイテムをマングースアレイにプッシュします

    4. redisサブスクリプションの出力をリダイレクトする方法