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
}
]