Wie aktualisiere ich die _id eines MongoDB-Dokuments?

129

Ich möchte ein _idFeld eines Dokuments aktualisieren . Ich weiß, dass es keine wirklich gute Übung ist. Aber aus irgendeinem technischen Grund muss ich es aktualisieren. Wenn ich versuche, es zu aktualisieren, erhalte ich:

> db.clients.update({ _id: ObjectId("123")}, { $set: { _id: ObjectId("456")}})

Performing an update on the path '_id' would modify the immutable field '_id'

Und das Update wird nicht gemacht. Wie kann ich es aktualisieren?

Shingara
quelle

Antworten:

216

Sie können es nicht aktualisieren. Sie müssen das Dokument mit einem neuen Dokument speichern _idund dann das alte Dokument entfernen.

// store the document in a variable
doc = db.clients.findOne({_id: ObjectId("4cc45467c55f4d2d2a000002")})

// set a new _id on the document
doc._id = ObjectId("4c8a331bda76c559ef000004")

// insert the document, using the new _id
db.clients.insert(doc)

// remove the document with the old _id
db.clients.remove({_id: ObjectId("4cc45467c55f4d2d2a000002")})
Niels van der Rest
quelle
29
Ein unterhaltsames Problem tritt auf, wenn ein Feld in diesem Dokument einen eindeutigen Index hat. In diesem Fall schlägt Ihr Beispiel fehl, da ein Dokument nicht mit einem doppelten Wert in ein eindeutiges indiziertes Feld eingefügt werden kann. Sie können dies beheben, indem Sie zuerst das Löschen durchführen. Dies ist jedoch eine schlechte Idee, da Ihre Daten jetzt verloren gehen, wenn Ihre Einfügung aus irgendeinem Grund fehlschlägt. Sie müssen stattdessen Ihren Index löschen, die Arbeit ausführen und dann den Index wiederherstellen.
Skelly
Guter Punkt @skelly! Ich habe zufällig über ähnliche Probleme nachgedacht und gesehen, dass Ihr neuer Kommentar erst vor 2 Stunden abgegeben wurde. Dieser Aufwand zum Ändern der ID wird also als ein intrinsisches Problem angesehen, das dadurch verursacht wird, dass der Benutzer die ID auswählen kann.
RayLuo
1
Wenn Sie eine duplicate key errorin der insertLeitung haben und sich keine Sorgen über das erwähnte Problem @skelly machen, besteht die einfachste Lösung darin, removezuerst die insertLeitung anzurufen und dann die Leitung anzurufen . Das docsollte bereits auf Ihrem Bildschirm gedruckt sein, damit es für einfache Dokumente leicht wiederhergestellt werden kann, im schlimmsten Fall, selbst wenn die Einfügung fehlschlägt.
Philfreo
Wenn Sie nur ObjectId () ohne die Zeichenfolge als Parameter verwenden, wird eine neue eindeutige generiert.
Erik
1
@ShankhadeepGhoshal Ja, das ist ein Risiko, insbesondere wenn Sie dies gegen ein Live-Produktionssystem ausführen. Leider denke ich, dass Ihre beste Option darin besteht, einen geplanten Ausfall zu nehmen und alle Autoren während dieses Prozesses zu stoppen. Eine andere Option, die etwas weniger schmerzhaft sein könnte, wäre, Anwendungen vorübergehend in einen schreibgeschützten Modus zu zwingen. Konfigurieren Sie alle Apps, die in die Datenbank schreiben, so, dass sie nur auf einen sekundären Knoten verweisen. Lesevorgänge sind erfolgreich, Schreibvorgänge schlagen jedoch während dieser Zeit fehl und Ihre Datenbank bleibt statisch.
Skelly
32

Um dies für Ihre gesamte Sammlung zu tun, können Sie auch eine Schleife verwenden (basierend auf dem Niels-Beispiel):

db.status.find().forEach(function(doc){ 
    doc._id=doc.UserId; db.status_new.insert(doc);
});
db.status_new.renameCollection("status", true);

In diesem Fall war UserId die neue ID, die ich verwenden wollte

Patrick Wolf
quelle
1
Würde ein Snapshot () bei find empfohlen, um zu verhindern, dass forEach versehentlich neuere Dokumente aufnimmt, während es iteriert?
John Flinchbaugh
Dieses Code-Snippet wird nie abgeschlossen. Es iteriert immer und immer wieder über die Sammlung. Der Schnappschuss macht nicht das, was Sie erwarten (Sie können ihn testen, indem Sie einen Schnappschuss machen, der der Sammlung ein Dokument hinzufügt und dann sieht, dass sich das neue Dokument im Schnappschuss befindet)
Patrick
Eine Alternative zum Snapshot finden Sie unter stackoverflow.com/a/28083980/305324 . list()ist die logische, aber für große Datenbanken kann dies den Speicher erschöpfen
Patrick
4
Äh, das hat 11 positive Stimmen, aber jemand sagt, es ist eine Endlosschleife? Was ist hier los?
Andrew
4
@ Andrew, weil die zeitgenössische Pop-Coder-Kultur vorschreibt, dass Sie immer gute Eingaben anerkennen sollten, bevor Sie tatsächlich überprüfen, ob diese Eingaben tatsächlich funktionieren.
CSvan
4

Falls Sie _id in derselben Sammlung umbenennen möchten (z. B. wenn Sie einige _ids voranstellen möchten):

db.someCollection.find().snapshot().forEach(function(doc) { 
   if (doc._id.indexOf("2019:") != 0) {
       print("Processing: " + doc._id);
       var oldDocId = doc._id;
       doc._id = "2019:" + doc._id; 
       db.someCollection.insert(doc);
       db.someCollection.remove({_id: oldDocId});
   }
});

if (doc._id.indexOf ("2019:")! = 0) {... wird benötigt, um eine Endlosschleife zu verhindern, da forEach die eingefügten Dokumente auch über die verwendete Methode .snapshot () auswählt .

Kennzeichen
quelle
find(...).snapshot is not a functionaber anders als das, tolle Lösung. Wenn Sie durch _idIhre benutzerdefinierte ID ersetzen möchten , können Sie auch überprüfen, ob doc._id.toString().length === 24die Endlosschleife verhindert werden soll (vorausgesetzt, Ihre benutzerdefinierten IDs sind nicht auch 24 Zeichen lang ).
Dan Dascalescu
1

Hier habe ich eine Lösung, die Mehrfachanforderungen für Schleifen und das Entfernen alter Dokumente vermeidet.

Sie können eine neue Idee einfach manuell erstellen, indem _id:ObjectId() Sie Folgendes verwenden: Wenn Sie jedoch wissen, dass Mongo automatisch eine _id zuweist, wenn diese fehlt, können Sie mithilfe des Aggregats eine erstellen, $projectdie alle Felder Ihres Dokuments enthält, aber das Feld _id weglassen. Sie können es dann mit speichern$out

Also, wenn Ihr Dokument ist:

{
"_id":ObjectId("5b5ed345cfbce6787588e480"),
"title": "foo",
"description": "bar"
}

Dann lautet Ihre Anfrage:

    db.getCollection('myCollection').aggregate([
        {$match:
             {_id: ObjectId("5b5ed345cfbce6787588e480")}
        }        
        {$project:
            {
             title: '$title',
             description: '$description'             
            }     
        },
        {$out: 'myCollection'}
    ])
Florent Arlandis
quelle
Interessante Idee ... aber normalerweise möchten Sie _idden Wert auf einen bestimmten Wert setzen, anstatt MongoDB einen anderen generieren zu lassen.
Dan Dascalescu
0

Sie können auch ein neues Dokument aus dem MongoDB-Kompass oder mit dem Befehl erstellen und den specific _idgewünschten Wert festlegen .

Talha Noyon
quelle