Apa yang Anda lewatkan di sini adalah $lookup
menghasilkan "array" di bidang keluaran yang ditentukan oleh as
dalam argumennya. Ini adalah konsep umum "hubungan" MongoDB, di mana "hubungan" antara dokumen direpresentasikan sebagai "sub-properti" yang "di dalam" dokumen itu sendiri, baik tunggal atau "array" bagi banyak orang.
Karena MongoDB "tanpa skema", anggapan umum $lookup
adalah yang Anda maksud "banyak" dan hasilnya adalah "selalu" sebuah array. Jadi mencari "hasil yang sama seperti di SQL" maka Anda perlu $unwind
array itu setelah $lookup
. Apakah itu "satu" atau "banyak" tidak ada artinya, karena masih "selalu" sebuah array:
db.getCollection.('tb1').aggregate([
// Filter conditions from the source collection
{ "$match": { "status": { "$ne": "closed" } }},
// Do the first join
{ "$lookup": {
"from": "tb2",
"localField": "id",
"foreignField": "profileId",
"as": "tb2"
}},
// $unwind the array to denormalize
{ "$unwind": "$tb2" },
// Then match on the condtion for tb2
{ "$match": { "tb2.profile_type": "agent" } },
// join the second additional collection
{ "$lookup": {
"from": "tb3",
"localField": "tb2.id",
"foreignField": "id",
"as": "tb3"
}},
// $unwind again to de-normalize
{ "$unwind": "$tb3" },
// Now filter the condition on tb3
{ "$match": { "tb3.status": 0 } },
// Project only wanted fields. In this case, exclude "tb2"
{ "$project": { "tb2": 0 } }
])
Di sini Anda perlu mencatat hal-hal lain yang Anda lewatkan dalam terjemahan:
Urutan itu "penting"
Pipa agregasi lebih "sangat ekspresif" daripada SQL. Mereka sebenarnya paling baik dianggap sebagai "urutan langkah" diterapkan ke sumber data untuk menyusun dan mengubah data. Analog terbaik untuk ini adalah instruksi baris perintah "piped", seperti:
ps -ef | grep mongod | grep -v grep | awk '{ print $1 }'
Dimana "pipa" |
dapat dianggap sebagai "tahap pipa" dalam "pipa" agregasi MongoDB.
Karena itu kami ingin $match
untuk memfilter sesuatu dari koleksi "sumber" sebagai operasi pertama kami. Dan ini umumnya merupakan praktik yang baik karena menghapus dokumen apa pun yang tidak memenuhi persyaratan yang disyaratkan dari kondisi lebih lanjut. Sama seperti apa yang terjadi dalam contoh "pipa baris perintah" kami, di mana kami mengambil "input" lalu "pipa" ke grep
untuk "menghapus" atau "menyaring".
Jalur Penting
Di mana hal berikutnya yang Anda lakukan di sini adalah "bergabung" melalui $lookup
. Hasilnya adalah "array" item dari "from"
argumen koleksi dicocokkan dengan bidang yang disediakan untuk keluaran di "as"
"jalur bidang" sebagai "array".
Penamaan yang dipilih di sini penting, karena sekarang "dokumen" dari koleksi sumber menganggap semua item dari "gabung" sekarang ada di jalur yang diberikan. Untuk mempermudah ini, saya menggunakan nama "koleksi" yang sama dengan "bergabung" untuk "jalur" baru.
Jadi mulai dari "join" pertama hasilnya adalah "tb2"
dan itu akan menampung semua hasil dari koleksi itu. Ada juga hal penting yang perlu diperhatikan dengan urutan berikut $unwind
lalu $match
, tentang bagaimana sebenarnya MongoDB memproses kueri.
Urutan tertentu "benar-benar" penting
Karena "sepertinya" ada "tiga" tahapan saluran, menjadi $lookup
lalu $unwind
lalu $match
. Tetapi pada kenyataannya, MongoDB benar-benar melakukan sesuatu yang lain, yang ditunjukkan dalam output { "explain": true }
ditambahkan ke .aggregate()
perintah:
{
"$lookup" : {
"from" : "tb2",
"as" : "tb2",
"localField" : "id",
"foreignField" : "profileId",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"profile_type" : {
"$eq" : "agent"
}
}
}
},
{
"$lookup" : {
"from" : "tb3",
"as" : "tb3",
"localField" : "tb2.id",
"foreignField" : "id",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"status" : {
"$eq" : 0.0
}
}
}
},
Jadi selain dari poin pertama "urutan" penerapan di mana Anda harus meletakkan $match
pernyataan di mana mereka dibutuhkan dan melakukan yang "paling baik", ini sebenarnya menjadi "sangat penting" dengan konsep "bergabung". Hal yang perlu diperhatikan di sini adalah urutan $lookup
lalu $unwind
lalu $match
, sebenarnya diproses oleh MongoDB hanya sebagai $lookup
tahap, dengan operasi lain "digabungkan" ke dalam satu tahap saluran untuk masing-masing.
Ini adalah perbedaan penting untuk cara lain "memfilter" hasil yang diperoleh dengan $lookup
. Karena dalam kasus ini, kondisi "query" yang sebenarnya pada "join" dari $match
dilakukan pada koleksi untuk bergabung "sebelum" hasilnya dikembalikan ke induk.
Ini dikombinasikan dengan $unwind
( yang diterjemahkan menjadi unwinding
) seperti yang ditunjukkan di atas adalah bagaimana MongoDB benar-benar menangani kemungkinan bahwa "bergabung" dapat mengakibatkan menghasilkan larik konten dalam dokumen sumber yang menyebabkannya melebihi batas BSON 16MB. Ini hanya akan terjadi dalam kasus di mana hasil yang digabungkan sangat besar, tetapi keuntungan yang sama adalah di mana "filter" benar-benar diterapkan, berada di kumpulan target "sebelum" hasil dikembalikan.
Ini adalah jenis penanganan yang paling "berkorelasi" dengan perilaku yang sama dengan SQL JOIN. Oleh karena itu, ini juga merupakan cara paling efektif untuk mendapatkan hasil dari $lookup
di mana ada kondisi lain untuk diterapkan pada GABUNG selain dari nilai kunci "lokal" dari nilai kunci "asing".
Perhatikan juga bahwa perubahan perilaku lainnya adalah dari apa yang pada dasarnya adalah LEFT JOIN yang dilakukan oleh $lookup
di mana dokumen "sumber" akan selalu disimpan terlepas dari keberadaan dokumen yang cocok dalam koleksi "target". Alih-alih $unwind
menambahkan ini dengan "membuang" hasil apa pun dari "sumber" yang tidak memiliki apa pun yang cocok dari "target" dengan ketentuan tambahan di $match
.
Bahkan mereka bahkan dibuang sebelumnya karena tersirat
preserveNullAndEmptyArrays: false
yang disertakan dan akan membuang apa pun di mana kunci "lokal" dan "asing" bahkan tidak cocok di antara dua koleksi. Ini adalah hal yang baik untuk jenis kueri khusus ini karena "gabung" dimaksudkan untuk "sama" pada nilai-nilai tersebut.
Simpulkan
Seperti disebutkan sebelumnya, MongoDB umumnya memperlakukan "hubungan" dengan sangat berbeda dengan cara Anda menggunakan "Database Relasional" atau RDBMS. Konsep umum "hubungan" sebenarnya adalah "menyematkan" data, baik sebagai properti tunggal atau sebagai larik.
Anda mungkin benar-benar menginginkan keluaran seperti itu, yang juga merupakan bagian dari alasan mengapa tanpa $unwind
urutkan di sini output dari $lookup
sebenarnya adalah "array". Namun menggunakan $unwind
dalam konteks ini sebenarnya adalah hal yang paling efektif untuk dilakukan, sekaligus memberikan jaminan bahwa data yang "bergabung" tidak benar-benar menyebabkan batas BSON tersebut di atas terlampaui sebagai akibat dari "bergabung" tersebut.
Jika Anda benar-benar menginginkan array keluaran, maka hal terbaik untuk dilakukan di sini adalah menggunakan $group
tahap pipa, dan mungkin sebagai beberapa tahap untuk "menormalkan" dan "membatalkan hasil" dari $unwind
{ "$group": {
"_id": "$_id",
"tb1_field": { "$first": "$tb1_field" },
"tb1_another": { "$first": "$tb1_another" },
"tb3": { "$push": "$tb3" }
}}
Di mana Anda sebenarnya untuk kasus ini mencantumkan semua bidang yang Anda perlukan dari "tb1"
dengan nama properti mereka menggunakan $first
untuk hanya menyimpan kejadian "pertama" ( pada dasarnya diulang dengan hasil "tb2"
dan "tb3"
unwound ) lalu $push
"detail" dari "tb3"
ke dalam "array" untuk merepresentasikan relasi ke "tb1"
.
Tetapi bentuk umum dari pipa agregasi seperti yang diberikan adalah representasi yang tepat tentang bagaimana hasil akan diperoleh dari SQL asli, yang merupakan keluaran "denormalisasi" sebagai hasil dari "gabungan". Apakah Anda ingin "menormalkan" hasil lagi setelah ini terserah Anda.