In mongoDb, come rimuovi un elemento dell'array dal suo indice?


97

Nell'esempio seguente, supponiamo che il documento si trovi nella raccolta db.people .

Come rimuovere il terzo elemento dell'array degli interessi dal suo indice ?

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

Questa è la mia soluzione attuale:

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

C'è un modo più diretto?


Risposte:


138

Non esiste un modo diretto per estrarre / rimuovere in base all'indice dell'array. In effetti, questo è un problema aperto http://jira.mongodb.org/browse/SERVER-1014 , puoi votarlo.

La soluzione alternativa consiste nell'usare $ unset e quindi $ pull:

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

Aggiornamento: come menzionato in alcuni commenti questo approccio non è atomico e può causare alcune condizioni di competizione se altri client leggono e / o scrivono tra le due operazioni. Se abbiamo bisogno che l'operazione sia atomica, potremmo:

  • Leggi il documento dal database
  • Aggiorna il documento e rimuovi l'elemento dall'array
  • Sostituisci il documento nel database. Per assicurarci che il documento non sia cambiato da quando lo leggiamo, possiamo usare l'aggiornamento se il modello corrente è descritto nei documenti di mongo

33
È passato un anno e mezzo? È ancora questo lo stato delle cose con mongodb?
Abe

La risposta successiva è più diretta e ha funzionato per me. Sebbene non rimuova per indice, ma piuttosto rimuova per valore.
vish

1
@ Javier - questa soluzione non ti lascerebbe aperto a problemi se il db fosse cambiato tra il momento in cui hai contato la posizione nell'indice e il tempo in cui hai annullato l'impostazione?
UpTheCreek

Questo non è atomico, quindi è probabile che causi condizioni di competizione oscure se hai del codice che non è pronto per far fronte a una voce nulla nell'array.
Glenn Maynard

3
8 anni e questo ticket non è stato ancora implementato ...
Olly John

19

È possibile utilizzare il $pullmodificatore di updateoperazione per rimuovere un particolare elemento in un array. Nel caso in cui tu abbia fornito una query sarà simile a questa:

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

Inoltre, potresti prendere in considerazione l'utilizzo $pullAllper rimuovere tutte le occorrenze. Maggiori informazioni sulla pagina della documentazione ufficiale - http://www.mongodb.org/display/DOCS/Updating#Updating-%24pull

Questo non utilizza l'indice come criterio per rimuovere un elemento, ma potrebbe comunque essere d'aiuto in casi simili ai tuoi. IMO, l'utilizzo degli indici per indirizzare gli elementi all'interno di un array non è molto affidabile poiché mongodb non è coerente sull'ordine degli elementi per quanto ne so.


6
Il suo elemento è una matrice nella domanda. Mongo è coerente con l'ordine in questo caso. In effetti, tutti i documenti sono effettivamente raccolte ordinate ma molti conducenti non li gestiscono in questo modo. A complicare ulteriormente la questione, se un documento deve essere spostato dopo un'operazione di aggiornamento (a causa della crescita oltre il fattore di riempimento) l'ordine dei tasti diventa improvvisamente alfabetico (sotto le coperte, penso che siano ordinati in base al loro valore binario, questo sospetto è in base alla mia conoscenza che gli array sono praticamente documenti standard con chiavi che sono numeri interi consecutivi invece di stringhe).
marr75

Questo evita i problemi di unset + pull, ma richiede che il tuo dizionario sia rigorosamente ordinato. I dizionari nella maggior parte delle lingue non sono ordinati, quindi può essere difficile farlo accadere.
Glenn Maynard

@ marr75: hai un riferimento? I documenti Mongo dovrebbero sempre mantenere l'ordine e, se mai riordinassero i documenti secondari, molte cose si rompono.
Glenn Maynard

Non ho referenze. Lo so per esperienza personale con 2.2, avendo risolto bug introdotti da documenti aggiornati e spostati. Non ho lavorato molto con il mongo negli ultimi mesi, quindi non posso parlare di versioni più attuali.
marr75

Per quanto riguarda la coerenza degli elementi, il mio caso d'uso desiderato è. Un client richiede json da mongo e quindi se il client sceglie di dire "elimina" un elemento da un array json, passerà l'indice dell'array al server per essere eliminato. se la posizione degli elementi dell'array si spostasse, sarebbe strano, ma spiegherebbe perché non possono implementare la funzione
meffect

4

Invece di usare unset (come nella risposta accettata), risolvo questo problema impostando il campo su un valore univoco (cioè non NULL) e quindi estraendo immediatamente quel valore. Un po 'più sicuro da una prospettiva asincrona. Ecco il codice:

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

Si spera che mongo risolva questo problema, forse estendendo il modificatore $ position per supportare $ pull così come $ push.


3

Suggerirei di utilizzare un campo GUID (tendo a usare ObjectID) o un campo con incremento automatico per ogni documento secondario nell'array.

Con questo GUID è facile emettere un $ pull ed essere sicuri che verrà estratto quello corretto. Lo stesso vale per altre operazioni sugli array.


2

A partire dal Mongo 4.4, l' $functionoperatore di aggregazione consente di applicare una funzione javascript personalizzata per implementare un comportamento non supportato dal linguaggio di query MongoDB.

Ad esempio, per aggiornare un array rimuovendo un elemento in un dato indice:

// { "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 accetta 3 parametri:

  • body, che è la funzione da applicare, il cui parametro è l'array da modificare. La funzione qui consiste semplicemente nell'usare la giunzione per rimuovere 1 elemento all'indice 2.
  • args, che contiene i campi del record che la bodyfunzione prende come parametro. Nel nostro caso "$interests".
  • lang, che è la lingua in cui bodyè scritta la funzione. Solo jsè attualmente disponibile.

2

in Mongodb 4.2 puoi fare questo:

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

P è l'indice dell'elemento che vuoi rimuovere dall'array.

Se vuoi rimuovere da P fino alla fine:

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

0

Per le persone che cercano una risposta utilizzando mangusta con nodejs. Ecco come lo faccio.

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

Posso riscrivere questa risposta se non capisce molto bene, ma penso che vada bene.

Spero che questo ti aiuti, ho perso molto tempo ad affrontare questo problema.


-3

Invece di usare $ pull possiamo usare $ pop per rimuovere gli elementi in un array dal suo indice. Ma dovresti sottrarre 1 dalla posizione dell'indice per la rimozione basata sull'indice.

Ad esempio, se vuoi rimuovere l'elemento nell'indice 0 dovresti usare -1, per l'indice 1 dovresti usare 0 e così via ...

Query per rimuovere il terzo elemento (gadget):

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

per riferimento: https://docs.mongodb.com/manual/reference/operator/update/pop/


3
Secondo la documentazione collegata, $poppuò rimuovere solo il primo o l'ultimo elemento dall'array. La domanda in questa risposta non rimuove il terzo elemento.
Travis G.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.