sql >> Database teknologi >  >> NoSQL >> MongoDB

Gruppér efter værdier og forhold

For at lave nogen form for "gruppering" med MongoDB-forespørgsler, vil du være i stand til at bruge aggregeringsrammen eller mapReduce. Aggregeringsrammen foretrækkes generelt, da den bruger indbyggede kodede operatorer frem for JavaScript-oversættelse, og den er derfor typisk hurtigere.

Aggregationssætninger kan kun køres på server API-siden, hvilket giver mening, fordi du ikke ønsker at gøre dette på klienten. Men det kan gøres der og gøre resultaterne tilgængelige for klienten.

Med tilskrivning til dette svar for at give metoderne til at publicere resultater:

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);
            }
        )
    );

});

Den generelle aggregeringssætning er bare en $group operation på den enkelte nøgle af "card_id". For at få "synes godt om" og "dislikes" bruger du et "betinget udtryk", som er $cond .

Dette er en "ternær" operatør, som overvejer en logisk test på værdien af ​​"stemme", og hvor den matcher forventningstypen, derefter en positiv 1 returneres, ellers er det 0 .

Disse værdier sendes derefter til akkumulatoren, som er $sum for at lægge dem sammen og producere det samlede antal for hvert "card_id" ved enten "like" eller "dislike".

For "total" er den mest effektive måde at tilskrive en "positiv" værdi for "synes godt om" og en negativ værdi for "dislike" på samme tid som grupperingen. Der er en $add operatør, men i dette tilfælde ville dets brug kræve et andet pipelinetrin. Så vi gør det bare på en enkelt scene i stedet for.

I slutningen af ​​dette er der en $sort i "faldende" rækkefølge, så de største positive stemmetal er i top. Dette er valgfrit, og du vil måske bare bruge klientsiden med dynamisk sortering. Men det er en god start for en standard, der fjerner omkostningerne ved at skulle gøre det.

Så det er at lave en betinget aggregering og arbejde med resultaterne.

Test fortegnelse

Dette er, hvad jeg testede med et nyoprettet meteorprojekt, uden tilføjelser og kun en enkelt skabelon og javascript-fil

konsolkommandoer

meteor create cardtest
cd cardtest
meteor remove autopublish

Oprettet "kort"-samlingen i databasen med dokumenterne i spørgsmålet. Og derefter redigerede standardfilerne med indholdet nedenfor:

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>

Endelig aggregeret samlingsindhold:

[
   {
     "_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. MongoDB gruppe og sum med id som nøgle

  2. Apache Phoenix til CDH

  3. Arbejder du med indlejrede objekter i Redis?

  4. MongoDB vs MySQL