Jadi saya mengajukan pertanyaan di komentar tetapi Anda tampaknya telah pergi, jadi saya rasa saya hanya menjawab tiga kemungkinan kasus yang saya lihat.
Untuk memulainya, saya tidak yakin apakah elemen yang ditampilkan dalam array bersarang adalah saja elemen dalam array atau sebenarnya jika arrayToDelete
adalah satu-satunya bidang yang ada dalam elemen-elemen tersebut. Jadi pada dasarnya saya perlu abstrak sedikit dan sertakan kasus itu:
{
field: 'value',
field2: 'value',
scan: [
[
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
[
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
[
{ somethingToKeep: 1 },
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
{
arrayToDelete: [0,1,2],
anotherField: "a"
},
],
]
}
Kasus 1 - Hapus elemen larik bagian dalam tempat Bidang ada
Ini akan menggunakan $pull
operator karena itulah yang menghapus elemen array sepenuhnya. Anda melakukan ini di MongoDB modern dengan pernyataan seperti ini:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
)
Itu mengubah semua dokumen yang cocok seperti ini:
{
"_id" : ObjectId("5ca1c36d9e31550a618011e2"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
],
[
{
"somethingToKeep" : 1
}
]
]
}
Jadi setiap elemen yang berisi bidang itu sekarang dihapus.
Kasus 2 - Hapus saja bidang yang cocok dari elemen dalam
Di sinilah Anda menggunakan $unset
. Hanya sedikit berbeda dengan "hard indexed" versi yang Anda lakukan:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[].$[].arrayToDelete": "" } }
)
Yang mengubah semua dokumen yang cocok menjadi:
{
"_id" : ObjectId("5ca1c4c49e31550a618011e3"),
"field" : "value",
"field2" : "value",
"scan" : [
[
{
"anotherField" : "a"
},
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
}
],
[
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
}
],
[
{
"somethingToKeep" : 1
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
},
{
"anotherField" : "a"
}
]
]
}
Jadi semuanya masih ada, tetapi hanya bidang yang diidentifikasi telah dihapus dari setiap dokumen larik dalam.
Kasus 3 - Anda Sebenarnya ingin menghapus "Semuanya" dalam larik.
Yang benar-benar hanya kasus sederhana menggunakan $set
dan menghapus semua yang ada di sana sebelumnya:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$set": { "scan": [] } }
)
Di mana hasilnya diharapkan cukup baik:
{
"_id" : ObjectId("5ca1c5c59e31550a618011e4"),
"field" : "value",
"field2" : "value",
"scan" : [ ]
}
Jadi, apa yang mereka lakukan?
Hal pertama yang harus Anda lihat adalah predikat kueri . Ini biasanya merupakan ide yang baik untuk memastikan Anda tidak cocok dan bahkan "mencoba" agar kondisi pembaruan terpenuhi pada dokumen yang bahkan tidak berisi data dengan pola yang ingin Anda perbarui. Array bersarang sulit yang terbaik, dan jika praktis, Anda benar-benar harus menghindarinya, seperti yang sering Anda "benar-benar maksud" sebenarnya diwakili dalam larik tunggal dengan atribut tambahan yang mewakili apa yang Anda "pikirkan" bersarang benar-benar melakukannya untuk Anda.
Tapi hanya karena mereka keras bukan berarti tidak mungkin . Hanya saja Anda perlu memahami $elemMatch
:
db.colelction.find(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
}}
)
Itulah dasar find()
contoh, yang cocok berdasarkan $elemMatch
kondisi untuk luar array menggunakan $elemMatch
lain untuk mencocokkan kondisi lain di dalam Himpunan. Meskipun ini "muncul" menjadi predikat tunggal. Sesuatu seperti:
"scan.arrayToDelete": { "$exists": true }
Hanya tidak akan bekerja. Tidak akan:
"scan..arrayToDelete": { "$exists": true }
Dengan "titik ganda" ..
karena itu pada dasarnya tidak valid.
Itu predikat kueri untuk mencocokkan "dokumen" yang perlu diproses, tetapi sisanya berlaku untuk benar-benar menentukan *bagian mana yang akan diperbarui".
Dalam Kasus 1 untuk $pull
dari dalam array, pertama-tama kita harus dapat mengidentifikasi elemen mana dari luar array berisi data yang akan diperbarui. Itulah "scan.$[a]"
hal yang dilakukan menggunakan $[<identifier>]
yang difilter posisi operator.
Operator itu pada dasarnya transpose indeks yang cocok ( jadi banyak dari mereka ) dalam larik ke predikat lain yang didefinisikan di bagian ketiga update
perintah gaya dengan arrayFilters
bagian. Bagian ini pada dasarnya mendefinisikan kondisi yang harus dipenuhi dari perspektif pengidentifikasi bernama.
Dalam hal ini "pengidentifikasi" diberi nama a
, dan itu adalah awalan yang digunakan dalam arrayFilters
entri:
{ "arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
]
}
Diambil dalam konteks dengan pernyataan pembaruan yang sebenarnya bagian:
{
"$pull": {
"scan.$[a]": { "arrayToDelete": { "$exists": true } }
}
},
Kemudian dari perspektif "a"
menjadi pengenal untuk luar elemen array pertama ke dalam dari "scan"
, maka ketentuan yang sama berlaku untuk predikat kueri asli tapi dari "di dalam" yang pertama $elemMatch
penyataan. Jadi pada dasarnya Anda dapat menganggap ini sebagai "kueri dalam kueri" dari perspektif yang sudah "melihat ke dalam" konten setiap bagian luar elemen.
Dengan cara yang sama $pull
bertindak seperti "kueri dalam kueri" dalam argumen itu sendiri juga diterapkan dari perspektif elemen array. Karenanya hanya arrayToDelete
bidang yang ada sebagai ganti:
// This would be wrong! and do nothing :(
{
"$pull": {
"scan.$[a]": { "$elemMatch": { "arrayToDelete": { "$exists": true } } }
}
}
Tapi itu semua khusus untuk $pull
, dan hal-hal lain memiliki kasus yang berbeda:
Kasus 2 melihat di mana Anda ingin $unset
bidang bernama. Tampaknya cukup mudah karena Anda hanya menyebutkan bidangnya, bukan? Yah tidak persis karena berikut ini jelas tidak benar dari apa yang kita ketahui sebelumnya:
{ "$unset": { "scan.arrayToDelete": "" } } // Not right :(
Dan tentu saja mencatat indeks array untuk semuanya hanya menyusahkan:
{ "$unset": {
"scan.0.0.arrayToDelete": "",
"scan.0.1.arrayToDelete": "",
"scan.0.2.arrayToDelete": "",
"scan.0.3.arrayToDelete": "", // My fingers are tired :-<
} }
Ini adalah alasan untuk posisi semua $[]
operator. Yang ini sedikit lebih "kekuatan kasar" daripada $[<identifier>]
. yang difilter posisi dalam hal itu alih-alih mencocokkan predikat lainnya disediakan dalam arrayFilters
, ini hanya berlaku untuk semuanya dalam konten array di "indeks" itu. Ini pada dasarnya adalah cara untuk mengatakan "semua indeks" tanpa mengetik satu per satu seperti mengerikan kasus yang ditunjukkan di atas.
Jadi ini bukan untuk semua kasus , tapi itu pasti cocok untuk $unset
karena itu memiliki penamaan jalur yang sangat spesifik yang tentu saja tidak masalah jika jalur tersebut tidak cocok dengan setiap elemen larik.
Anda bisa masih menggunakan arrayFilters
dan $[<identifier>]
. yang difilter posisi , tapi di sini itu akan berlebihan. Selain itu, tidak ada salahnya untuk mendemonstrasikan pendekatan lain.
Tapi tentu saja mungkin perlu dipahami bagaimana persis pernyataan itu akan terlihat, jadi:
db.collection.updateMany(
{ "scan": {
"$elemMatch": {
"$elemMatch": {
"arrayToDelete": { "$exists": true }
}
}
} },
{ "$unset": { "scan.$[a].$[b].arrayToDelete": "" } },
{
"arrayFilters": [
{ "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } },
{ "b.arrayToDelete": { "$exists": true } },
]
}
)
Perhatikan bahwa "b.arrayToDelete"
mungkin tidak seperti yang Anda harapkan pada awalnya, tetapi mengingat posisinya di "scan.$[a].$[b]
itu benar-benar masuk akal dari b
nama elemen akan dicapai melalui "notasi titik" seperti yang ditunjukkan. Dan sebenarnya dalam kedua kasus. Sekali lagi, sebuah $unset
hanya akan berlaku untuk bidang yang disebutkan, jadi kriteria pemilihan benar-benar tidak diperlukan.
Dan Kasus 3 . Yah itu cukup sederhana jika Anda tidak membutuhkan untuk menyimpan apa pun dalam larik setelah menghapus konten ini (yaitu $pull
di mana bidang yang cocok dengan ini adalah hanya hal-hal di sana, atau $unset
dalam hal ini ), maka jangan main-main dengan hal lain dan cukup hapus array .
Ini adalah perbedaan penting jika Anda menganggapnya sebagai titik untuk memperjelas apakah dokumen dengan bidang bernama di mana hanya elemen dalam array bersarang, dan memang bahwa kunci bernama adalah saja hal yang ada dalam dokumen.
Dengan alasan bahwa menggunakan $pull
seperti yang ditunjukkan di sini dan dalam kondisi tersebut Anda akan mendapatkan:
{
"_id" : ObjectId("5ca321909e31550a618011e6"),
"field" : "value",
"field2" : "value",
"scan" : [
[ ],
[ ],
[ ]
]
}
Atau dengan $unset
:
{
"_id" : ObjectId("5ca322bc9e31550a618011e7"),
"field" : "value",
"field2" : "value",
"scan" : [
[{ }, { }, { }, { }],
[{ }, { }, { }, { }],
[{ }, { }, { }, { }]
]
}
Keduanya jelas tidak diinginkan. Jadi masuk akal jika arrayToDelete
bidang adalah satu-satunya konten yang ada di sana sama sekali, maka cara paling logis untuk menghapus semua hanyalah mengganti array dengan yang kosong. Atau memang $unset
seluruh properti dokumen.
Namun perhatikan bahwa semua "hal-hal mewah" ( dengan pengecualian $set
tentu saja ) mengharuskan Anda setidaknya harus memiliki MongoDB 3.6 tersedia untuk menggunakan fungsi ini.
Jika Anda masih menjalankan MongoDB versi yang lebih lama dari itu (dan pada tanggal penulisan, Anda seharusnya tidak melakukannya karena dukungan resmi Anda habis hanya dalam 5 bulan dari tanggal ini) maka jawaban lain yang ada tentang Cara Memperbarui Banyak Elemen Array di mongodb sebenarnya untuk Anda.