Най-добрият ви вариант тук е да стартирате отделни заявки за всяка „Държава“ (в идеалния случай паралелно) и да върнете комбинираните резултати. Заявките са доста прости и просто връщат първите 2 стойности след прилагане на сортиране върху стойността на рейтинга и ще се изпълнят доста бързо, дори ако трябва да извършите множество заявки, за да получите пълния резултат.
Рамката за агрегиране не е подходяща за това сега и дори в близко бъдеще. Проблемът е, че няма такъв оператор, който да "ограничава" резултата от всяко групиране по някакъв начин. Така че, за да направите това, вие основно трябва да $push
цялото съдържание в масив и извлечете "топ n" стойностите от него.
Настоящите операции, необходими за извършване на това, са доста ужасни и основният проблем е, че резултатите вероятно ще надхвърлят ограничението на BSON от 16 MB на документ в повечето реални източници на данни.
Също така има n
сложността на това поради начина, по който трябва да го направите точно сега. Но само за демонстрация с 2 елемента:
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"
}
}
}
}
}}
])
Това дава резултата, но не е страхотен подход и става много по-сложен с итерации за по-високи граници или дори когато групировките вероятно имат по-малко от n
резултати, които да се върнат в някои случаи.
Текущата поредица за разработка ( 3.1.x ) към момента на писане има $slice
оператор, който прави това малко по-просто, но все още има същия "размер" капан:
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 ] }
}}
])
Но основно докато рамката за агрегиране има някакъв начин да "ограничи" броя на елементите, произведени от $push
или подобен оператор за "лимит" на групиране, тогава рамката за агрегиране всъщност не е оптималното решение за този тип проблем.
Прости заявки като тази:
db.collection.find({ "Country": "USA" }).sort({ "rating": -1 }).limit(1)
Изпълнение за всяка отделна държава и в идеалния случай при паралелна обработка чрез цикъл на събитие от нишка с комбиниран резултат създава най-оптималния подход в момента. Те извличат само това, което е необходимо, което е големият проблем, с който рамката за агрегиране все още не може да се справи в такова групиране.
Така че вместо това потърсете поддръжка, за да направите тези „комбинирани резултати от заявка“ по най-оптималния начин за избрания от вас език, тъй като ще бъде много по-малко сложно и много по-производително, отколкото да хвърлите това в рамката за агрегиране.