Nejlepší možností je spustit samostatné dotazy pro každou "Země" (ideálně paralelně) a vrátit kombinované výsledky. Dotazy jsou poměrně jednoduché a po použití řazení na hodnotu hodnocení stačí vrátit 2 nejvyšší hodnoty a provedou se poměrně rychle, i když k získání úplného výsledku potřebujete provést více dotazů.
Agregační rámec se k tomu nehodí, a to ani nyní, ani v blízké budoucnosti. Problém je v tom, že neexistuje žádný takový operátor, který by nějakým způsobem „omezoval“ výsledek jakéhokoli seskupování. Takže abyste to mohli udělat, musíte v podstatě $push
veškerý obsah do pole a extrahujte z něj „horních n“ hodnot.
Současné operace, které jsou k tomu zapotřebí, jsou dost hrozné a hlavním problémem je, že výsledky pravděpodobně překročí limit BSON 16 MB na dokument u většiny skutečných zdrojů dat.
Existuje také n
složitost kvůli tomu, jak byste to museli udělat právě teď. Ale jen pro demonstraci pomocí 2 položek:
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"
}
}
}
}
}}
])
To dostane výsledek, ale není to skvělý přístup a je mnohem složitější s iteracemi pro vyšší limity nebo dokonce tam, kde seskupení mají možná méně než n
výsledky se v některých případech vrátí.
Aktuální vývojová řada (3.1.x) má v době psaní $slice
operátor, díky kterému je to o něco jednodušší, ale stále má stejnou "velikost" úskalí:
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 v zásadě dokud nebude mít agregační rámec nějaký způsob, jak „omezit“ počet položek produkovaných $push
nebo podobný operátor „limit“ seskupení, pak agregační rámec není skutečně optimálním řešením pro tento typ problému.
Jednoduché dotazy, jako je tento:
db.collection.find({ "Country": "USA" }).sort({ "rating": -1 }).limit(1)
Běh pro každou jednotlivou zemi a ideálně paralelní zpracování pomocí smyčky událostí vlákna s kombinovaným výsledkem vytváří právě teď nejoptimálnější přístup. Přinesou pouze to, co je potřeba, což je velký problém, který agregační rámec v takovém seskupování zatím nedokáže zvládnout.
Hledejte tedy podporu, abyste mohli tyto „kombinované výsledky dotazů“ provádět nejoptimálnějším způsobem pro vámi zvolený jazyk, protože to bude mnohem méně složité a mnohem výkonnější, než když to házíte do agregačního rámce.