MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Aktualisieren eines verschachtelten Arrays mit MongoDB

Allgemeiner Geltungsbereich und Erläuterung

Es gibt ein paar Dinge falsch mit dem, was Sie hier tun. Zunächst Ihre Abfragebedingungen. Sie beziehen sich auf mehrere _id Werte, die Sie nicht benötigen sollten, und von denen sich mindestens einer nicht auf der obersten Ebene befindet.

Um in einen "verschachtelten" Wert zu kommen und auch davon auszugehen, dass _id Wert eindeutig ist und in keinem anderen Dokument erscheinen würde, sollte Ihr Abfrageformular wie folgt aussehen:

Model.update(
    { "array1.array2._id": "123" },
    { "$push": { "array1.0.array2.$.answeredBy": "success" } },
    function(err,numAffected) {
       // something with the result in here
    }
);

Nun, das würde tatsächlich funktionieren, aber es ist wirklich nur ein Zufall, da es sehr gute Gründe gibt, warum es bei Ihnen nicht funktionieren sollte.

Die wichtige Lektüre ist in der offiziellen Dokumentation für die Position $ Operator unter dem Thema "Nested Arrays". Was das bedeutet, ist:

Der Positionsoperator $ kann nicht für Abfragen verwendet werden, die mehr als ein Array durchlaufen, wie z. B. Abfragen, die Arrays durchlaufen, die in anderen Arrays verschachtelt sind, da der Platzhalter $ durch einen einzelnen Wert ersetzt wird

Das bedeutet insbesondere, dass das Element, das abgeglichen und im Positionsplatzhalter zurückgegeben wird, der Wert des Index vom ersten ist passendes Array. Dies bedeutet in Ihrem Fall den passenden Index auf dem Array der "obersten" Ebene.

Wenn Sie sich also die Abfragenotation wie gezeigt ansehen, haben wir die erste "fest codiert". ( oder 0 index ) Position im Array der obersten Ebene, und es passiert einfach so, dass das übereinstimmende Element in "array2" auch der Null-Indexeintrag ist.

Um dies zu demonstrieren, können Sie die passende _id ändern Wert auf "124" und das Ergebnis wird $push sein ein neuer Eintrag auf das Element mit _id "123", da sie beide im Nullindexeintrag von "array1" stehen und das ist der Wert, der an den Platzhalter zurückgegeben wird.

Das ist also das allgemeine Problem beim Verschachteln von Arrays. Sie könnten eine der Ebenen entfernen und trotzdem $push ausführen zum richtigen Element in Ihrem "obersten" Array, aber es gäbe immer noch mehrere Ebenen.

Versuchen Sie, das Verschachteln von Arrays zu vermeiden, da Sie wie gezeigt auf Aktualisierungsprobleme stoßen werden.

Der allgemeine Fall besteht darin, die Dinge, von denen Sie glauben, dass sie „Ebenen“ sind, zu „glätten“ und tatsächlich diese „Attribute“ für die endgültigen Detailelemente zu erstellen. Beispielsweise sollte die "abgeflachte" Form der Struktur in der Frage so aussehen:

 {
   "answers": [
     { "by": "success", "type2": "123", "type1": "12" }
   ]
 }

Oder auch beim Akzeptieren des inneren Arrays ist $push nur und nie aktualisiert:

 {
   "array": [
     { "type1": "12", "type2": "123", "answeredBy": ["success"] },
     { "type1": "12", "type2": "124", "answeredBy": [] }
   ]
 }

Beide eignen sich für atomare Aktualisierungen im Rahmen des positionellen $ Betreiber

MongoDB 3.6 und höher

Ab MongoDB 3.6 sind neue Funktionen verfügbar, um mit verschachtelten Arrays zu arbeiten. Dies verwendet den positionsgefilterten $[<identifier>] Syntax, um die spezifischen Elemente abzugleichen und unterschiedliche Bedingungen durch arrayFilters anzuwenden in der Update-Anweisung:

Model.update(
  {
    "_id": 1,
    "array1": {
      "$elemMatch": {
        "_id": "12","array2._id": "123"
      }
    }
  },
  {
    "$push": { "array1.$[outer].array2.$[inner].answeredBy": "success" }
  },
  {
    "arrayFilters": [{ "outer._id": "12" },{ "inner._id": "123" }] 
  }
)

Die "arrayFilters" wie an die Optionen für .update() übergeben oder sogar.updateOne() , .updateMany() , .findOneAndUpdate() oder .bulkWrite() Die Methode gibt die Bedingungen an, die mit dem in der Update-Anweisung angegebenen Bezeichner abgeglichen werden müssen. Alle Elemente, die der angegebenen Bedingung entsprechen, werden aktualisiert.

Da die Struktur "verschachtelt" ist, verwenden wir tatsächlich "mehrere Filter", wie es mit einem "Array" von Filterdefinitionen wie gezeigt angegeben ist. Die markierte "Kennung" wird beim Abgleich mit dem nach Position gefilterten $[<identifier>] verwendet Syntax, die tatsächlich im Aktualisierungsblock der Anweisung verwendet wird. In diesem Fall inner und outer sind die Bezeichner, die für jede Bedingung verwendet werden, wie mit der verschachtelten Kette angegeben.

Diese neue Erweiterung ermöglicht die Aktualisierung von verschachtelten Array-Inhalten, hilft aber nicht wirklich bei der praktischen Anwendbarkeit des "Abfragens" solcher Daten, daher gelten die gleichen Vorbehalte wie zuvor erläutert.

Sie „wollen“ normalerweise wirklich als „Attribute“ ausdrücken, auch wenn Ihr Gehirn anfangs an „Verschachtelung“ denkt, ist dies normalerweise nur eine Reaktion darauf, wie Sie glauben, dass die „vorherigen relationalen Teile“ zusammenkommen. In Wirklichkeit brauchen Sie wirklich mehr Denormalisierung.

Siehe auch So aktualisieren Sie mehrere Array-Elemente in mongodb, da diese neuen Aktualisierungsoperatoren tatsächlich „mehrere Array-Elemente“ abgleichen und aktualisieren und nicht nur das erste , was die vorherige Aktion von Positionsaktualisierungen war.

HINWEIS Etwas ironisch, da dies im "options"-Argument für .update() angegeben ist und wie bei Methoden ist die Syntax im Allgemeinen mit allen neueren Treiberversionen kompatibel.

Dies gilt jedoch nicht für den mongo Shell, da die Art und Weise, wie die Methode dort implementiert ist ( "ironischerweise aus Gründen der Abwärtskompatibilität"), die arrayFilters -Argument wird nicht erkannt und von einer internen Methode entfernt, die die Optionen parst, um „Abwärtskompatibilität“ mit früheren MongoDB-Serverversionen und einem „alten“ .update() bereitzustellen API-Aufrufsyntax.

Wenn Sie also den Befehl in der mongo verwenden möchten Shell oder andere "Shell-basierte" Produkte (insbesondere Robo 3T ) benötigen Sie eine neueste Version entweder aus dem Entwicklungszweig oder eine Produktionsversion ab 3.6 oder höher.

Siehe auch positional all $[] was auch "mehrere Array-Elemente" aktualisiert, aber ohne Anwendung auf bestimmte Bedingungen und gilt für alle Elemente im Array, wo dies die gewünschte Aktion ist.