Det bästa alternativet här är att köra separata frågor för varje "Land" (helst parallellt) och returnera de kombinerade resultaten. Frågorna är ganska enkla och returnerar bara de två översta värdena efter att ha tillämpat en sortering på betygsvärdet och kommer att köras ganska snabbt även om du behöver utföra flera frågor för att få det fullständiga resultatet.
Aggregeringsramverket passar inte bra för detta, nu och även i en nära framtid. Problemet är att det inte finns någon sådan operatör som "begränsar" resultatet av någon gruppering på något sätt. Så för att göra detta behöver du i princip $push
allt innehåll i en array och extrahera "top n"-värdena från det.
De nuvarande operationerna som krävs för att göra det är ganska hemska, och kärnproblemet är att resultaten sannolikt kommer att överskrida BSON-gränsen på 16 MB per dokument på de flesta riktiga datakällor.
Det finns också en n
komplexiteten till detta på grund av hur du skulle behöva göra det just nu. Men bara för att demonstrera med 2 objekt:
db.collection.aggregate([
// Sort content by country and rating
{ "$sort": { "Country": 1, "rating": -1 } },
// Group by country and push all items, keeping first result
{ "$group": {
"_id": "$Country",
"results": {
"$push": {
"name": "$name",
"rating": "$rating",
"id": "$id"
}
},
"first": {
"$first": {
"name": "$name",
"rating": "$rating",
"id": "$id"
}
}
}},
// Unwind the array
{ "$unwind": "results" },
// Remove the seen result from the array
{ "$redact": {
"$cond": {
"if": { "$eq": [ "$results.id", "$first.id" ] },
"then": "$$PRUNE",
"else": "$$KEEP"
}
}},
// Group to return the second result which is now first on stack
{ "$group": {
"_id": "$_id",
"first": { "$first": "$first" },
"second": {
"$first": {
"name": "$results.name",
"rating": "$results.rating",
"id": "$results.id"
}
}
}},
// Optionally put these in an array format
{ "$project": {
"results": {
"$map": {
"input": ["A","B"],
"as": "el",
"in": {
"$cond": {
"if": { "$eq": [ "$$el", "A" ] },
"then": "$first",
"else": "$second"
}
}
}
}
}}
])
Det ger resultatet men det är inte ett bra tillvägagångssätt och blir mycket mer komplicerat med iterationer för högre gränser eller till och med där grupperingar möjligen har mindre än n
resultat att returnera i vissa fall.
Den nuvarande utvecklingsserien ( 3.1.x ) har i skrivande stund en $slice
operator som gör detta lite enklare, men som fortfarande har samma "storlek" fallgrop:
db.collection.aggregate([
// Sort content by country and rating
{ "$sort": { "Country": 1, "rating": -1 } },
// Group by country and push all items, keeping first result
{ "$group": {
"_id": "$Country",
"results": {
"$push": {
"name": "$name",
"rating": "$rating",
"id": "$id"
}
}
}},
{ "$project": {
"results": { "$slice": [ "$results", 2 ] }
}}
])
Men i princip tills aggregeringsramverket har något sätt att "begränsa" antalet objekt som produceras av $push
eller en liknande gruppering "limit"-operatör, så är aggregeringsramverket inte riktigt den optimala lösningen för den här typen av problem.
Enkla frågor som denna:
db.collection.find({ "Country": "USA" }).sort({ "rating": -1 }).limit(1)
Kör för varje distinkt land och idealiskt i parallell bearbetning av händelseslinga med ett kombinerat resultat ger det mest optimala tillvägagångssättet just nu. De hämtar bara det som behövs, vilket är det stora problemet som aggregeringsramverket ännu inte kan hantera i en sådan gruppering.
Så leta efter stöd för att göra dessa "kombinerade frågeresultat" på det mest optimala sättet för ditt valda språk istället, eftersom det kommer att vara mycket mindre komplext och mycket mer prestanda än att kasta detta på aggregeringsramverket.