MongoDBクエリであらゆる種類の「グループ化」を行うには、集約フレームワークまたはmapReduceを使用できるようにする必要があります。集約フレームワークは、JavaScript変換ではなくネイティブのコード化演算子を使用するため、一般的に好まれます。したがって、通常は高速です。
集計ステートメントはサーバーAPI側でのみ実行できます。これは、クライアントで実行したくないため、意味があります。ただし、そこで実行して、結果をクライアントが利用できるようにすることはできます。
結果を公開する方法を提供するためのこの回答に起因するもの:
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);
}
)
);
});
一般的な集計ステートメントは、 $group
だけです。 「card_id」の単一キーに対する操作。 「いいね」と「嫌い」を取得するには、 $cond
である「条件式」を使用します。 。
これは「投票」の値の論理テストを検討する「三項」演算子であり、期待されるタイプと一致する場合は、正の1
が返されます。それ以外の場合は0
です。 。
これらの値は、 $sum
であるアキュムレータに送信されます。 それらを合計し、「like」または「dislike」のいずれかで各「card_id」の合計カウントを生成します。
「合計」の場合、最も効率的な方法は、グループ化を行うと同時に、「好き」の「正」の値と「嫌い」の負の値を関連付けることです。 $add
があります 演算子ですが、この場合、その使用には別のパイプラインステージが必要になります。そのため、代わりに1つのステージで実行します。
この最後に$sort
があります 「降順」であるため、最大の賛成票数が一番上になります。これはオプションであり、動的並べ替えクライアント側を使用したい場合があります。しかし、それをしなければならないというオーバーヘッドを取り除くデフォルトの良いスタートです。
つまり、条件付き集計を実行し、結果を処理します。
テストリスト
これは、新しく作成されたmeteorプロジェクトでテストしたもので、アドインはなく、テンプレートとjavascriptファイルは1つだけです
コンソールコマンド
meteor create cardtest
cd cardtest
meteor remove autopublish
質問に投稿されたドキュメントを使用して、データベースに「カード」コレクションを作成しました。次に、以下の内容でデフォルトファイルを編集しました:
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>
最終的な集約コレクションコンテンツ:
[
{
"_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
}
]