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

値と条件でグループ化

    MongoDBクエリであらゆる種類の「グループ化」を行うには、集約フレームワークまたはmapReduceを使用できるようにする必要があります。集約フレームワークは、JavaScript変換ではなくネイティブのコード化演算子を使用するため、一般的に好まれます。したがって、通常は高速です。

    集計ステートメントはサーバーAPI側でのみ実行できます。これは、クライアントで実行したくないため、意味があります。ただし、そこで実行して、結果をクライアントが利用できるようにすることはできます。

    結果を公開する方法を提供するためのこの回答に起因するもの:

    Meteor.publish("cardLikesDislikes", function(args) {
        var sub = this;
    
        var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
    
        var pipeline = [
            { "$group": {
                "_id": "$card_id",
                "likes": {
                    "$sum": {
                        "$cond": [
                            { "$eq": [ "$vote", 1 ] },
                            1,
                            0
                        ]
                    }
                },
                "dislikes": {
                    "$sum": {
                        "$cond": [
                            { "$eq": [ "$vote", 2 ] },
                            1,
                            0
                        ]
                    }
                },
                "total": {
                    "$sum": {
                        "$cond": [
                            { "$eq": [ "$vote", 1 ] },
                            1,
                            -1
                        ]
                    }
                }
            }},
            { "$sort": { "total": -1 } }
        ];
    
        db.collection("server_collection_name").aggregate(        
            pipeline,
            // Need to wrap the callback so it gets called in a Fiber.
            Meteor.bindEnvironment(
                function(err, result) {
                    // Add each of the results to the subscription.
                    _.each(result, function(e) {
                        // Generate a random disposable id for aggregated documents
                        sub.added("client_collection_name", Random.id(), {
                            card: e._id,                        
                            likes: e.likes,
                            dislikes: e.dislikes,
                            total: e.total
                        });
                    });
                    sub.ready();
                },
                function(error) {
                    Meteor._debug( "Error doing aggregation: " + error);
                }
            )
        );
    
    });
    

    一般的な集計ステートメントは、 $groupだけです。 「card_id」の単一キーに対する操作。 「いいね」と「嫌い」を取得するには、 $condである「条件式」を使用します。

    これは「投票」の値の論理テストを検討する「三項」演算子であり、期待されるタイプと一致する場合は、正の1 が返されます。それ以外の場合は0です。 。

    これらの値は、 $sumであるアキュムレータに送信されます。 それらを合計し、「like」または「dislike」のいずれかで各「card_id」の合計カウントを生成します。

    「合計」の場合、最も効率的な方法は、グループ化を行うと同時に、「好き」の「正」の値と「嫌い」の負の値を関連付けることです。 $addがあります 演算子ですが、この場合、その使用には別のパイプラインステージが必要になります。そのため、代わりに1つのステージで実行します。

    この最後に$sortがあります 「降順」であるため、最大の賛成票数が一番上になります。これはオプションであり、動的並べ替えクライアント側を使用したい場合があります。しかし、それをしなければならないというオーバーヘッドを取り除くデフォルトの良いスタートです。

    つまり、条件付き集計を実行し、結果を処理します。

    テストリスト

    これは、新しく作成されたmeteorプロジェクトでテストしたもので、アドインはなく、テンプレートとjavascriptファイルは1つだけです

    コンソールコマンド

    meteor create cardtest
    cd cardtest
    meteor remove autopublish
    

    質問に投稿されたドキュメントを使用して、データベースに「カード」コレクションを作成しました。次に、以下の内容でデフォルトファイルを編集しました:

    cardtest.js

    Cards = new Meteor.Collection("cardStore");
    
    if (Meteor.isClient) {
    
      Meteor.subscribe("cards");
    
      Template.body.helpers({
        cards: function() {
          return Cards.find({});
        }
      });
    
    }
    
    if (Meteor.isServer) {
    
      Meteor.publish("cards",function(args) {
        var sub = this;
    
        var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
    
        var pipeline = [
          { "$group": {
            "_id": "$card_id",
            "likes": { "$sum": { "$cond": [{ "$eq": [ "$vote", 1 ] },1,0] } },
            "dislikes": { "$sum": { "$cond": [{ "$eq": [ "$vote", 2 ] },1,0] } },
            "total": { "$sum": { "$cond": [{ "$eq": [ "$vote", 1 ] },1,-1] } }
          }},
          { "$sort": { "total": -1, "_id": 1 } }
    
        ];
    
        db.collection("cards").aggregate(
          pipeline,
          Meteor.bindEnvironment(
            function(err,result) {
              _.each(result,function(e) {
                e.card_id = e._id;
                delete e._id;
    
                sub.added("cardStore",Random.id(), e);
              });
              sub.ready();
            },
            function(error) {
              Meteor._debug( "error running: " + error);
            }
          )
        );
    
      });
    }
    

    cardtest.html

    <head>
      <title>cardtest</title>
    </head>
    
    <body>
      <h1>Card aggregation</h1>
    
      <table border="1">
        <tr>
          <th>Card_id</th>
          <th>Likes</th>
          <th>Dislikes</th>
          <th>Total</th>
        </tr>
        {{#each cards}}
          {{> card }}
        {{/each}}
      </table>
    
    </body>
    
    <template name="card">
      <tr>
        <td>{{card_id}}</td>
        <td>{{likes}}</td>
        <td>{{dislikes}}</td>
        <td>{{total}}</td>
      </tr>
    </template>
    

    最終的な集約コレクションコンテンツ:

    [
       {
         "_id":"Z9cg2p2vQExmCRLoM",
         "likes":3,
         "dislikes":1,
         "total":2,
         "card_id":1
       },
       {
         "_id":"KQWCS8pHHYEbiwzBA",
          "likes":2,
          "dislikes":0,
          "total":2,
          "card_id":2
       },
       {
          "_id":"KbGnfh3Lqcmjow3WN",
          "likes":1,
          "dislikes":0,
          "total":1,
          "card_id":3
       }
    ]
    


    1. NoSQLのユースケース

    2. MongoDBで大文字と小文字を区別しないインデックスを作成する方法

    3. Redisでの整数と文字列の保存の違い

    4. 2.0ドライバーで同等のMongoServer.State