sql >> Databáze >  >> NoSQL >> MongoDB

Seskupení podle hodnot a podmínek

Chcete-li provést jakýkoli druh „seskupení“ s dotazy MongoDB, pak chcete mít možnost použít agregační rámec nebo mapReduce. Obecně je preferován agregační rámec, protože místo překladu JavaScriptu používá nativní kódované operátory, a je proto obvykle rychlejší.

Příkazy agregace lze spouštět pouze na straně API serveru, což dává smysl, protože byste to nechtěli dělat na klientovi. Ale lze to udělat tam a zpřístupnit výsledky klientovi.

S uvedením této odpovědi za poskytnutí metod pro publikování výsledků:

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

});

Obecné prohlášení o agregaci tam je pouze $group operace na jediném klíči "card_id". Abyste získali „líbí se“ a „nelíbí se“, použijte „podmíněný výraz“, který je $cond .

Toto je "ternární" operátor, který bere v úvahu logický test hodnoty "vote" a tam, kde odpovídá typu očekávání, pak kladné 1 je vrácena, jinak je 0 .

Tyto hodnoty jsou poté odeslány do akumulátoru, což je $sum abyste je sečetli a vytvořili celkový počet pro každé „card_id“ buď „to se mi líbí“ nebo „nelíbí“.

Pro „celkem“ je nejúčinnějším způsobem přiřazení „kladné“ hodnoty pro „líbí se“ a záporné hodnoty „nelíbí se“ ve stejnou dobu jako seskupování. Existuje $add operátor, ale v tomto případě by jeho použití vyžadovalo další fázi potrubí. Takže to místo toho děláme na jednom pódiu.

Na konci je $sort v „sestupném“ pořadí, takže největší počet kladných hlasů je nahoře. Toto je volitelné a možná budete chtít použít dynamickou třídicí stranu klienta. Ale je to dobrý začátek pro výchozí nastavení, které odstraňuje režii, kterou byste museli dělat.

Jde tedy o podmíněnou agregaci a práci s výsledky.

Testovací výpis

To je to, co jsem testoval s nově vytvořeným projektem meteor, bez doplňků a pouze s jedinou šablonou a souborem javascript

příkazy konzoly

meteor create cardtest
cd cardtest
meteor remove autopublish

Vytvořil sbírku "karty" v databázi s dokumenty zveřejněnými v otázce. A poté upravil výchozí soubory s obsahem níže:

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>

Konečný agregovaný obsah sbírky:

[
   {
     "_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. Získání více klíčových hodnot z Redis

  2. nemůže zabít redis-server na linuxu

  3. Scrapy webu pomocí Scrapy a MongoDB

  4. Meteor bez monga