Để thực hiện bất kỳ loại "nhóm" nào với các truy vấn MongoDB thì bạn muốn có thể sử dụng khung tổng hợp hoặc mapReduce. Khuôn khổ tổng hợp thường được ưa thích hơn vì nó sử dụng các toán tử được mã hóa gốc thay vì dịch JavaScript và do đó thường nhanh hơn.
Các câu lệnh tổng hợp chỉ có thể chạy ở phía API máy chủ, điều này có ý nghĩa vì bạn không muốn thực hiện việc này trên máy khách. Nhưng nó có thể được thực hiện ở đó và cung cấp kết quả cho khách hàng.
Với sự ghi nhận câu trả lời này để cung cấp các phương pháp xuất bản kết quả:
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);
}
)
);
});
Câu lệnh tổng hợp chung chỉ có một $group
hoạt động trên một khóa duy nhất của "card_id". Để nhận được "lượt thích" và "lượt không thích", bạn sử dụng "biểu thức điều kiện" là $cond
.
Đây là một toán tử "bậc ba" xem xét một bài kiểm tra logic trên giá trị của "phiếu bầu" và trong đó nó khớp với loại mong đợi sau đó là 1
dương được trả về, nếu không thì nó là 0
.
Sau đó, các giá trị đó được gửi đến bộ tích lũy là $sum
để cộng chúng lại với nhau và tạo ra tổng số cho mỗi "card_id" bằng "thích" hoặc "không thích".
Đối với "tổng", cách hiệu quả nhất là gán giá trị "dương" cho "thích" và giá trị âm cho "không thích" cùng lúc khi thực hiện nhóm. Có một $add
nhưng trong trường hợp này, việc sử dụng nó sẽ yêu cầu một giai đoạn đường ống khác. Vì vậy, chúng tôi chỉ làm điều đó trên một giai đoạn duy nhất.
Ở cuối phần này, có một $sort
theo thứ tự "giảm dần" để số phiếu tích cực lớn nhất nằm trên cùng. Đây là tùy chọn và bạn có thể chỉ muốn sử dụng phía máy khách sắp xếp động. Nhưng đó là một khởi đầu tốt cho một mặc định loại bỏ chi phí phải làm điều đó.
Vì vậy, đó là thực hiện một tổng hợp có điều kiện và làm việc với các kết quả.
Danh sách thử nghiệm
Đây là những gì tôi đã thử nghiệm với một dự án sao băng mới được tạo, không có bổ trợ và chỉ một mẫu và tệp javascript
lệnh bảng điều khiển
meteor create cardtest
cd cardtest
meteor remove autopublish
Đã tạo bộ sưu tập "thẻ" trong cơ sở dữ liệu với các tài liệu được đăng trong câu hỏi. Và sau đó chỉnh sửa các tệp mặc định với nội dung bên dưới:
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>
Nội dung bộ sưu tập tổng hợp cuối cùng:
[
{
"_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
}
]