Dit kan (nog) het beste worden afgehandeld door meerdere zoekopdrachten, aangezien MongoDB echt "nog" niet over de echt efficiënte operators beschikt om dit nog te doen.
Je kunt echter zoiets doen met MongoDB 3.2, maar er zijn duidelijke "vangsten":
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] }
}}
])
En het voor de hand liggende probleem is dat wat er ook gebeurt, je nog steeds alles . toevoegt van de "url"-inhoud in de gegroepeerde array. Dit heeft het potentieel om de BSON-limiet van 16 MB te overschrijden. Misschien niet, maar het is nog steeds een beetje verspilling om "alle" inhoud toe te voegen als je er maar "drie" van wilt.
Dus zelfs dan is het waarschijnlijk praktischer om de "urls" afzonderlijk op elk van de top 10 resultaten te zoeken.
Hier is een lijst voor node.js die laat zien:
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
}
);
}
)
});
Ja, het zijn meer oproepen naar de database, maar het zijn echt slechts tien en daarom niet echt een probleem.
De echte resolutie hiervoor wordt behandeld in SERVER-9377 - Verleng $push of $max om het verzamelen van "top " N waarden per _id-sleutel in $group-fase . Dit heeft de veelbelovende status 'In uitvoering', dus er wordt actief aan gewerkt.
Als dat eenmaal is opgelost, wordt een enkele aggregatieverklaring levensvatbaar, aangezien je dan de resulterende "urls" in de initiële $push
kunt "beperken" tot slechts drie vermeldingen, in plaats van ze allemaal op drie na te verwijderen.