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

集約フレームワークを使用したグループ化とカウント

    あなたはこれを始めたようですが、他のいくつかの概念に迷いました。ドキュメント内の配列を操作する場合、いくつかの基本的な真実がありますが、中断したところから始めましょう:

    db.sample.aggregate([
        { "$group": {
            "_id": "$status",
            "count": { "$sum": 1 }
        }}
    ])
    

    つまり、 $groupを使用するだけです。 パイプラインを使用して、「ステータス」フィールドのさまざまな値に関するドキュメントを収集し、「カウント」用の別のフィールドを生成します。もちろん、 1> $ sum 見つかった各ドキュメントの演算子。これにより、あなたが説明するのとよく似たポイントになります:

    { "_id" : "done", "count" : 2 }
    { "_id" : "canceled", "count" : 1 }
    

    これはこの最初の段階であり、理解するのは簡単ですが、配列から値を取得する方法を知る必要があります。 「ドット表記」> このようなことを行うための適切な概念:

    db.sample.aggregate([
        { "$group": {
            "_id": "$status",
            "count": { "$sum": 1 },
            "total": { "$sum": "$devices.cost" }
        }}
    ])
    

    しかし、実際には「合計」は 0になります。 これらの結果のそれぞれについて:

    { "_id" : "done", "count" : 2, "total" : 0 }
    { "_id" : "canceled", "count" : 1, "total" : 0 }
    

    なんで?このようなMongoDB集約操作は、グループ化時に実際には配列要素をトラバースしません。そのために、集約フレームワークにはという概念があります。 $ unwind 。名前は比較的自明です。 MongoDBの埋め込み配列は、リンクされたデータソース間に「1対多」の関連付けを行うのとよく似ています。つまり、 $ unwind まさにそのような「結合」結果であり、結果の「ドキュメント」は、配列のコンテンツと各親の重複情報に基づいています。

    したがって、配列要素を操作するには、<を使用する必要があります。 code> $ unwind 最初。これにより、論理的に次のようなコードになります。

    db.sample.aggregate([
        { "$unwind": "$devices" },
        { "$group": {
            "_id": "$status",
            "count": { "$sum": 1 },
            "total": { "$sum": "$devices.cost" }
        }}
    ])
    

    そして結果:

    { "_id" : "done", "count" : 4, "total" : 700 }
    { "_id" : "canceled", "count" : 2, "total" : 350 }
    

    しかし、それは正しくありませんか? $ unwindから学んだことを思い出してください 非正規化された結合はどのようにして親情報と結合しますか?両方に2つの配列メンバーがあったため、これはすべてのドキュメントで複製されます。したがって、「合計」フィールドは正しいものの、「カウント」はそれぞれの場合の2倍になります。

    もう少し注意が必要なので、単一の $ group ステージでは、2つで行われます:

    db.sample.aggregate([
        { "$unwind": "$devices" },
        { "$group": {
            "_id": "$_id",
            "status": { "$first": "$status" },
            "total": { "$sum": "$devices.cost" }
        }},
        { "$group": {
            "_id": "$status",
            "count": { "$sum": 1 },
            "total": { "$sum": "$total" }
        }}
    ])
    

    これで、正しい合計が含まれる結果が得られます:

    { "_id" : "canceled", "count" : 1, "total" : 350 }
    { "_id" : "done", "count" : 2, "total" : 700 }
    

    今では数字は正しいですが、それでもあなたが求めているものとは正確には一致しません。あなたが期待している種類の結果は、集計だけからの単一の結果に実際には適していないので、そこで停止する必要があると思います。結果の「内部」にある合計を探しています。それは実際にはそこに属していませんが、小さなデータでは問題ありません:

    db.sample.aggregate([
        { "$unwind": "$devices" },
        { "$group": {
            "_id": "$_id",
            "status": { "$first": "$status" },
            "total": { "$sum": "$devices.cost" }
        }},
        { "$group": {
            "_id": "$status",
            "count": { "$sum": 1 },
            "total": { "$sum": "$total" }
        }},
        { "$group": {
            "_id": null,
            "data": { "$push": { "count": "$count", "total": "$total" } },
            "totalCost": { "$sum": "$total" }
        }}
    ])
    

    そして最終結果フォーム:

    {
        "_id" : null,
        "data" : [
                {
                        "count" : 1,
                        "total" : 350
                },
                {
                        "count" : 2,
                        "total" : 700
                }
        ],
        "totalCost" : 1050
    }
    

    しかし、「それをしないでください」 。 MongoDBには、BSON仕様の制限である16MBの応答に関するドキュメント制限があります。小さな結果では、この種の便利なラッピングを実行できますが、より大きなスキームでは、結果を以前の形式で個別のクエリにするか、すべてのドキュメントから合計を取得するために結果全体を繰り返し処理する必要があります。

    2.6未満のMongoDBバージョンを使用しているか、最新バージョンの機能をサポートしていないRoboMongoシェルから出力をコピーしているようです。ただし、MongoDB 2.6以降、集計の結果は単一のBSON配列ではなく「カーソル」になる可能性があります。したがって、全体的な応答は16MBをはるかに超える可能性がありますが、最後の例で示したように、結果として単一のドキュメントに圧縮していない場合に限ります。

    これは、結果を「ページング」して数百から数千の結果行を表示しているが、25件の結果の「ページ」のみを返す場合にAPI応答で「合計」を返す場合に特に当てはまります。時間。

    とにかく、それはあなたがあなたの一般的な文書フォームからあなたが期待しているタイプの結果を得る方法についてあなたに合理的なガイドを与えるはずです。 $ unwindを覚えておいてください 配列を処理するために、一般的には $ group ドキュメントとコレクションのグループ化からさまざまなグループ化レベルの合計を取得するために、複数回実行します。




    1. 科学的(指数)表記を返すmongoexport

    2. すべてのホストにmongodbのレプリカセットを許可する方法

    3. findOneは最初または最後に一致しますか?

    4. ループはクエリに基づいてtrueまたはfalseを出力していません