Това наистина (все още) се обработва най-добре от множество заявки, тъй като MongoDB наистина „все още“ няма наистина ефективните оператори, за да направи това.
Можете обаче да направите нещо подобно с MongoDB 3.2, но има очевидни „уловки“:
db.Books.aggregate([
{ "$group": {
"_id": "$company",
"count": { "$sum": 1 },
"urls": {
"$push": "$url"
}
}},
{ "$sort": { "count": -1 } },
{ "$limit": 10 },
{ "$project": {
"count": 1,
"urls": { "$slice": ["$urls",0, 3] }
}}
])
И очевидният проблем е, че независимо от всичко, вие все още добавяте всички на съдържанието „url“ в групирания масив. Това има потенциал да надхвърли ограничението за BSON от 16MB. Може и да не е, но все пак е малко разточително да добавяте „цялото“ съдържание, когато искате само „три“ от тях.
Така че дори тогава вероятно е по-практично просто да направите заявка за „url адресите“ отделно за всеки от първите 10 резултата.
Ето списък за node.js, който демонстрира:
var async = require('async'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient;
MongoClient.connect("mongodb://localhost/test",function(err,db) {
if (err) throw err;
// Get the top 10
db.collection("Books").aggregate(
[
{ "$group": {
"_id": "$company",
"count": { "$sum": 1 }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 10 }
],function(err,results) {
if (err) throw err;
// Query for each result and map query response as urls
async.map(
results,
function(result,callback) {
db.collection("Books").find({
"company": result.company
}).limit(3).toArray(function(err,items) {
result.urls = items.map(function(item) {
return item.url;
});
callback(err,result);
})
},
function(err,results) {
if (err) throw err;
// each result entry has 3 urls
}
);
}
)
});
Да, има повече обаждания към базата данни, но наистина са само десет и следователно всъщност не е проблем.
Истинската резолюцията за това е описана в SERVER-9377 - Разширете $push или $max, за да позволите събирането на "топ " N стойности на ключ _id в $group фаза . Това има обещаващия статус „В процес на изпълнение“, така че по него се работи активно.
След като това бъде решено, единичен оператор за агрегиране става жизнеспособен, тъй като тогава ще можете да „ограничите“ получените „url адреси“ в началния $push
само до три записа, вместо да премахнете всички освен три след факта.