Di mongoDb, bagaimana Anda menghapus elemen array dengan indeksnya?

97

Dalam contoh berikut, asumsikan dokumen ada dalam koleksi db.people .

Bagaimana cara menghapus elemen ke-3 dari array minat dengan indeksnya ?

{
  "_id" : ObjectId("4d1cb5de451600000000497a"),           
  "name" : "dannie",  
  "interests" : [  
    "guitar",  
    "programming",           
    "gadgets",  
    "reading"  
  ]   
}

Ini solusi saya saat ini:

var interests = db.people.findOne({"name":"dannie"}).interests;  
interests.splice(2,1)  
db.people.update({"name":"dannie"}, {"$set" : {"interests" : interests}});

Apakah ada cara yang lebih langsung?

dannie.f
sumber

Jawaban:

138

Tidak ada cara langsung untuk menarik / menghapus dengan indeks array. Sebenarnya, ini adalah terbitan terbuka http://jira.mongodb.org/browse/SERVER-1014 , Anda dapat memilihnya.

Solusinya adalah menggunakan $ unset dan kemudian $ pull:

db.lists.update({}, {$unset : {"interests.3" : 1 }}) 
db.lists.update({}, {$pull : {"interests" : null}})

Pembaruan: seperti yang disebutkan dalam beberapa komentar, pendekatan ini tidak atomic dan dapat menyebabkan beberapa kondisi balapan jika klien lain membaca dan / atau menulis di antara dua operasi. Jika kita membutuhkan operasi menjadi atom, kita dapat:

  • Baca dokumen dari database
  • Perbarui dokumen dan hapus item dalam array
  • Ganti dokumen di database. Untuk memastikan dokumen tidak berubah sejak kita membacanya, kita dapat menggunakan pembaruan jika pola saat ini dijelaskan di dokumen mongo
Javier Ferrero
sumber
33
Sudah satu setengah tahun? Apakah ini masih keadaan dengan mongodb?
Abe
Jawaban berikutnya lebih langsung dan berhasil untuk saya. Meskipun tidak menghapus menurut indeks, melainkan menghapus berdasarkan nilai.
vish
1
@ Javier - bukankah solusi ini akan membuat Anda terbuka untuk masalah jika db berubah antara waktu Anda menghitung posisi dalam indeks, dan waktu Anda melakukan unset?
UpTheCreek
Ini bukan atom, jadi kemungkinan menyebabkan kondisi balapan tidak jelas jika Anda memiliki kode yang tidak siap untuk mengatasi entri null dalam larik.
Glenn Maynard
3
8 tahun dan tiket ini masih belum diterapkan ...
Olly John
19

Anda dapat menggunakan $pullpengubah updateoperasi untuk menghapus elemen tertentu dalam array. Jika Anda memberikan kueri akan terlihat seperti ini:

db.people.update({"name":"dannie"}, {'$pull': {"interests": "guitar"}})

Juga, Anda dapat mempertimbangkan menggunakan $pullAlluntuk menghapus semua kejadian. Lebih lanjut tentang ini di halaman dokumentasi resmi - http://www.mongodb.org/display/DOCS/Updating#Updating-%24pull

Ini tidak menggunakan indeks sebagai kriteria untuk menghapus elemen, tetapi masih dapat membantu dalam kasus yang serupa dengan Anda. IMO, menggunakan indeks untuk menangani elemen di dalam array sangat tidak dapat diandalkan karena mongodb tidak konsisten pada urutan elemen seperti yang saya tahu.

Sunseeker
sumber
6
Elemennya adalah larik dalam pertanyaan. Mongo konsisten dalam hal ini. Faktanya, semua dokumen sebenarnya adalah koleksi yang dipesan tetapi banyak driver tidak menanganinya dengan cara ini. Lebih memperumit masalah, jika dokumen harus dipindahkan setelah operasi pembaruan (karena pertumbuhan di luar faktor padding) urutan kunci tiba-tiba menjadi menurut abjad (di bawah sampul, saya pikir mereka diurutkan berdasarkan nilai binernya, kecurigaan ini adalah berdasarkan pengetahuan saya bahwa array adalah dokumen standar yang cukup banyak dengan kunci yang merupakan integer berurutan, bukan string).
marr75
Ini menghindari masalah unset + pull, tetapi mengharuskan kamus Anda diurutkan dengan ketat. Kamus di sebagian besar bahasa tidak berurutan, jadi sulit untuk mewujudkannya.
Glenn Maynard
@ marr75: Apakah Anda punya referensi? Dokumen Mongo seharusnya selalu menjaga ketertiban, dan jika dokumen itu disusun ulang, banyak hal yang akan rusak.
Glenn Maynard
Saya tidak punya referensi. Saya tahu ini dari pengalaman pribadi dengan 2.2, memiliki bug yang diperbaiki yang diperkenalkan oleh dokumen yang diperbarui dan dipindahkan. Saya belum melakukan banyak pekerjaan mongo dalam beberapa bulan terakhir jadi saya tidak dapat berbicara dengan versi yang lebih baru.
marr75
Sejauh konsistensi elemen, kasus penggunaan yang saya inginkan adalah. Klien meminta json dari mongo dan kemudian jika klien memilih untuk mengatakan, 'hapus' item dari larik json, ia akan meneruskan indeks larik ke server untuk dihapus. jika posisi item array dipindahkan, itu akan aneh, tetapi akan menjelaskan mengapa mereka tidak dapat mengimplementasikan fitur
meffect
4

Alih-alih menggunakan unset (seperti pada jawaban yang diterima), saya menyelesaikan ini dengan mengatur bidang ke nilai unik (yaitu bukan NULL) dan kemudian segera menarik nilai itu. Sedikit lebih aman dari perspektif asynch. Ini kodenya:

    var update = {};
    var key = "ToBePulled_"+ new Date().toString();
    update['feedback.'+index] = key;
    Venues.update(venueId, {$set: update});
    return Venues.update(venueId, {$pull: {feedback: key}});

Mudah-mudahan mongo akan mengatasi hal ini, mungkin dengan memperluas pengubah $ position untuk mendukung $ pull serta $ push.

Stephen Orr
sumber
3

Saya akan merekomendasikan menggunakan bidang GUID (Saya cenderung menggunakan ObjectID), atau bidang auto-incrementing untuk setiap sub-dokumen dalam larik.

Dengan GUID ini, mudah untuk mengeluarkan $ pull dan memastikan bahwa yang benar akan ditarik. Hal yang sama berlaku untuk operasi array lainnya.

Klimaks
sumber
2

Mulai dari Mongo 4.4,$function operator agregasi memungkinkan penerapan fungsi javascript khusus untuk mengimplementasikan perilaku yang tidak didukung oleh Bahasa Kueri MongoDB.

Misalnya, untuk memperbarui array dengan menghapus elemen pada indeks tertentu:

// { "name": "dannie", "interests": ["guitar", "programming", "gadgets", "reading"] }
db.collection.update(
  { "name": "dannie" },
  [{ $set:
    { "interests":
      { $function: {
          body: function(interests) { interests.splice(2, 1); return interests; },
          args: ["$interests"],
          lang: "js"
      }}
    }
  }]
)
// { "name": "dannie", "interests": ["guitar", "programming", "reading"] }

$function membutuhkan 3 parameter:

  • body, yang merupakan fungsi untuk diterapkan, yang parameternya adalah larik yang akan diubah. Fungsi di sini hanya terdiri dari penggunaan sambatan untuk menghapus 1 elemen pada indeks 2.
  • args, yang berisi bidang dari rekaman yang bodydiambil fungsi sebagai parameter. Dalam kasus kami "$interests".
  • lang, yang merupakan bahasa bodypenulisan fungsi. Hanya jssaat ini tersedia.
Xavier Guihot
sumber
2

di Mongodb 4.2 Anda bisa melakukan ini:

db.example.update({}, [
     {$set: {field: {
           $concatArrays: [ 
                  {$slice: ["$field", P]}, 
                  {$slice: ["$field", {$add: [1, P]}, {$size: "$field"}]}
           ]
     }}}
]);

P adalah indeks elemen yang ingin Anda hapus dari array.

Jika Anda ingin menghapus dari P sampai akhir:

db.example.update({}, [
     {$set: {field: {$slice: ["$field", P]}}
]);
Ali
sumber
0

Untuk orang yang mencari jawaban menggunakan luwak dengan nodejs. Beginilah cara saya melakukannya.

exports.deletePregunta = function (req, res) {
let codTest = req.params.tCodigo;
let indexPregunta = req.body.pregunta; // the index that come from frontend
let inPregunta = `tPreguntas.0.pregunta.${indexPregunta}`; // my field in my db
let inOpciones = `tPreguntas.0.opciones.${indexPregunta}`; // my other field in my db
let inTipo = `tPreguntas.0.tipo.${indexPregunta}`; // my  other field in my db

Test.findOneAndUpdate({ tCodigo: codTest },
    {
        '$unset': {
            [inPregunta]: 1, // put the field with [] 
            [inOpciones]: 1,
            [inTipo]: 1
        }
    }).then(()=>{ 
    Test.findOneAndUpdate({ tCodigo: codTest }, {
        '$pull': {
            'tPreguntas.0.pregunta': null,
            'tPreguntas.0.opciones': null,
            'tPreguntas.0.tipo': null
        }
    }).then(testModificado => {
        if (!testModificado) {
            res.status(404).send({ accion: 'deletePregunta', message: 'No se ha podido borrar esa pregunta ' });
        } else {
            res.status(200).send({ accion: 'deletePregunta', message: 'Pregunta borrada correctamente' });
        }
    })}).catch(err => { res.status(500).send({ accion: 'deletePregunta', message: 'error en la base de datos ' + err }); });
 }

Saya dapat menulis ulang jawaban ini jika tidak dipahami dengan baik, tetapi menurut saya tidak masalah.

Semoga ini membantu Anda, saya kehilangan banyak waktu menghadapi masalah ini.

Schwarz54
sumber
-3

Alih-alih menggunakan $ pull, kita dapat menggunakan $ pop untuk menghapus elemen dalam array dengan indeksnya. Tetapi Anda harus mengurangi 1 dari posisi indeks untuk menghapus berdasarkan indeks.

Untuk Misal jika ingin menghapus elemen pada index 0 sebaiknya gunakan -1, untuk index 1 sebaiknya gunakan 0 dan seterusnya ...

Kueri Untuk Menghapus Elemen ke-3 (gadget):

db.people.update({"name":"dannie"}, {'$pop': {"interests": 1}})

untuk referensi: https://docs.mongodb.com/manual/reference/operator/update/pop/

Mohan Krishnan
sumber
4
Menurut dokumentasi terkait, $pophanya dapat menghapus elemen pertama atau terakhir dari array. Kueri dalam jawaban ini tidak menghapus elemen ketiga.
Travis G.