So aktualisieren Sie mehrere Array-Elemente in Mongodb

181

Ich habe ein Mongo-Dokument, das eine Reihe von Elementen enthält.

Ich möchte das .handledAttribut aller Objekte im Array zurücksetzen, wobei .profile= XX.

Das Dokument hat die folgende Form:

{
    "_id": ObjectId("4d2d8deff4e6c1d71fc29a07"),
    "user_id": "714638ba-2e08-2168-2b99-00002f3d43c0",
    "events": [{
            "handled": 1,
            "profile": 10,
            "data": "....."
        } {
            "handled": 1,
            "profile": 10,
            "data": "....."
        } {
            "handled": 1,
            "profile": 20,
            "data": "....."
        }
        ...
    ]
}

Also habe ich folgendes versucht:

.update({"events.profile":10},{$set:{"events.$.handled":0}},false,true)

Es wird jedoch nur das erste übereinstimmende Array-Element in jedem Dokument aktualisiert . (Das ist das definierte Verhalten für $ - den Positionsoperator .)

Wie kann ich alle übereinstimmenden Array-Elemente aktualisieren ?

LiorH
quelle
2
Das Aktualisieren einer Teilmenge oder aller Array-Elemente wurde zu mongodb 3.6 hinzugefügt: docs.mongodb.com/manual/reference/operator/update/…
Jaap
Schauen Sie sich unbedingt arrayFilters an und überlegen Sie, welche Abfrage verwendet werden soll, um das Update effizient zu gestalten. Überprüfen Sie die Antwort von Neil Lunn: stackoverflow.com/a/46054172/337401
Jaap
Überprüfen Sie meine Antwort
Ucdemir

Antworten:

111

Derzeit ist es nicht möglich, mit dem Positionsoperator alle Elemente in einem Array zu aktualisieren. Siehe JIRA http://jira.mongodb.org/browse/SERVER-1243

Als Workaround können Sie:

  • Aktualisieren Sie jedes Element einzeln (events.0.handled events.1.handled ...) oder ...
  • Lesen Sie das Dokument, nehmen Sie die Änderungen manuell vor und speichern Sie es, indem Sie das ältere ersetzen (aktivieren Sie "Update if Current", wenn Sie atomare Updates sicherstellen möchten).
Javier Ferrero
quelle
15
Wenn Sie ein ähnliches Problem haben, stimmen Sie für dieses Problem - jira.mongodb.org/browse/SERVER-1243
LiorH
Ich mag das gelesene Dokument und den Speicheransatz. Aber ich habe Couch vor Mongo verwendet, so dass dieser Ansatz natürlicher erscheint, da es keine Abfrage-API für Couch gibt, nur eine REST-API für ganze Dokumente
Adam
1
Beide Ansätze erfordern ziemlich viel Speicher, oder? Wenn es viele Dokumente gibt, die durchsucht werden müssen und alle (oder die verschachtelten Arrays) laden müssen, um zu aktualisieren ... + auch etwas umständlich zu implementieren, wenn dies asynchron erfolgen muss ...
Ixx
12
Abgesehen von allen technischen Schwierigkeiten ist es ziemlich erstaunlich, dass diese Funktion in MongoDB nicht verfügbar ist. Diese Einschränkung nimmt dem Anpassen des Datenbankschemas viel Freiheit.
Sung Cho
5
Neil Lunn stackoverflow.com/a/46054172/337401 hat dies für Version 3.6 beantwortet. Da dies eine beliebte Frage ist, kann es sich lohnen, diese akzeptierte Antwort mit einem Verweis auf die Antwort von Neil Lunn zu aktualisieren.
Jaap
70

Mit der Veröffentlichung von MongoDB 3.6 (und verfügbar in der Entwicklungsabteilung von MongoDB 3.5.12) können Sie jetzt mehrere Array-Elemente in einer einzigen Anforderung aktualisieren.

Dies verwendet die in dieser Version eingeführte gefilterte$[<identifier>] Syntax des Positionsaktualisierungsoperators :

db.collection.update(
  { "events.profile":10 },
  { "$set": { "events.$[elem].handled": 0 } },
  { "arrayFilters": [{ "elem.profile": 10 }], "multi": true }
)

Die "arrayFilters"in Bezug auf die übergebenen Optionen für .update()oder auch .updateOne(), .updateMany(), .findOneAndUpdate()oder .bulkWrite()Verfahren werden die Bedingungen auf der Kennung in der Update - Anweisung gegeben entsprechen. Alle Elemente, die der angegebenen Bedingung entsprechen, werden aktualisiert.

Unter Hinweis darauf, dass das "multi"im Kontext der Frage angegebene in der Erwartung verwendet wurde, dass dies "mehrere Elemente aktualisieren" würde, dies jedoch nicht der Fall war und immer noch nicht der Fall ist. Die Verwendung hier gilt für "mehrere Dokumente", wie dies immer der Fall war oder jetzt .updateMany()in modernen API-Versionen als obligatorische Einstellung festgelegt wurde .

HINWEIS Ironischerweise ist .update()die Syntax im Allgemeinen mit allen neueren Treiberversionen kompatibel , da dies im Argument "options" für und ähnliche Methoden angegeben ist.

Dies gilt jedoch nicht für die mongoShell, da die Art und Weise, wie die Methode dort implementiert ist ("ironischerweise aus Gründen der Abwärtskompatibilität"), das arrayFiltersArgument nicht von einer internen Methode erkannt und entfernt wird, die die Optionen analysiert, um "Abwärtskompatibilität" mit Prior zu erzielen MongoDB-Serverversionen und eine "Legacy" .update()-API-Aufrufsyntax.

Wenn Sie den Befehl in der mongoShell oder anderen "Shell-basierten" Produkten (insbesondere Robo 3T) verwenden möchten, benötigen Sie eine neueste Version aus dem Entwicklungszweig oder der Produktionsversion ab Version 3.6.

Siehe auch, positional all $[]was auch "mehrere Array-Elemente" aktualisiert, ohne jedoch auf bestimmte Bedingungen anzuwenden, und auf alle Elemente im Array angewendet wird, bei denen dies die gewünschte Aktion ist.

Siehe auch Aktualisieren eines verschachtelten Arrays mit MongoDB, wie diese neuen Positionsoperatoren auf "verschachtelte" Array-Strukturen angewendet werden, bei denen sich "Arrays in anderen Arrays befinden".

WICHTIG - Aktualisierte Installationen von früheren Versionen haben möglicherweise "nicht" MongoDB-Funktionen aktiviert, was auch dazu führen kann, dass Anweisungen fehlschlagen. Sie sollten sicherstellen, dass Ihr Upgrade-Vorgang mit Details wie Index-Upgrades abgeschlossen ist, und dann ausführen

   db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )

Oder eine höhere Version, wie sie für Ihre installierte Version gilt. dh ab "4.0"Version 4 derzeit. Dies ermöglichte Funktionen wie die neuen Positionsaktualisierungsoperatoren und andere. Sie können auch überprüfen mit:

   db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

Rückgabe der aktuellen Einstellung

Neil Lunn
quelle
9
Die akzeptierte Antwort sollte aktualisiert werden und sich auf diese Antwort beziehen.
Jaap
2
Was ist elem?
user1063287
1
Das ist richtig. Beachten Sie, dass RoboMongo noch keine Unterstützung arrayFiltersbietet. Führen Sie daher das Update über die CLI aus. stackoverflow.com/questions/48322834/…
drlff
Vielen Dank, Neil, besonders für den WICHTIGEN Abschnitt, genau das, was ich brauchte
Janfabian
Dieser Code gibt ERROR in Pymongo zurück. Es ist ein Fehler: Erhöhen Sie TypeError ("% s muss wahr oder falsch sein"% (Option,)) TypeError: Upsert muss wahr oder falsch sein
Vagif
67

Was für mich funktioniert hat, war Folgendes:

db.collection.find({ _id: ObjectId('4d2d8deff4e6c1d71fc29a07') })
  .forEach(function (doc) {
    doc.events.forEach(function (event) {
      if (event.profile === 10) {
        event.handled=0;
      }
    });
    db.collection.save(doc);
  });

Ich denke, es ist klarer für Mongo-Neulinge und alle, die mit JQuery & Freunden vertraut sind.

Daniel Cerecedo
quelle
Ich benutze db.posts.find({ 'permalink':permalink }).forEach( function(doc) {...und ich bekommeOops.. TypeError: Object # has no method 'forEach'
Squirrl
3
@Squirrl könnte veraltete Mongodb-Version sein? Das Dokument enthält Informationen zum Anwenden der Funktion forEach auf einen Cursor, gibt jedoch nicht an, seitdem welche Version unterstützt wird. docs.mongodb.org/manual/reference/method/cursor.forEach
Daniel Cerecedo
@Squirrl versuchendb.posts.find(...).toArray().forEach(...)
Marmor
Können wir das nicht ohne verwenden Javascript? Ich möchte dieses Update direkt von einer Mongo-Shell aus ausführen, ohne die Javascript-API zu verwenden.
Meliodas
1
Können Sie diese Abfrage bitte im Mongodb-Treiber für Java oder mit Spring-Data-Mongodb schreiben? Danke, kris
chiku
18

Dies kann auch mit einer while-Schleife erreicht werden, die prüft, ob noch Dokumente vorhanden sind, deren Unterdokumente noch nicht aktualisiert wurden. Diese Methode bewahrt die Atomizität Ihrer Updates (was viele der anderen Lösungen hier nicht tun).

var query = {
    events: {
        $elemMatch: {
            profile: 10,
            handled: { $ne: 0 }
        }
    }
};

while (db.yourCollection.find(query).count() > 0) {
    db.yourCollection.update(
        query,
        { $set: { "events.$.handled": 0 } },
        { multi: true }
    );
}

Die Häufigkeit, mit der die Schleife ausgeführt wird, entspricht der maximalen Häufigkeit, mit der Unterdokumente mit profile10 und handledungleich 0 in einem der Dokumente in Ihrer Sammlung vorkommen. Wenn Sie also 100 Dokumente in Ihrer Sammlung haben und eines davon drei übereinstimmende Unterdokumente enthält queryund alle anderen Dokumente weniger übereinstimmende Unterdokumente haben, wird die Schleife dreimal ausgeführt.

Diese Methode vermeidet die Gefahr, dass andere Daten, die möglicherweise von einem anderen Prozess aktualisiert werden, während die Ausführung dieses Skripts aktualisiert wird, überlastet werden. Es minimiert auch die Datenmenge, die zwischen Client und Server übertragen wird.

Sean
quelle
13

Dies bezieht sich in der Tat auf das langjährige Problem unter http://jira.mongodb.org/browse/SERVER-1243, bei dem es tatsächlich eine Reihe von Herausforderungen für eine klare Syntax gibt, die "alle Fälle" unterstützt, in denen mehrere Array-Übereinstimmungen vorliegen gefunden. Es gibt bereits Methoden, die bei der Lösung dieses Problems "helfen", wie z. B. Massenoperationen, die nach diesem ursprünglichen Beitrag implementiert wurden.

Es ist immer noch nicht möglich, mehr als ein einzelnes übereinstimmendes Array-Element in einer einzelnen Update-Anweisung zu aktualisieren. Selbst mit einem "Multi" -Update können Sie also nur ein mathematisches Element im Array für jedes Dokument in diesem einzelnen aktualisieren Aussage.

Die derzeit bestmögliche Lösung besteht darin, alle übereinstimmenden Dokumente zu finden und zu schleifen und Massenaktualisierungen zu verarbeiten, sodass zumindest viele Vorgänge in einer einzigen Anforderung mit einer einzelnen Antwort gesendet werden können. Optional können Sie .aggregate()den im Suchergebnis zurückgegebenen Array-Inhalt auf diejenigen reduzieren, die den Bedingungen für die Update-Auswahl entsprechen:

db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$project": {
        "events": {
            "$setDifference": [
               { "$map": {
                   "input": "$events",
                   "as": "event",
                   "in": {
                       "$cond": [
                           { "$eq": [ "$$event.handled", 1 ] },
                           "$$el",
                           false
                       ]
                   }
               }},
               [false]
            ]
        }
    }}
]).forEach(function(doc) {
    doc.events.forEach(function(event) {
        bulk.find({ "_id": doc._id, "events.handled": 1  }).updateOne({
            "$set": { "events.$.handled": 0 }
        });
        count++;

        if ( count % 1000 == 0 ) {
            bulk.execute();
            bulk = db.collection.initializeOrderedBulkOp();
        }
    });
});

if ( count % 1000 != 0 )
    bulk.execute();

Der .aggregate()Teil dort funktioniert, wenn es eine "eindeutige" Kennung für das Array gibt oder der gesamte Inhalt für jedes Element selbst ein "eindeutiges" Element bildet. Dies ist auf den Operator "set" zurückzuführen, mit dem $setDifferencealle falseWerte gefiltert werden , die von der $mapOperation zurückgegeben wurden, mit der das Array auf Übereinstimmungen verarbeitet wurde.

Wenn Ihr Array-Inhalt keine eindeutigen Elemente enthält, können Sie einen alternativen Ansatz ausprobieren mit $redact:

db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$redact": {
        "$cond": {
            "if": {
                "$eq": [ { "$ifNull": [ "$handled", 1 ] }, 1 ]
            },
            "then": "$$DESCEND",
            "else": "$$PRUNE"
        }
    }}
])

Die Einschränkung besteht darin, dass, wenn "behandelt" tatsächlich ein Feld war, das auf anderen Dokumentebenen vorhanden sein soll, Sie wahrscheinlich nicht erwartete Ergebnisse erhalten. Dies ist jedoch in Ordnung, wenn dieses Feld nur an einer Dokumentposition angezeigt wird und eine Gleichheitsübereinstimmung darstellt.

Zukünftige Versionen (nach 3.1 MongoDB) werden zum Zeitpunkt des Schreibens eine $filtereinfachere Operation haben :

db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$project": {
        "events": {
            "$filter": {
                "input": "$events",
                "as": "event",
                "cond": { "$eq": [ "$$event.handled", 1 ] }
            }
        }
    }}
])

Alle Releases, die dies unterstützen, .aggregate()können den folgenden Ansatz verwenden $unwind, aber die Verwendung dieses Operators macht ihn aufgrund der Array-Erweiterung in der Pipeline zum am wenigsten effizienten Ansatz:

db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$unwind": "$events" },
    { "$match": { "events.handled": 1 } },
    { "$group": {
        "_id": "$_id",
        "events": { "$push": "$events" }
    }}        
])

In allen Fällen, in denen die MongoDB-Version einen "Cursor" aus der Gesamtausgabe unterstützt, müssen Sie lediglich einen Ansatz auswählen und die Ergebnisse mit demselben Codeblock iterieren, der zur Verarbeitung der Bulk-Update-Anweisungen angezeigt wird. Massenoperationen und "Cursor" aus der Gesamtausgabe werden in derselben Version (MongoDB 2.6) eingeführt und arbeiten daher normalerweise Hand in Hand für die Verarbeitung.

In noch früheren Versionen ist es wahrscheinlich am besten, nur .find()den Cursor zurückzugeben und die Ausführung von Anweisungen so herauszufiltern, wie oft das Array-Element für die .update()Iterationen übereinstimmt :

db.collection.find({ "events.handled": 1 }).forEach(function(doc){ 
    doc.events.filter(function(event){ return event.handled == 1 }).forEach(function(event){
        db.collection.update({ "_id": doc._id },{ "$set": { "events.$.handled": 0 }});
    });
});

Wenn Sie absolut entschlossen sind, "Multi" -Updates durchzuführen, oder dies als letztendlich effizienter erachten als die Verarbeitung mehrerer Updates für jedes übereinstimmende Dokument, können Sie immer die maximale Anzahl möglicher Array-Übereinstimmungen bestimmen und einfach ein "Multi" -Update ausführen, das so viele enthält Mal, bis im Grunde keine Dokumente mehr zu aktualisieren sind.

Ein gültiger Ansatz für MongoDB 2.4- und 2.2-Versionen könnte ebenfalls verwendet werden .aggregate(), um diesen Wert zu ermitteln:

var result = db.collection.aggregate([
    { "$match": { "events.handled": 1 } },
    { "$unwind": "$events" },
    { "$match": { "events.handled": 1 } },
    { "$group": {
        "_id": "$_id",
        "count": { "$sum": 1 }
    }},
    { "$group": {
        "_id": null,
        "count": { "$max": "$count" }
    }}
]);

var max = result.result[0].count;

while ( max-- ) {
    db.collection.update({ "events.handled": 1},{ "$set": { "events.$.handled": 0 }},{ "multi": true })
}

In jedem Fall gibt es bestimmte Dinge, die Sie im Rahmen des Updates nicht tun möchten:

  1. Aktualisieren Sie das Array nicht auf einmal: Wenn Sie der Meinung sind, dass es möglicherweise effizienter ist, den gesamten Array-Inhalt im Code und dann nur $setdas gesamte Array in jedem Dokument zu aktualisieren . Dies scheint schneller zu verarbeiten zu sein, es gibt jedoch keine Garantie dafür, dass sich der Array-Inhalt seit dem Lesen und der Aktualisierung nicht geändert hat. Obwohl $setes sich immer noch um einen atomaren Operator handelt, wird das Array nur mit den "Daten" aktualisiert, die es für "richtig" hält, und es ist daher wahrscheinlich, dass Änderungen zwischen Lesen und Schreiben überschrieben werden.

  2. Berechnen Sie keine zu aktualisierenden Indexwerte: Wenn Sie dem "One-Shot" -Ansatz ähnlich sind, berechnen Sie einfach, dass Position 0und Position 2(usw.) die Elemente sind, um diese zu aktualisieren und zu codieren, und eventuelle Anweisungen wie:

    { "$set": {
        "events.0.handled": 0,
        "events.2.handled": 0
    }}

    Auch hier besteht das Problem in der "Annahme", dass die beim Lesen des Dokuments gefundenen Indexwerte zum Zeitpunkt der Aktualisierung dieselben Indexwerte im Array sind. Wenn dem Array neue Elemente hinzugefügt werden, die die Reihenfolge ändern, sind diese Positionen nicht mehr gültig und die falschen Elemente werden tatsächlich aktualisiert.

Bis eine vernünftige Syntax für die Verarbeitung mehrerer übereinstimmender Array-Elemente in einer einzigen Aktualisierungsanweisung festgelegt ist, besteht der grundlegende Ansatz darin, entweder jedes übereinstimmende Array-Element in einer einzelnen Anweisung (idealerweise in Bulk) zu aktualisieren oder im Wesentlichen die maximalen Array-Elemente zu ermitteln um zu aktualisieren oder weiter zu aktualisieren, bis keine geänderten Ergebnisse mehr zurückgegeben werden. In jedem Fall sollten Sie "immer" Positionsaktualisierungen$ für das übereinstimmende Array-Element verarbeiten, auch wenn dadurch nur ein Element pro Anweisung aktualisiert wird.

Massenoperationen sind in der Tat die "verallgemeinerte" Lösung für die Verarbeitung von Operationen, die sich als "Mehrfachoperationen" herausstellen. Da es dafür mehr Anwendungen gibt als nur die Aktualisierung mehrerer Array-Elemente mit demselben Wert, wurde sie natürlich implementiert bereits, und es ist derzeit der beste Ansatz, um dieses Problem zu lösen.

Blakes Seven
quelle
8

Ich bin erstaunt, dass dies in Mongo immer noch nicht angesprochen wurde. Insgesamt scheint Mongo im Umgang mit Sub-Arrays nicht besonders gut zu sein. Sie können Sub-Arrays beispielsweise nicht einfach zählen.

Ich habe Javiers erste Lösung verwendet. Lesen Sie das Array in Ereignisse ein, durchlaufen Sie es und erstellen Sie das Set exp:

var set = {}, i, l;
for(i=0,l=events.length;i<l;i++) {
  if(events[i].profile == 10) {
    set['events.' + i + '.handled'] = 0;
  }
}

.update(objId, {$set:set});

Dies kann mithilfe eines Rückrufs für den bedingten Test in eine Funktion abstrahiert werden

lukenofurther
quelle
Danke dafür! Ich kann nicht glauben, dass diese Funktion von Haus aus immer noch nicht unterstützt wird! Verwenden Sie diese Option, um jedes Element eines Subarrays zu erhöhen, damit andere lesen können ... um jedes Element zu aktualisieren, entfernen Sie einfach die if-Anweisung.
Zaheer
9
Dies ist keine sichere Lösung. Wenn ein Datensatz hinzugefügt wird, während Sie das Update ausführen, werden Ihre Daten beschädigt.
Merc
4

Ich habe nach einer Lösung für dieses Problem mit dem neuesten Treiber für C # 3.6 gesucht und hier ist das Update, auf das ich mich schließlich festgelegt habe. Der Schlüssel hier ist die Verwendung von "$ []", das laut MongoDB ab Version 3.6 neu ist. Siehe https://docs.mongodb.com/manual/reference/operator/update/positional-all/#up. S [] für weitere Informationen.

Hier ist der Code:

{
   var filter = Builders<Scene>.Filter.Where(i => i.ID != null);
   var update = Builders<Scene>.Update.Unset("area.$[].discoveredBy");
   var result = collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = true});
}

Weitere Informationen finden Sie in meinem ursprünglichen Beitrag hier: Entfernen Sie das Array-Element mit dem MongoDB C # -Treiber aus ALLEN Dokumenten

C0d3 0n3
quelle
4

Der Thread ist sehr alt, aber ich habe hier nach einer Antwort gesucht, um eine neue Lösung zu finden.

Mit MongoDB Version 3.6+ ist es jetzt möglich, den Positionsoperator zu verwenden, um alle Elemente in einem Array zu aktualisieren. Siehe offizielle Dokumentation hier .

Die folgende Abfrage würde für die hier gestellte Frage funktionieren. Ich habe auch mit Java-MongoDB-Treiber überprüft und es funktioniert erfolgreich.

.update(   // or updateMany directly, removing the flag for 'multi'
   {"events.profile":10},
   {$set:{"events.$[].handled":0}},  // notice the empty brackets after '$' opearor
   false,
   true
)

Hoffe das hilft jemandem wie mir.

ersnh
quelle
1

Ich habe folgendes versucht und es funktioniert gut.

.update({'events.profile': 10}, { '$set': {'events.$.handled': 0 }},{ safe: true, multi:true }, callback function);

// Rückruffunktion bei Nodejs

Pranay Saha
quelle
Dieser Code aktualisiert nur das erste übereinstimmende Element im Array. Falsche Antwort.
Hamlett
Es funktioniert für v2.6. Sie sind möglicherweise in einer älteren Version? Hier ist sein Dokument docs.mongodb.com/manual/reference/method/db.collection.update/…
Jialin Zou
1

Sie können alle Elemente in MongoDB aktualisieren

db.collectioname.updateOne(
{ "key": /vikas/i },
{ $set: { 
 "arr.$[].status" : "completed"
} }
)

Der gesamte "Status" -Wert wird im "arr" -Array auf "abgeschlossen" aktualisiert

Wenn nur ein Dokument

db.collectioname.updateOne(
 { key:"someunique", "arr.key": "myuniq" },
 { $set: { 
   "arr.$.status" : "completed", 
   "arr.$.msgs":  {
                "result" : ""
        }
   
 } }
)

Wenn dies jedoch nicht der Fall ist und Sie nicht möchten, dass alle Dokumente im Array aktualisiert werden, müssen Sie das Element und den if-Block durchlaufen

db.collectioname.find({findCriteria })
  .forEach(function (doc) {
    doc.arr.forEach(function (singlearr) {
      if (singlearr check) {
        singlearr.handled =0
      }
    });
    db.collection.save(doc);
  });
VIKAS KOHLI
quelle
0

Tatsächlich ist der Befehl save nur für eine Instanz der Document-Klasse. Das hat viele Methoden und Attribute. Sie können also die Lean () - Funktion verwenden, um die Arbeitsbelastung zu reduzieren. Siehe hier. https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j

Ein weiteres Problem mit der Speicherfunktion, bei dem Konfliktdaten gleichzeitig mit Multi-Save erstellt werden. Model.Update erstellt Daten konsistent. So aktualisieren Sie mehrere Elemente in Array von Dokumenten. Verwenden Sie Ihre vertraute Programmiersprache und probieren Sie so etwas aus. Ich verwende Mungo darin:

User.findOne({'_id': '4d2d8deff4e6c1d71fc29a07'}).lean().exec()
  .then(usr =>{
    if(!usr)  return
    usr.events.forEach( e => {
      if(e && e.profile==10 ) e.handled = 0
    })
    User.findOneAndUpdate(
      {'_id': '4d2d8deff4e6c1d71fc29a07'},
      {$set: {events: usr.events}},
      {new: true}
    ).lean().exec().then(updatedUsr => console.log(updatedUsr))
})
user3176403
quelle
0

Der Operator $ [] wählt alle verschachtelten Arrays aus. Sie können alle Array-Elemente mit '$ []' aktualisieren.

.update({"events.profile":10},{$set:{"events.$[].handled":0}},false,true)

Referenz

Ucdemir
quelle
Können Sie bitte erklären, warum Sie hier am Ende das "Falsche, Wahre" einfügen? Ich kann es nicht in der Dokumentation finden.
Garson
Falsche Antwort Der Operator für alle Positionen $[] aktualisiert nur alle Felder im angegebenen Array. Was funktioniert, ist der gefilterte Positionsoperator $[identifier], der Operatoren in Arrayfeldern bearbeitet, die den angegebenen Bedingungen entsprechen. Sollte mit arrayFilters Refrence verwendet werden: docs.mongodb.com/manual/release-notes/3.6/#arrayfilters und docs.mongodb.com/manual/reference/operator/update/…
Lawrence Eagles
0

Bitte beachten Sie, dass einige Antworten in diesem Thread, die die Verwendung von $ [] vorschlagen, FALSCH sind.

db.collection.update(
   {"events.profile":10},
   {$set:{"events.$[].handled":0}},
   {multi:true}
)

Der obige Code aktualisiert "behandelt" für alle Elemente im Array "Ereignisse" auf 0, unabhängig von seinem Wert "Profil". Die Abfrage {"events.profile":10}dient nur zum Filtern des gesamten Dokuments, nicht der Dokumente im Array. In dieser Situation ist es ein Muss , verwenden , $[elem]mit arrayFiltersder Bedingung der Array - Elemente angeben , so Antwort Neil Lunns korrekt ist.

Wenda Hu
quelle
0

Aktualisieren Sie das Array-Feld in mehreren Dokumenten in mongo db.

Verwenden Sie $ pull oder $ push mit update many query, um Array-Elemente in mongoDb zu aktualisieren.

Notification.updateMany(
    { "_id": { $in: req.body.notificationIds } },
    {
        $pull: { "receiversId": req.body.userId }
    }, function (err) {
        if (err) {
            res.status(500).json({ "msg": err });
        } else {
            res.status(200).json({
                "msg": "Notification Deleted Successfully."
            });
        }
    });
Irfan
quelle
0

Erstens: Ihr Code hat nicht funktioniert, weil Sie den Positionsoperator verwendet haben, $der nur ein Element identifiziert, das in einem Array aktualisiert werden soll, aber nicht einmal explizit seine Position im Array angibt.

Was Sie brauchen, ist der gefilterte Positionsoperator $[<identifier>]. Es werden alle Elemente aktualisiert, die einer Array-Filterbedingung entsprechen.

Lösung:

db.collection.update({"events.profile":10}, { $set: { "events.$[elem].handled" : 0 } },
   {
     multi: true,
     arrayFilters: [ { "elem.profile": 10 } ]
})

Besuchen Sie mongodb doc hier

Was der Code macht:

  1. {"events.profile":10} filtert Ihre Sammlung und gibt die Dokumente zurück, die dem Filter entsprechen

  2. Der $setAktualisierungsoperator: Ändert übereinstimmende Felder von Dokumenten, auf die er reagiert.

  3. {multi:true}Es .update()ändert alle Dokumente, die mit dem Filter übereinstimmen, und verhält sich daher wie folgtupdateMany()

  4. { "events.$[elem].handled" : 0 } and arrayFilters: [ { "elem.profile": 10 } ] Diese Technik beinhaltet die Verwendung des gefilterten Positionsarrays mit arrayFilters. Das gefilterte Positionsarray $[elem]fungiert hier als Platzhalter für alle Elemente in den Arrayfeldern, die den im Arrayfilter angegebenen Bedingungen entsprechen.

Array-Filter

Lawrence Eagles
quelle