Wie entfernen Sie in mongoDb ein Array-Element anhand seines Index?

97

Nehmen Sie im folgenden Beispiel an, dass sich das Dokument in der Auflistung db.people befindet .

Wie entferne ich das dritte Element des Interessenarrays anhand seines Index ?

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

Dies ist meine aktuelle Lösung:

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

Gibt es einen direkteren Weg?

dannie.f
quelle

Antworten:

138

Es gibt keine direkte Möglichkeit zum Ziehen / Entfernen nach Array-Index. In der Tat ist dies eine offene Ausgabe http://jira.mongodb.org/browse/SERVER-1014 , Sie können dafür stimmen.

Die Problemumgehung verwendet $ unset und dann $ pull:

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

Update: Wie in einigen Kommentaren erwähnt, ist dieser Ansatz nicht atomar und kann einige Rennbedingungen verursachen, wenn andere Clients zwischen den beiden Operationen lesen und / oder schreiben. Wenn die Operation atomar sein muss, können wir:

  • Lesen Sie das Dokument aus der Datenbank
  • Aktualisieren Sie das Dokument und entfernen Sie das Element im Array
  • Ersetzen Sie das Dokument in der Datenbank. Um sicherzustellen, dass sich das Dokument seit dem Lesen nicht geändert hat, können wir das Update verwenden, wenn das aktuelle Muster in den Mongo-Dokumenten beschrieben ist
Javier Ferrero
quelle
33
Es ist anderthalb Jahre her? Ist das immer noch der Stand der Dinge bei Mongodb?
Abe
Die nächste Antwort ist direkter und hat für mich funktioniert. Es wird zwar nicht nach Index entfernt, sondern nach Wert entfernt.
Vish
1
@Javier - würde diese Lösung Sie nicht für Probleme offen lassen, wenn sich die Datenbank zwischen dem Zeitpunkt, zu dem Sie die Position im Index gezählt haben, und dem Zeitpunkt, zu dem Sie die Einstellung vorgenommen haben, geändert hat?
UpTheCreek
Dies ist nicht atomar, daher kann es zu undurchsichtigen Rennbedingungen kommen, wenn Sie Code haben, der nicht darauf vorbereitet ist, mit einem Null-Eintrag im Array fertig zu werden.
Glenn Maynard
3
8 Jahre und dieses Ticket wurde noch nicht implementiert ...
Olly John
19

Sie können den $pullModifikator der updateOperation verwenden, um ein bestimmtes Element in einem Array zu entfernen. Falls Sie eine Abfrage angegeben haben, sieht diese folgendermaßen aus:

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

Sie können auch in Betracht ziehen $pullAll, alle Vorkommen zu entfernen. Mehr dazu auf der offiziellen Dokumentationsseite - http://www.mongodb.org/display/DOCS/Updating#Updating-%24pull

Dies verwendet den Index nicht als Kriterium zum Entfernen eines Elements, kann jedoch in ähnlichen Fällen hilfreich sein. IMO, die Verwendung von Indizes zur Adressierung von Elementen innerhalb eines Arrays ist nicht sehr zuverlässig, da Mongodb in einer Elementreihenfolge nicht so konsistent ist, wie ich weiß.

Sonnensucher
quelle
6
Sein Element ist ein Array in der Frage. Mongo ist in diesem Fall in der Reihenfolge konsistent. Tatsächlich sind alle Dokumente geordnete Sammlungen, aber viele Fahrer behandeln sie nicht auf diese Weise. Erschwerend kommt hinzu, dass die Reihenfolge der Tasten plötzlich alphabetisch wird, wenn ein Dokument nach einem Aktualisierungsvorgang verschoben werden muss (aufgrund des Wachstums über den Auffüllfaktor hinaus) Meines Wissens nach sind Arrays so ziemlich Standarddokumente mit Schlüsseln, die aufeinanderfolgende Ganzzahlen anstelle von Zeichenfolgen sind.
März 75
Dies vermeidet die Probleme von Unset + Pull, erfordert jedoch, dass Ihr Wörterbuch streng geordnet ist. Die Wörterbücher in den meisten Sprachen sind ungeordnet, daher kann es schwierig sein, dies zu erreichen.
Glenn Maynard
@ marr75: Hast du eine Referenz? Mongo-Dokumente sollen immer die Ordnung behalten, und wenn sie Unterdokumente jemals neu ordneten, würden viele Dinge kaputt gehen.
Glenn Maynard
Ich habe keine Referenz. Ich weiß dies aus persönlicher Erfahrung mit 2.2, wobei Fehler behoben wurden, die durch aktualisierte und verschobene Dokumente verursacht wurden. Ich habe in den letzten Monaten nicht viel Mongo-Arbeit geleistet, daher kann ich nicht mit aktuelleren Versionen sprechen.
März 75
In Bezug auf die Elementkonsistenz ist mein gewünschter Anwendungsfall. Ein Client fordert json von mongo an. Wenn der Client dann sagt, dass ein Element aus einem json-Array "gelöscht" werden soll, übergibt er den Array-Index an den zu löschenden Server. Wenn sich die Position der Array-Elemente verschieben würde, wäre das seltsam, würde aber erklären, warum sie die Funktion nicht implementieren können
Meffect
4

Anstatt das Unset zu verwenden (wie in der akzeptierten Antwort), löse ich dies, indem ich das Feld auf einen eindeutigen Wert setze (dh nicht NULL) und diesen Wert dann sofort ziehe. Aus asynchroner Sicht etwas sicherer. Hier ist der Code:

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

Hoffentlich wird Mongo dies beheben, indem er möglicherweise den Modifikator $ position erweitert, um sowohl $ pull als auch $ push zu unterstützen.

Stephen Orr
quelle
3

Ich würde empfehlen, für jedes Unterdokument im Array ein GUID-Feld (ich verwende normalerweise ObjectID) oder ein automatisch inkrementierendes Feld zu verwenden.

Mit dieser GUID ist es einfach, einen $ pull auszugeben und sicherzustellen, dass der richtige gezogen wird. Gleiches gilt für andere Array-Operationen.

Höhepunkt
quelle
2

Ab Mongo 4.4dem $functionAggregationsoperator können Sie eine benutzerdefinierte Javascript-Funktion anwenden, um ein Verhalten zu implementieren, das von der MongoDB-Abfragesprache nicht unterstützt wird.

So aktualisieren Sie beispielsweise ein Array, indem Sie ein Element an einem bestimmten Index entfernen:

// { "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 nimmt 3 Parameter:

  • bodyDies ist die anzuwendende Funktion, deren Parameter das zu ändernde Array ist. Die Funktion besteht hier einfach darin, einen Spleiß zu verwenden, um 1 Element am Index 2 zu entfernen.
  • args, enthält die Felder aus dem Datensatz, die die bodyFunktion als Parameter verwendet. In unserem Fall "$interests".
  • langDies ist die Sprache, in der die bodyFunktion geschrieben ist. Nur jsist derzeit verfügbar.
Xavier Guihot
quelle
2

In Mongodb 4.2 können Sie dies tun:

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

P ist der Index des Elements, das Sie aus dem Array entfernen möchten.

Wenn Sie bis zum Ende von P entfernen möchten:

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

Für Leute, die eine Antwort mit Mungo mit nodejs suchen. So mache ich es.

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

Ich kann diese Antwort umschreiben, wenn sie nicht sehr gut verstanden wird, aber ich denke, ist in Ordnung.

Hoffe das hilft dir, ich habe viel Zeit verloren, als ich mich diesem Problem gestellt habe.

Schwarz54
quelle
-3

Anstatt $ pull zu verwenden, können wir $ pop verwenden, um Elemente in einem Array anhand seines Index zu entfernen. Sie sollten jedoch 1 von der Indexposition abziehen, um sie basierend auf dem Index zu entfernen.

Zum Beispiel, wenn Sie das Element in Index 0 entfernen möchten, sollten Sie -1 verwenden, für Index 1 sollten Sie 0 verwenden und so weiter ...

Abfrage zum Entfernen des 3. Elements (Gadgets):

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

als Referenz: https://docs.mongodb.com/manual/reference/operator/update/pop/

Mohan Krishnan
quelle
3
Laut der verknüpften Dokumentation $popkann nur das erste oder letzte Element aus dem Array entfernt werden. Die Abfrage in dieser Antwort entfernt das dritte Element nicht.
Travis G.