Jika Anda perlu menghitung sesuatu seperti ini saat runtime, dengan konten "difilter" dari array yang menentukan urutan pengurutan, maka Anda sebaiknya melakukan sesuatu dengan .aggregate()
untuk membentuk kembali dan menentukan nilai sortir seperti ini:
db.collection.aggregate([
// Pre-filter the array elements
{ "$project": {
"tags": 1,
"score": {
"$setDifference": [
{ "$map": {
"input": "$tags",
"as": "tag",
"in": {
"$cond": [
{ "$eq": [ "$$el.id", "t1" ] },
"$$el.score",
false
]
}
}},
[false]
]
}
}},
// Unwind to denormalize
{ "$unwind": "$score" },
// Group back the "max" score
{ "$group": {
"_id": "$_id",
"tags": { "$first": "$tags" },
"score": { "$max": "$score" }
}},
// Sort descending by score
{ "$sort": { "score": -1 } }
])
Di mana bagian pertama dari pipa digunakan untuk "memfilter" konten array (serta menjaga bidang asli) hanya untuk nilai "skor" di mana id sama dengan "t1". Ini dilakukan dengan memproses $map
yang menerapkan ketentuan untuk setiap elemen melalui $cond
untuk menentukan apakah akan mengembalikan "skor" untuk elemen tersebut atau false
.
$setDifference
operasi melakukan perbandingan dengan larik elemen tunggal [false]
yang secara efektif menghapus false
nilai yang dikembalikan dari $map
. Sebagai "set", ini juga menghapus entri duplikat, tetapi untuk tujuan pengurutan di sini ini adalah hal yang baik.
Dengan array dikurangi dan dibentuk kembali ke nilai yang Anda proses $unwind
siap untuk tahap selanjutnya untuk menangani nilai-nilai sebagai elemen individu. $group
stage pada dasarnya berlaku $max
pada "skor" untuk mengembalikan nilai tertinggi yang terkandung dalam hasil yang difilter.
Maka tinggal menerapkan $sort
pada nilai yang ditentukan untuk memesan dokumen. Tentu saja jika Anda menginginkan ini sebaliknya, gunakan $min
dan urutkan dalam urutan menaik.
Tentu saja tambahkan $match
tahap ke awal jika yang Anda inginkan hanyalah dokumen yang benar-benar berisi nilai "t1" untuk id
dalam tag. Tetapi bagian itu paling tidak relevan dengan penyortiran pada hasil yang difilter yang ingin Anda capai.
Alternatif untuk menghitung adalah melakukan semuanya saat Anda menulis entri ke larik di dokumen. Agak berantakan, tapi kira-kira seperti ini:
db.collection.update(
{ "_id": docId },
{
"$push": { "tags": { "id": "t1", "score": 60 } },
"$max": { "maxt1score": 60 },
"$min": { "mint1score": 60 }
}
)
Berikut $max
operator pembaruan hanya menetapkan nilai untuk bidang yang ditentukan jika nilai baru lebih besar dari nilai yang ada atau jika tidak, properti belum ada. Kasus sebaliknya berlaku untuk $min
, dimana hanya jika kurang dari itu akan diganti dengan nilai baru.
Ini tentu saja akan berdampak pada penambahan berbagai properti tambahan ke dokumen, tetapi hasil akhirnya adalah penyortiran yang sangat disederhanakan:
db.collection.find().sort({ "maxt1score": -1 })
Dan itu akan berjalan jauh lebih cepat daripada menghitung dengan pipeline agregasi.
Jadi pertimbangkan prinsip-prinsip desain. Data terstruktur dalam larik di mana Anda ingin menyaring dan memasangkan hasil untuk pengurutan berarti menghitung saat run-time untuk menentukan nilai mana yang akan diurutkan. Menambahkan properti tambahan ke dokumen di .update()
berarti Anda cukup mereferensikan properti tersebut untuk menyortir hasil secara langsung.