Ada beberapa cara untuk melakukannya menggunakan kerangka kerja agregasi
Hanya sekumpulan data sederhana misalnya:
{
"_id" : ObjectId("538181738d6bd23253654690"),
"movies": [
{ "_id": 1, "rating": 5 },
{ "_id": 2, "rating": 6 },
{ "_id": 3, "rating": 7 }
]
},
{
"_id" : ObjectId("538181738d6bd23253654691"),
"movies": [
{ "_id": 1, "rating": 5 },
{ "_id": 4, "rating": 6 },
{ "_id": 2, "rating": 7 }
]
},
{
"_id" : ObjectId("538181738d6bd23253654692"),
"movies": [
{ "_id": 2, "rating": 5 },
{ "_id": 5, "rating": 6 },
{ "_id": 6, "rating": 7 }
]
}
Menggunakan "pengguna" pertama sebagai contoh, sekarang Anda ingin mengetahui apakah salah satu dari dua pengguna lainnya memiliki setidaknya dua film yang sama.
Untuk MongoDB 2.6 dan yang lebih baru, Anda cukup menggunakan $setIntersection
operator bersama dengan $size
operator:
db.users.aggregate([
// Match the possible documents to reduce the working set
{ "$match": {
"_id": { "$ne": ObjectId("538181738d6bd23253654690") },
"movies._id": { "$in": [ 1, 2, 3 ] },
"$and": [
{ "movies": { "$not": { "$size": 1 } } }
]
}},
// Project a copy of the document if you want to keep more than `_id`
{ "$project": {
"_id": {
"_id": "$_id",
"movies": "$movies"
},
"movies": 1,
}},
// Unwind the array
{ "$unwind": "$movies" },
// Build the array back with just `_id` values
{ "$group": {
"_id": "$_id",
"movies": { "$push": "$movies._id" }
}},
// Find the "set intersection" of the two arrays
{ "$project": {
"movies": {
"$size": {
"$setIntersection": [
[ 1, 2, 3 ],
"$movies"
]
}
}
}},
// Filter the results to those that actually match
{ "$match": { "movies": { "$gte": 2 } } }
])
Ini masih dimungkinkan di MongoDB versi sebelumnya yang tidak memiliki operator tersebut, cukup gunakan beberapa langkah lagi:
db.users.aggregate([
// Match the possible documents to reduce the working set
{ "$match": {
"_id": { "$ne": ObjectId("538181738d6bd23253654690") },
"movies._id": { "$in": [ 1, 2, 3 ] },
"$and": [
{ "movies": { "$not": { "$size": 1 } } }
]
}},
// Project a copy of the document along with the "set" to match
{ "$project": {
"_id": {
"_id": "$_id",
"movies": "$movies"
},
"movies": 1,
"set": { "$cond": [ 1, [ 1, 2, 3 ], 0 ] }
}},
// Unwind both those arrays
{ "$unwind": "$movies" },
{ "$unwind": "$set" },
// Group back the count where both `_id` values are equal
{ "$group": {
"_id": "$_id",
"movies": {
"$sum": {
"$cond":[
{ "$eq": [ "$movies._id", "$set" ] },
1,
0
]
}
}
}},
// Filter the results to those that actually match
{ "$match": { "movies": { "$gte": 2 } } }
])
Rincian
Itu mungkin sedikit untuk dipertimbangkan, jadi kita bisa melihat setiap tahap dan memecahnya untuk melihat apa yang mereka lakukan.
$match :Anda tidak ingin mengoperasikan setiap dokumen dalam koleksi, jadi ini adalah kesempatan untuk menghapus item yang tidak mungkin cocok meskipun masih banyak pekerjaan yang harus dilakukan untuk menemukan tepat yang. Jadi hal yang jelas adalah mengecualikan "pengguna" yang sama dan kemudian hanya mencocokkan dokumen yang memiliki setidaknya satu film yang sama seperti yang ditemukan untuk "pengguna" tersebut.
Hal berikutnya yang masuk akal adalah mempertimbangkan bahwa ketika Anda ingin mencocokkan n
entri maka hanya dokumen yang memiliki larik "film" yang lebih besar dari n-1
mungkin benar-benar berisi kecocokan. Penggunaan $and
di sini terlihat lucu dan tidak diperlukan secara khusus, tetapi jika kecocokan yang diperlukan adalah 4
maka bagian pernyataan yang sebenarnya akan terlihat seperti ini:
"$and": [
{ "movies": { "$not": { "$size": 1 } } },
{ "movies": { "$not": { "$size": 2 } } },
{ "movies": { "$not": { "$size": 3 } } }
]
Jadi pada dasarnya Anda "mengesampingkan" array yang tidak mungkin cukup panjang untuk memiliki n
pertandingan. Perhatikan di sini bahwa $size
ini
operator dalam formulir kueri berbeda dengan $size
untuk kerangka agregasi. Misalnya, tidak ada cara untuk menggunakan ini dengan operator ketidaksetaraan seperti $gt
apakah tujuannya adalah untuk secara khusus mencocokkan "ukuran" yang diminta. Oleh karena itu bentuk kueri ini untuk menentukan semua kemungkinan ukuran yang kurang dari.
$proyek :Ada beberapa tujuan dalam pernyataan ini, beberapa di antaranya berbeda tergantung pada versi MongoDB yang Anda miliki. Pertama, dan secara opsional, salinan dokumen disimpan di bawah _id
nilai sehingga bidang ini tidak diubah oleh langkah-langkah selanjutnya. Bagian lain di sini adalah menjaga larik "film" di bagian atas dokumen sebagai salinan untuk tahap berikutnya.
Apa yang juga terjadi pada versi yang disajikan untuk versi pra 2.6 apakah ada larik tambahan yang mewakili _id
nilai untuk "film" yang cocok. Penggunaan $cond
operator di sini hanyalah cara untuk membuat representasi "literal" dari array. Cukup lucu, MongoDB 2.6 memperkenalkan operator yang dikenal sebagai $literal
untuk melakukan ini tanpa cara yang lucu kita menggunakan $cond
di sini.
$santai :Untuk melakukan sesuatu yang lebih jauh, susunan film perlu dibatalkan karena dalam kedua kasus itu adalah satu-satunya cara untuk mengisolasi _id
yang ada nilai untuk entri yang perlu dicocokkan dengan "set". Jadi untuk versi pra 2.6 Anda perlu "melepaskan" kedua larik yang ada.
$grup :Untuk MongoDB 2.6 dan yang lebih baru, Anda hanya mengelompokkan kembali ke array yang hanya berisi _id
nilai film dengan "peringkat" dihapus.
Pra 2.6 karena semua nilai disajikan "berdampingan" (dan dengan banyak duplikasi), Anda melakukan perbandingan kedua nilai untuk melihat apakah keduanya sama. Dimana itu true
, ini memberitahu $cond
pernyataan operator untuk mengembalikan nilai 1
atau 0
dimana kondisinya false
. Ini langsung diteruskan kembali melalui $sum
untuk menjumlahkan jumlah elemen yang cocok dalam larik ke "set" yang diperlukan.
$proyek :Di mana ini adalah bagian yang berbeda untuk MongoDB 2.6 dan yang lebih besar adalah karena Anda telah mendorong kembali larik _id
"film" nilai yang kemudian Anda gunakan $setIntersection
untuk langsung membandingkan array tersebut. Hasilnya adalah array yang berisi elemen-elemen yang sama, kemudian dibungkus dengan $size
operator untuk menentukan berapa banyak elemen yang dikembalikan dalam set yang cocok itu.
$match :Apakah tahap akhir yang telah dilaksanakan disini yang melakukan langkah yang jelas untuk mencocokkan hanya dokumen-dokumen yang jumlah elemen berpotongannya lebih besar atau sama dengan jumlah yang dibutuhkan.
Akhir
Itu pada dasarnya bagaimana Anda melakukannya. Sebelum 2.6 sedikit lebih rumit dan akan membutuhkan sedikit lebih banyak memori karena ekspansi yang dilakukan dengan menduplikasi setiap anggota array yang ditemukan oleh semua nilai yang mungkin dari himpunan, tetapi masih merupakan cara yang valid untuk melakukan ini.
Yang perlu Anda lakukan adalah menerapkan ini dengan n
greater yang lebih besar nilai yang cocok untuk memenuhi kondisi Anda, dan tentu saja pastikan kecocokan pengguna asli Anda memiliki n
. yang diperlukan kemungkinan. Jika tidak, buat saja ini di n-1
dari panjang larik "film" "pengguna".