Pilihan terbaik Anda di sini adalah menjalankan kueri terpisah untuk setiap "Negara" (idealnya secara paralel) dan mengembalikan hasil gabungan. Kuerinya cukup sederhana, dan hanya mengembalikan 2 nilai teratas setelah menerapkan pengurutan pada nilai peringkat dan akan dijalankan dengan cukup cepat bahkan jika Anda perlu melakukan beberapa kueri untuk mendapatkan hasil yang lengkap.
Kerangka agregasi tidak cocok untuk ini, sekarang dan bahkan dalam waktu dekat. Masalahnya adalah tidak ada operator yang "membatasi" hasil pengelompokan apa pun dengan cara apa pun. Jadi untuk melakukan ini, pada dasarnya Anda perlu $push
semua konten ke dalam array dan ekstrak nilai "top n" dari itu.
Operasi saat ini yang diperlukan untuk melakukan itu cukup mengerikan, dan masalah intinya adalah hasil cenderung melebihi batas BSON 16MB per dokumen pada sebagian besar sumber data nyata.
Juga ada n
kompleksitas ini karena bagaimana Anda harus melakukannya sekarang. Tapi hanya untuk mendemonstrasikan dengan 2 item:
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"
}
}
}
}
}}
])
Itu mendapatkan hasil tetapi ini bukan pendekatan yang bagus dan menjadi jauh lebih kompleks dengan iterasi untuk batas yang lebih tinggi atau bahkan di mana pengelompokan mungkin kurang dari n
hasil untuk kembali dalam beberapa kasus.
Seri pengembangan saat ini ( 3.1.x ) pada saat penulisan memiliki $slice
operator yang membuat ini sedikit lebih sederhana, tetapi masih memiliki "ukuran" perangkap yang sama:
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 ] }
}}
])
Tetapi pada dasarnya sampai kerangka agregasi memiliki beberapa cara untuk "membatasi" jumlah item yang dihasilkan oleh $push
atau operator "batas" pengelompokan serupa, maka kerangka kerja agregasi bukanlah solusi optimal untuk jenis masalah ini.
Kueri sederhana seperti ini:
db.collection.find({ "Country": "USA" }).sort({ "rating": -1 }).limit(1)
Jalankan untuk setiap negara yang berbeda dan idealnya dalam pemrosesan paralel dengan loop peristiwa dengan hasil gabungan menghasilkan pendekatan paling optimal saat ini. Mereka hanya mengambil apa yang dibutuhkan, yang merupakan masalah besar yang belum dapat ditangani oleh kerangka kerja agregasi dalam pengelompokan tersebut.
Jadi, carilah dukungan untuk melakukan "hasil kueri gabungan" ini dengan cara yang paling optimal untuk bahasa pilihan Anda, karena ini akan jauh lebih kompleks dan lebih berperforma daripada melemparkannya ke kerangka kerja agregasi.