MongoDb'de, bir dizi elemanını dizinine göre nasıl kaldırırsınız?


97

Aşağıdaki örnekte, belgenin db.people koleksiyonunda olduğunu varsayın .

İlgi alanları dizisinin 3. öğesi indeksine göre nasıl kaldırılır ?

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

Bu benim mevcut çözümüm:

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

Daha doğrudan bir yol var mı?


Yanıtlar:


139

Dizi indeksine göre çekmenin / çıkarmanın düz bir yolu yoktur. Aslında bu açık bir sorundur http://jira.mongodb.org/browse/SERVER-1014 , buna oy verebilirsiniz.

Çözüm, $ unset ve ardından $ pull kullanıyor:

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

Güncelleme: bazı yorumlarda belirtildiği gibi, bu yaklaşım atomik değildir ve diğer istemciler iki işlem arasında okur ve / veya yazarsa bazı yarış koşullarına neden olabilir. Operasyonun atomik olmasına ihtiyacımız varsa, şunları yapabiliriz:

  • Belgeyi veritabanından okuyun
  • Belgeyi güncelleyin ve dizideki öğeyi kaldırın
  • Veritabanındaki belgeyi değiştirin. Belgenin okuduğumuzdan beri değişmediğinden emin olmak için, güncel kalıbı mongo belgelerinde açıklandığı takdirde güncellemeyi kullanabiliriz.

33
Bir buçuk yıl mı oldu? Bu hala mongodb ile ilgili durum mu?
Abe

Bir sonraki cevap daha doğrudan ve benim için çalıştı. Dizine göre değil, değere göre kaldırılmasına rağmen.
13'te

1
@Javier - db, dizindeki konumu saydığınız zaman ile ayarlamadığınız zaman arasında değişse, bu çözüm sizi sorunlara açık bırakmaz mı?
UpTheCreek

Bu atomik değildir, bu nedenle dizideki boş bir girişle başa çıkmaya hazır olmayan herhangi bir kodunuz varsa, muhtemelen belirsiz yarış koşullarına neden olabilir.
Glenn Maynard

3
8 yıldır ve bu bilet hala uygulanmadı ...
Olly John

19

Bir dizideki belirli bir öğeyi kaldırmak için işlem $pulldeğiştiricisini kullanabilirsiniz update. Bir sorgu sağlamanız durumunda şöyle görünecektir:

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

Ayrıca, $pullAlltüm oluşumları kaldırmak için kullanmayı düşünebilirsiniz . Resmi dokümantasyon sayfasında bununla ilgili daha fazla bilgi - http://www.mongodb.org/display/DOCS/Updating#Updating-%24pull

Bu, indeksi bir öğeyi kaldırmak için bir kriter olarak kullanmaz, ancak yine de sizinkine benzer durumlarda yardımcı olabilir. IMO, bir dizi içindeki öğeleri adreslemek için indeksler kullanmak çok güvenilir değildir çünkü mongodb, bildiğim kadarıyla fas bir öğe düzeninde tutarlı değildir.


6
Elemanı sorudaki bir dizidir. Mongo bu durumda sırayla tutarlı. Aslında, tüm belgeler aslında sıralı koleksiyonlardır, ancak birçok sürücü bunları bu şekilde işlemez. Konuyu daha da karmaşık hale getiren, bir belgenin bir güncelleme işleminden sonra taşınması gerekiyorsa (dolgu faktörünün ötesinde büyüme nedeniyle) tuşların sırası aniden alfabetik hale gelir (kapakların altında, bunların ikili değerlerine göre sıralandığını düşünüyorum, bu şüphe şudur: dizilerin dizeler yerine ardışık tamsayı olan anahtarlara sahip hemen hemen standart belgeler olduğu bilgisine dayanarak).
Marr75

Bu, ayarlanmamış + çekme sorunlarını ortadan kaldırır, ancak sözlüğünüzün kesin olarak sıralanmasını gerektirir. Çoğu dilde sözlükler sırasızdır, bu yüzden bunu yapmak acı verici olabilir.
Glenn Maynard

@ marr75: Referansınız var mı? Mongo belgelerinin her zaman sırayı koruması gerekiyordu ve alt belgeleri yeniden düzenlerse birçok şey bozulacaktı.
Glenn Maynard

Referansım yok. Bunu, güncellenen ve taşınan belgelerin getirdiği düzeltilmiş hatalara sahip 2.2 ile kişisel deneyimimden biliyorum. Geçtiğimiz birkaç aydır fazla mongo çalışması yapmadığım için daha güncel sürümlerle konuşamam.
marr75

Öğe tutarlılığına gelince, benim istediğim kullanım durumu. Bir istemci, mongo'dan json talep eder ve ardından eğer müşteri bir json dizisinden bir öğeyi 'sil' demeyi seçerse, dizi dizinini silinecek sunucuya iletir. dizi öğelerinin konumu taşınırsa, bu garip olurdu, ancak özelliği neden uygulayamadıklarını açıklar
Northstrider

4

Unset'i kullanmak yerine (kabul edilen cevapta olduğu gibi), alanı benzersiz bir değere (yani NULL değil) ayarlayarak ve ardından hemen bu değeri çekerek çözerim. Eşzamansız bir bakış açısıyla biraz daha güvenli. İşte kod:

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

Umarım mongo bunu, belki $ pozisyon değiştiricisini $ pull ve $ push'u destekleyecek şekilde genişleterek ele alır.


2

Bir GUID (Nesne Kimliği kullanma eğilimindeyim) alanı veya dizideki her bir alt belge için otomatik olarak artan bir alan kullanmanızı tavsiye ederim.

Bu GUID ile bir $ çekmesi yapmak ve doğru olanın çekileceğinden emin olmak kolaydır. Aynısı diğer dizi işlemleri için de geçerlidir.


2

Başlangıç ​​olarak Mongo 4.4, $functiontoplama operatörü, MongoDB Sorgu Dili tarafından desteklenmeyen davranışı uygulamak için özel bir javascript işlevinin uygulanmasına izin verir.

Örneğin, belirli bir dizindeki bir öğeyi kaldırarak bir diziyi güncellemek için:

// { "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 3 parametre alır:

  • body, hangi parametresi değiştirilecek dizi olan uygulanacak işlevdir. Buradaki işlev basitçe, endeks 2'deki 1 öğeyi kaldırmak için ekleme kullanmaktan ibarettir.
  • args, bodyişlevin parametre olarak aldığı kayıttaki alanları içeren . Bizim durumumuzda "$interests".
  • lang, bodyfonksiyonun yazıldığı dildir . Yalnızca jsşu anda mevcuttur.

2

Mongodb 4.2'de bunu yapabilirsiniz:

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

P, diziden çıkarmak istediğiniz öğenin dizinidir.

P'den sonuna kadar çıkarmak isterseniz:

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

0

Nodejs ile firavun faresi kullanarak cevap arayanlar için. Ben böyle yaparım.

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 }); });
 }

Çok iyi anlamazsa bu cevabı yeniden yazabilirim, ama bence sorun değil.

Umarım bu size yardımcı olur, bu sorunla yüzleşmek için çok zaman kaybettim.


-3

$ Pull kullanmak yerine bir dizideki elemanları indeksine göre kaldırmak için $ pop kullanabiliriz. Ancak dizine bağlı olarak kaldırmak için dizin konumundan 1 çıkarmanız gerekir.

Örneğin, 0 dizinindeki öğeyi kaldırmak istiyorsanız -1, dizin 1 için 0 kullanmalısınız vb ...

3. Öğeyi Kaldırmak İçin Sorgu (gadget'lar):

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

referans için: https://docs.mongodb.com/manual/reference/operator/update/pop/


4
Bağlantılı belgelere göre $pop, diziden yalnızca ilk veya son öğeyi kaldırabilir. Bu yanıttaki sorgu üçüncü öğeyi kaldırmaz.
Travis G.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.