sql >> Databasteknik >  >> NoSQL >> MongoDB

Gruppera efter värderingar och förutsättningar

För att göra någon form av "gruppering" med MongoDB-frågor så vill du kunna använda aggregeringsramverket eller mapReduce. Aggregeringsramverket är i allmänhet att föredra eftersom det använder inbyggda kodade operatorer snarare än JavaScript-översättning, och är därför vanligtvis snabbare.

Aggregationssatser kan bara köras på serverns API-sida, vilket är vettigt eftersom du inte vill göra detta på klienten. Men det kan göras där och göra resultaten tillgängliga för kunden.

Med tillskrivning till detta svar för att tillhandahålla metoderna för att publicera resultat:

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 allmänna aggregeringssatsen där är bara en $group operation på den enda nyckeln för "card_id". För att få "gillar" och "ogillar" använder du ett "villkorligt uttryck" som är $cond .

Detta är en "ternär" operatör som överväger ett logiskt test på värdet av "röst", och där den matchar förväntade typen sedan en positiv 1 returneras, annars är det 0 .

Dessa värden skickas sedan till ackumulatorn som är $sum för att lägga ihop dem och producera det totala antalet för varje "card_id" genom att antingen "gilla" eller "ogilla".

För "total" är det mest effektiva sättet att tillskriva ett "positivt" värde för "gilla" och ett negativt värde för "ogilla" samtidigt som du gör grupperingen. Det finns en $add operatör, men i det här fallet skulle dess användning kräva ytterligare ett steg i pipeline. Så vi gör det bara på en enda scen istället.

I slutet av detta finns en $sort i "fallande" ordning så de största positiva rösterna ligger på topp. Detta är valfritt och du kanske bara vill använda klientsidan för dynamisk sortering. Men det är en bra början för en standard som tar bort omkostnader för att behöva göra det.

Så det är att göra en villkorad aggregering och arbeta med resultaten.

Testlista

Detta är vad jag testade med ett nyskapat meteorprojekt, utan tillägg och bara en enda mall och javascript-fil

konsolkommandon

meteor create cardtest
cd cardtest
meteor remove autopublish

Skapade "kort"-samlingen i databasen med de dokument som lagts upp i frågan. Och redigerade sedan standardfilerna med innehållet nedan:

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>

Slutligt samlat samlingsinnehåll:

[
   {
     "_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. Vad är skillnaden mellan Spring Data MongoDB och Hibernate OGM för MongoDB?

  2. Är det möjligt att få enstaka resultat sammanlagt?

  3. Socket.io, Redis Store och IE

  4. Cloudera Replication Plugin möjliggör x-plattformsreplikering för Apache HBase