Najlepszą opcją jest tutaj uruchomienie oddzielnych zapytań dla każdego „Kraju” (najlepiej równolegle ) i zwrócenie połączonych wyników. Zapytania są dość proste i po prostu zwracają 2 najwyższe wartości po zastosowaniu sortowania według wartości oceny i będą wykonywane dość szybko, nawet jeśli musisz wykonać wiele zapytań, aby uzyskać pełny wynik.
Ramy agregacji nie są do tego odpowiednie, teraz, a nawet w najbliższej przyszłości. Problem w tym, że nie ma takiego operatora, który w jakikolwiek sposób „ogranicza” wynik dowolnego grupowania. Aby to zrobić, musisz w zasadzie $push
całą zawartość do tablicy i wyodrębnij z niej „n górnych” wartości.
Obecne operacje potrzebne do tego są dość okropne, a głównym problemem jest to, że wyniki prawdopodobnie przekroczą limit 16 MB na dokument w przypadku większości rzeczywistych źródeł danych.
Istnieje również n
złożoność tego ze względu na to, jak musiałbyś to zrobić teraz. Ale wystarczy zademonstrować z 2 przedmiotami:
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"
}
}
}
}
}}
])
Daje to wynik, ale nie jest to świetne podejście i staje się znacznie bardziej złożone z iteracjami dla wyższych limitów lub nawet tam, gdzie grupowanie ma prawdopodobnie mniej niż n
wyniki zwracane w niektórych przypadkach.
Obecna seria rozwojowa ( 3.1.x ) od momentu pisania ma $slice
operator, który czyni to nieco prostszym, ale nadal ma tę samą pułapkę „rozmiaru”:
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 ] }
}}
])
Ale w zasadzie dopóki struktura agregacji nie będzie miała sposobu na „ograniczenie” liczby elementów wytwarzanych przez $push
lub podobny operator „limit” grupowania, wtedy struktura agregacji nie jest tak naprawdę optymalnym rozwiązaniem dla tego typu problemu.
Proste zapytania, takie jak:
db.collection.find({ "Country": "USA" }).sort({ "rating": -1 }).limit(1)
Uruchom dla każdego odrębnego kraju i najlepiej w równoległym przetwarzaniu przez pętlę zdarzeń wątku z połączonym wynikiem, daje teraz najbardziej optymalne podejście. Pobierają tylko to, co jest potrzebne, co jest dużym problemem, z którym struktura agregacji nie może jeszcze poradzić sobie w takim grupowaniu.
Dlatego poszukaj wsparcia, aby wykonać te „połączone wyniki zapytań” w najbardziej optymalny sposób dla wybranego języka, ponieważ będzie to znacznie mniej złożone i znacznie bardziej wydajne niż rzucanie tego w ramach agregacji.