web-dev-qa-db-de.com

Wie aktualisiere ich ein Objekt in MongoDB teilweise, so dass das neue Objekt mit dem vorhandenen überlagert/zusammengeführt wird

Dieses Dokument wurde in MongoDB gespeichert 

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
   }
}

Ein Objekt mit neuen Informationen zu param2 und param3 aus der Außenwelt muss gespeichert werden

var new_info = {
    param2 : "val2_new",
    param3 : "val3_new"
};

Ich möchte die neuen Felder mit dem vorhandenen Status des Objekts zusammenführen/überlagern, sodass param1 nicht entfernt wird

Dies tun

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } } 

Dies führt dazu, dass MongoDB genau so läuft, wie es verlangt wurde, und setzt some_key auf diesen Wert. den alten ersetzen.

{
   _id : ...,
   some_key: { 
      param2 : "val2_new",
      param3 : "val3_new"
   }
}

Wie kann MongoDB nur neue Felder aktualisieren (ohne sie explizit anzugeben)? um das zu bekommen:

{
   _id : ...,
   some_key: { 
        param1 : "val1",
        param2 : "val2_new",
        param3 : "val3_new"
   }
}

Ich verwende den Java-Client, aber jedes Beispiel wird geschätzt

134
Eran Medan

Wenn ich die Frage richtig verstanden habe, möchten Sie ein Dokument mit dem Inhalt eines anderen Dokuments aktualisieren, jedoch nur mit den Feldern, die noch nicht vorhanden sind, und die bereits festgelegten Felder vollständig ignorieren (auch wenn auf einen anderen Wert gesetzt).

In einem einzigen Befehl ist dies nicht möglich.

Sie müssen zuerst das Dokument abfragen, herausfinden, was Sie $set wollen, und es anschließend aktualisieren (verwenden Sie die alten Werte als übereinstimmenden Filter, um sicherzustellen, dass Sie keine gleichzeitigen Aktualisierungen dazwischen erhalten).


Eine andere Lektüre Ihrer Frage wäre, dass Sie mit $set zufrieden sind, aber nicht alle Felder explizit festlegen möchten. Wie würdest du die Daten dann weitergeben?

Sie wissen, dass Sie Folgendes tun können:

db.collection.update(  { _id:...} , { $set: someObjectWithNewData } 
86
Thilo

Ich habe es mit meiner eigenen Funktion gelöst. Wenn Sie das angegebene Feld im Dokument aktualisieren möchten, müssen Sie es eindeutig ansprechen.

Beispiel:

{
    _id : ...,
    some_key: { 
        param1 : "val1",
        param2 : "val2",
        param3 : "val3"
    }
}

Wenn Sie nur param2 aktualisieren möchten, ist Folgendes falsch:

db.collection.update(  { _id:...} , { $set: { some_key : new_info  } }  //WRONG

Du musst benutzen:

db.collection.update(  { _id:...} , { $set: { some_key.param2 : new_info  } } 

Also habe ich so etwas geschrieben:

function _update($id, $data, $options=array()){

    $temp = array();
    foreach($data as $key => $value)
    {
        $temp["some_key.".$key] = $value;
    } 

    $collection->update(
        array('_id' => $id),
        array('$set' => $temp)
    );

}

_update('1', array('param2' => 'some data'));
88
mehmatrix

Sie können Punktnotation verwenden, um auf Felder tief in Objekten zuzugreifen und sie festzulegen, ohne die anderen Eigenschaften dieser Objekte zu beeinflussen.

In Anbetracht des oben angegebenen Objekts:

> db.test.insert({"id": "test_object", "some_key": {"param1": "val1", "param2": "val2", "param3": "val3"}})
WriteResult({ "nInserted" : 1 })

Wir können nur some_key.param2 und some_key.param3 aktualisieren:

> db.test.findAndModify({
... query: {"id": "test_object"},
... update: {"$set": {"some_key.param2": "val2_new", "some_key.param3": "val3_new"}},
... new: true
... })
{
    "_id" : ObjectId("56476e04e5f19d86ece5b81d"),
    "id" : "test_object",
    "some_key" : {
        "param1" : "val1",
        "param2" : "val2_new",
        "param3" : "val3_new"
    }
}

Sie können so tief eintauchen, wie Sie möchten. Dies ist auch hilfreich, wenn Sie einem Objekt neue Eigenschaften hinzufügen möchten, ohne die vorhandenen zu beeinflussen.

14
Samir Talwar

Die beste Lösung ist, Eigenschaften aus einem Objekt zu extrahieren und sie zu flachen Punktnotations-Schlüssel-Wert-Paaren zu machen. Sie können zum Beispiel diese Bibliothek verwenden: 

https://www.npmjs.com/package/mongo-dot-notation

Die .flatten-Funktion ermöglicht es Ihnen, das Objekt in einen flachen Satz von Eigenschaften zu ändern, der dann an den Modifizierer $ set übergeben werden kann, ohne dass befürchtet wird, dass eine Eigenschaft Ihres vorhandenen DB-Objekts ohne Notwendigkeit gelöscht/überschrieben wird.

Entnommen aus mongo-dot-notation docs:

var person = {
  firstName: 'John',
  lastName: 'Doe',
  address: {
    city: 'NY',
    street: 'Eighth Avenu',
    number: 123
  }
};



var instructions = dot.flatten(person)
console.log(instructions);
/* 
{
  $set: {
    'firstName': 'John',
    'lastName': 'Doe',
    'address.city': 'NY',
    'address.street': 'Eighth Avenu',
    'address.number': 123
  }
}
*/

Und dann bildet es den perfekten Selektor - es aktualisiert NUR die angegebenen Eigenschaften. EDIT: Ich mag es manchmal, Archäologe zu sein;)

10
SzybkiSasza

Mit Mongo können Sie verschachtelte Dokumente mit einer .-Konvention aktualisieren. Schauen Sie nach: Verschachtelte Dokumente in mongodb aktualisieren . Hier ist eine andere Frage aus der Vergangenheit über ein Merge-Update, wie zum Beispiel die, nach der Sie suchen, glaube ich: MongoDB Atom-Update über 'Merge'-Dokument

7
Pavel Veller

Ich hatte Erfolg auf diese Weise:

db.collection.update(  { _id:...} , { $set: { 'key.another_key' : new_info  } } );

Ich habe eine Funktion, die meine ProfilAktualisierungen dynamisch behandelt

function update(prop, newVal) {
  const str = `profile.${prop}`;
  db.collection.update( { _id:...}, { $set: { [str]: newVal } } );
}

Hinweis: "Profil" ist spezifisch für meine Implementierung, es ist nur die Zeichenfolge des Schlüssels, die Sie ändern möchten.

3
Matt Smith

Es sieht so aus, als könnten Sie isPartialObject setzen, um das zu erreichen, was Sie möchten.

1
Patrick Tescher

Ja, am besten konvertieren Sie die Objektnotation in eine flache Repräsentation von Schlüsselwerten, wie in diesem Kommentar erwähnt: https://stackoverflow.com/a/39357531/2529199

Ich wollte eine alternative Methode mit dieser NPM-Bibliothek hervorheben: https://www.npmjs.com/package/dot-object , mit der Sie verschiedene Objekte mit Punktnotation bearbeiten können.

Ich habe dieses Muster verwendet, um programmgesteuert eine verschachtelte Objekteigenschaft zu erstellen, wenn der Schlüsselwert wie folgt als Funktionsvariable akzeptiert wird:

const dot = require('dot-object');

function(docid, varname, varvalue){
  let doc = dot.dot({
      [varname]: varvalue 
  });

  Mongo.update({_id:docid},{$set:doc});
}

Mit diesem Muster kann ich verschachtelte und einstufige Eigenschaften austauschbar verwenden und sie sauber in Mongo einfügen.

Wenn Sie mit JS Objects jenseits von Mongo herumspielen müssen, insbesondere auf der Clientseite, aber bei der Arbeit mit Mongo konsistent sind, bietet Ihnen diese Bibliothek mehr Optionen als das zuvor erwähnte mongo-dot-notation NPM-Modul.

PS Ich wollte das ursprünglich nur als Kommentar erwähnen, aber anscheinend ist mein S/O-Vertreter nicht hoch genug, um einen Kommentar zu posten. Daher wollte ich nicht versuchen, SzybkiSaszas Kommentar zu kommentieren. Ich wollte nur ein alternatives Modul anbieten .

1
Ashwin Shankar

Wenn Sie _Mongo 4.2_, db.collection.update() starten, kann eine Aggregationspipeline akzeptiert werden, mit der Aggregationsoperatoren wie $addFields verwendet werden, die alle vorhandenen Felder aus den Eingabedokumenten ausgeben und neu hinzugefügte Felder:

_var new_info = { param2: "val2_new", param3: "val3_new" }

// { some_key: { param1: "val1", param2: "val2", param3: "val3" } }
// { some_key: { param1: "val1", param2: "val2"                 } }
db.collection.update({}, [{ $addFields: { some_key: new_info } }], { multi: true })
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
// { some_key: { param1: "val1", param2: "val2_new", param3: "val3_new" } }
_
  • Der erste Teil _{}_ ist die Übereinstimmungsabfrage, bei der gefiltert wird, welche Dokumente aktualisiert werden sollen (in diesem Fall alle Dokumente).

  • Der zweite Teil _[{ $addFields: { some_key: new_info } }]_ ist die Update-Aggregations-Pipeline:

    • Beachten Sie die eckigen Klammern, die die Verwendung einer Aggregationspipeline kennzeichnen.
    • Da dies eine Aggregationspipeline ist, können wir $addFields verwenden.
    • _$addFields_ führt genau das aus, was Sie benötigen: Aktualisieren des Objekts, sodass das neue Objekt mit dem vorhandenen Objekt überlagert/zusammengeführt wird:
    • In diesem Fall wird _{ param2: "val2_new", param3: "val3_new" }_ mit dem vorhandenen _some_key_ zusammengeführt, indem _param1_ unberührt bleibt und entweder beide _param2_ und _param3_ hinzugefügt oder ersetzt werden.
  • Vergiss nicht _{ multi: true }_, sonst wird nur das erste passende Dokument aktualisiert.

0
Xavier Guihot
    // where clause DBObject
    DBObject query = new BasicDBObject("_id", new ObjectId(id));

    // modifications to be applied
    DBObject update = new BasicDBObject();

    // set new values
    update.put("$set", new BasicDBObject("param2","value2"));

   // update the document
    collection.update(query, update, true, false); //3rd param->upsertFlag, 4th param->updateMultiFlag

Wenn Sie über mehrere Felder verfügen, die aktualisiert werden sollen

        Document doc = new Document();
        doc.put("param2","value2");
        doc.put("param3","value3");
        update.put("$set", doc);
0
Vino

Erstellen Sie ein Update-Objekt mit den Eigenschaftsnamen einschließlich des erforderlichen Punktpfads. ("somekey." + aus dem OP-Beispiel) und verwenden Sie dann das Update.

//the modification that's requested
updateReq = { 
   param2 : "val2_new",
   param3 : "val3_new"   
}

//build a renamed version of the update request
var update = {};
for(var field in updateReq){
    update["somekey."+field] = updateReq[field];
}

//apply the update without modifying fields not originally in the update
db.collection.update({._id:...},{$set:update},{upsert:true},function(err,result){...});
0
Andrew

Ich habe findAndModify() versucht, ein bestimmtes Feld in einem bereits vorhandenen Objekt zu aktualisieren.

https://docs.mongodb.com/manual/reference/method/db.collection.findAndModify/

0
nick-s

verwenden Sie $ set, um diesen Prozess auszuführen

.update({"_id": args.dashboardId, "viewData._id": widgetId}, {$set: {"viewData.$.widgetData": widgetDoc.widgetData}})
.exec()
.then(dashboardDoc => {
    return {
        result: dashboardDoc
    };
}); 
0
KARTHIKEYAN.A