sql >> Database >  >> NoSQL >> MongoDB

Groeperen op waarden en voorwaarden

Om enige vorm van "groepering" met MongoDB-query's uit te voeren, wilt u het aggregatieraamwerk of mapReduce kunnen gebruiken. Het aggregatieraamwerk heeft over het algemeen de voorkeur omdat het native gecodeerde operators gebruikt in plaats van JavaScript-vertaling, en daarom meestal sneller is.

Aggregatie-instructies kunnen alleen aan de server-API-kant worden uitgevoerd, wat logisch is omdat u dit niet op de client wilt doen. Maar het kan daar worden gedaan en de resultaten beschikbaar stellen aan de klant.

Met toeschrijving aan dit antwoord voor het verstrekken van de methoden om resultaten te publiceren:

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

});

De algemene aggregatieverklaring is er slechts een $group bewerking op de enkele sleutel van "card_id". Om de "vind-ik-leuks" en "niet leuk" te krijgen, gebruikt u een "voorwaardelijke uitdrukking" die $cond is .

Dit is een "ternaire" operator die een logische test op de waarde van "vote" beschouwt, en waar deze overeenkomt met het type verwachting, dan een positieve 1 wordt geretourneerd, anders is het 0 .

Die waarden worden vervolgens naar de accumulator gestuurd die $sum . is om ze bij elkaar op te tellen en de totale tellingen voor elke "card_id" te produceren door middel van "vind ik leuk" of "niet leuk".

Voor het "totaal" is de meest efficiënte manier om een ​​"positieve" waarde toe te kennen aan "vind ik leuk" en een negatieve waarde voor "niet leuk" terwijl u de groepering uitvoert. Er is een $add operator, maar in dit geval zou het gebruik ervan een andere pijplijnfase vereisen. Dus we doen het gewoon op een enkel podium.

Aan het einde hiervan staat een $sort in "aflopende" volgorde zodat het grootste aantal positieve stemmen bovenaan staat. Dit is optioneel en misschien wilt u de client-side dynamisch sorteren gebruiken. Maar het is een goed begin voor een standaard die de overhead verwijdert om dat te moeten doen.

Dus dat is een voorwaardelijke aggregatie doen en werken met de resultaten.

Testlijst

Dit is wat ik heb getest met een nieuw gemaakt meteorenproject, zonder add-ins en slechts een enkele sjabloon en javascript-bestand

console-opdrachten

meteor create cardtest
cd cardtest
meteor remove autopublish

De verzameling "kaarten" in de database gemaakt met de documenten die in de vraag zijn geplaatst. En bewerkte vervolgens de standaardbestanden met de onderstaande inhoud:

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>

Definitieve geaggregeerde collectie-inhoud:

[
   {
     "_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. SCAN vs KEYS-prestaties in Redis

  2. Maak kennis met de Redis-database:itereren over sleutels

  3. Redis installeren op CentOS 8

  4. Hoe redis-server draaiende te houden?