Frage

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

Ich möchte das zurücksetzen .handled Attribut aller Objekte im Array wo .profile = XX.

Das Dokument hat 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, ich habe folgendes probiert:

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

Es aktualisiert jedoch nur die Erste übereinstimmendes Array-Element in jedem Dokument.(Das ist das definierte Verhalten für $ – der Positionsoperator.)

Wie kann ich aktualisieren alle übereinstimmende Array-Elemente?

War es hilfreich?

Lösung

In diesem Moment ist es nicht möglich, den Positions Operator zu verwenden, um alle Elemente in einem Array zu aktualisieren. Siehe JIRA http://jira.mongodb.org/browse/SERVER-1243

Als eine Arbeit um können Sie:

  • Aktualisieren Sie jedes Element einzeln (Events.0.handled events.1.handled ...) oder ...
  • Lesen Sie das Dokument, führen Sie die Änderungen manuell und speichern Sie die ersetzende ältere (überprüfen "Update, wenn Current " wenn Sie sicherstellen möchten, Atom-Updates)

Andere Tipps

Was für mich gearbeitet, war dies:

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 jedermann vertraut mit JQuery und Freunden.

Mit der Veröffentlichung von MongoDB 3.6 (und in der Entwicklungszweig von MongoDB 3.5.12) Sie können nun mehr Array-Elemente in einer einzigen Anfrage aktualisieren.

Dies verwendet die gefilterter Positions $[<identifier>] Update Operator Syntax in dieser Version eingeführt:

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 die Bedingung entsprechen gegeben werden aktualisiert.

Hinweis darauf, dass die "multi" wie er im Rahmen der Frage gegeben wurden, in der Erwartung, verwenden, dass diese „mehrere Elemente aktualisieren“ würde, aber dies war und ist immer noch nicht der Fall. Es ist lastet hier auf „mehrere Dokumente“ , wie der Fall oder jetzt anders als die verpflichtende Einstellung von .updateMany() in modernen API-Versionen.

Hinweis Ein wenig ironisch, da dies in den „Optionen“ Argument für .update() angegeben ist und wie Methoden, die Syntax ist im Allgemeinen kompatibel mit all aktuellen Release-Treiber-Versionen.

Dies ist jedoch nicht wahr des mongo Schals, da die Art und Weise der Methode dort implementiert ist ( „ironischerweise für die Abwärtskompatibilität“) das arrayFilters Argument wird durch ein internes Verfahren nicht erkannt und entfernt, die die Optionen, um analysiert zu liefern " Rückwärtskompatibilität“mit früheren MongoDB-Server-Versionen und einer "Legacy" .update() API-Aufruf-Syntax.

Also, wenn Sie den Befehl in dem mongo Shell oder anderen „Shell basierten“ verwenden mögen Produkte (insbesondere Robo 3T) benötigen Sie eine aktuelle Version entweder aus dem Entwicklungszweig oder Produktions-Release als 3,6 oder größer ist.

Siehe auch positional all $[] die auch aktualisiert „mehr Array Elemente“, aber ohne auf bestimmte Bedingungen der Anwendung und bezieht sich auf all Elemente in dem Array, in dem die die gewünschte Wirkung ist.

Siehe auch eine verschachtelte Array mit MongoDB aktualisiert , wie diese neuen Positions Operatoren gelten für „verschachtelte“ Array-Strukturen, in denen "Arrays sind in anderen Arrays".

Wichtig - Verbesserte Installationen von früheren Versionen haben „kann“ nicht aktiviert MongoDB Features, die auch Ursache Aussagen zum Scheitern verurteilt. Sie sollten Ihr Upgrade-Verfahren ist komplett mit Details wie Index-Upgrades und führte

gewährleisten
   db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )

oder eine höhere Version als zu Ihrer installierten Version anwendbar ist. d.h "4.0" für Version 4 und ab heute. Dies ermöglichte es solche Eigenschaften wie die neuen Positions Update Operatoren und andere. Sie können auch überprüfen mit:

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

Um die aktuelle Einstellung zurück

Dies kann auch mit einer while-Schleife erreicht werden, die überprüft, ob alle Dokumente bleiben, dass noch Subdokumente, die nicht aktualisiert worden sind. Diese Methode bewahrt die Unteilbarkeit Ihres Updates (die viele der anderen Lösungen hier nicht).

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 Anzahl der Male die Schleife ausgeführt wird, wird die maximale Anzahl von Mal Subdokumente mit profile gleich gleich 10 und handled nicht gleich 0 in eines der Dokumente in Ihrer Sammlung auftreten. Also, wenn Sie 100 Dokumente in Ihrer Sammlung haben und einer von ihnen hat drei Subdokumente dass Spiel query und alle anderen Dokumente weniger passende Subdokumente haben, wird die Schleife dreimal ausgeführt werden.

Dieses Verfahren vermeidet die Gefahr von clobbering anderen Daten, die von einem anderen Prozess während dieses Skript aus aktualisiert werden können. Es minimiert auch die Menge der Daten zwischen Client und Server übertragen werden.

Dies ist in der Tat zum althergebrachten Problem bezieht sich auf http://jira.mongodb.org / browse / SERVER-1243 , wo es in der Tat eine Reihe von Herausforderungen zu einer klaren Syntax ist, dass Träger „alle Fälle“, wo sind mutiple Array Spiele gefunden. Es gibt in der Tat Methoden bereits an Ort und Stelle, dass „Hilfe“ in Lösungen für dieses Problem, wie Massenvorgänge , die nach diesem ursprünglichen Beitrag umgesetzt wurden.

Es ist noch nicht möglich, mehr als ein einzelne abgestimmt Array-Element in einer einzigen Update-Anweisung zu aktualisieren, so dass auch mit einem „multi“ aktualisiert alles, was Sie jemals zu aktualisieren können, ist nur ein Mathed Element in dem Feld für jedes Dokument in dieser einzigen Anweisung.

Die bestmögliche Lösung gegenwärtig zu finden und Schleife alle passenden Dokumente und Prozess Bulk-Updates, die zumindest können viele Operationen in einer einzigen Anfrage mit einer einzigartigen Antwort gesendet werden. Optional können Sie verwenden .aggregate() das Array Gehalt zu reduzieren zurück in das Suchergebnis auf nur diejenigen, die die Voraussetzungen für die Aktualisierung 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() Abschnitt dort eingesetzt werden, wenn es ein „einzigartiger“ ist Identifikator für die Anordnung oder alle Inhalte für jedes Element bildet ein „einzigartiges“ Element selbst. Dies ist auf die „Set“ -Operator in $setDifference verwendet, um Filter etwaige false Werte aus der $map Operation verwendet, um das Array zu verarbeiten für Streichhölzer.

Wenn Ihr Array Inhalt nicht eindeutig zuzuordnen Elemente nicht haben, können Sie einen alternativen Ansatz versuchen, mit $redact :

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

Dabei gilt es Beschränkung ist, dass, wenn „behandelt“ in der Tat war ein Feld bedeutete in anderen Dokumentebenen vorhanden sein, dann werden Sie wahrscheinlich unexepected Ergebnisse zu erhalten, aber es ist in Ordnung, wo das Feld nur in einem Dokument Position angezeigt wird, und ist eine Gleichheit Spiel.

Zukünftige Versionen (Beitrag 3.1 MongoDB) zum Schreiben einen $filter Betrieb hat, die einfacher ist:

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

Und alle Meldungen, dass die Unterstützung .aggregate() kann den folgenden Ansatz verwenden, um mit $unwind , aber die Verwendung von diesem Betreiber macht es die am wenigsten effizienten Ansatz aufgrund der Array-Erweiterung in der Pipeline:

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 Gesamtleistung unterstützt, dann ist dies nur eine Frage des einen Ansatz wählen und die Ergebnisse mit dem gleichen Code-Block iteriert gezeigt, um die Bulk-Update-Anweisungen zu verarbeiten. Massenvorgänge und „Cursor“ aus Gesamtleistung in der gleichen Version (MongoDB 2.6) eingeführt und damit in der Regel arbeitet Hand in Hand für die Verarbeitung.

In noch frühere Versionen dann ist es wahrscheinlich am besten, nur Gebrauch .find() die Cursor zurückzukehren und Filter aus der Ausführung von Anweisungen nur auf die Anzahl, wie oft das Array-Element für die .update() Iterationen abgestimmt ist:

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 aboslutely bestimmt sind „multi“ Updates zu tun oder dass erachten letztlich effizienter zu sein als die Verarbeitung mehr Updates für jedes Dokument abgestimmt, dann können Sie immer die maximale Anzahl der möglichen Array-Matches bestimmen und nur einen „multi“ ausführen Update, das viele Male, bis im Grunde gibt es nicht mehr Dokumente zu aktualisieren sind.

Ein gültiger Ansatz für MongoDB 2.4 und 2.2 Versionen auch .aggregate() verwenden könnte diesen Wert zu finden:

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

Wie auch immer, gibt es bestimmte Dinge, die Sie tun nicht wollen innerhalb der Aktualisierung tun:

  1. Do not "one shot" aktualisieren das Array: Wo, wenn Sie denken, es könnte effizienter sein, das gesamte Array Inhalt in Code zu aktualisieren und dann $set nur das gesamte Array in jedem Dokument . Dies könnte schneller Prozess sein, aber es gibt keine Garantie, dass der Array Inhalt, da es wurde nicht geändert hat gelesen und das Update durchgeführt wird. Obwohl $set noch ein Atom Operator ist, wird es nur um das Array zu aktualisieren mit dem, was er „denkt“ die richtigen Daten ist, und somit ist wahrscheinlich keine Änderungen überschrieben zwischen Lese- und Schreib auftreten.

  2. Do not berechnen Indexwerte Update: Wo ähnlich dem "one shot" nähern Sie trainieren gerade diese Position 0 und Position 2 (und so weiter) sind die Elemente zu aktualisieren und Code diese in mit und eventuelle Aussage wie:

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

    Auch das Problem hier ist die „Vermutung“, dass diese Indexwerte gefunden, wenn das Dokument der gleichen Indexwert in th Array sind zum Zeitpunkt der Aktualisierung gelesen wurde. Wenn neue Objekte werden in einer Art und Weise zu dem Array hinzugefügt, die die Reihenfolge ändert sich dann diese Positionen nicht mehr gültig ist und die falschen Einzelteile tatsächlich aktualisiert werden.

Also, bis es eine vernünftige Syntax zu ermöglichen, dass mehrere abgestimmte Array-Elemente bestimmt wird dann in einzelner Update-Anweisung zu verarbeiten ist der grundlegende Ansatz zu jeder Aktualisierung jeden angepasste Array-Element in einer indvidual Aussage (idealerweise in loser Schüttung) oder im Wesentlichen funktioniert die maximal Arrayelemente zu aktualisieren oder zu halten, bis keine Aktualisierung mehr modifizierte Ergebnisse zurückgegeben. Auf jeden Fall sollten Sie „immer“ seine Verarbeitung Positions $ Updates auf dem Array-Elemente angepasst, auch wenn diese nur aktualisiert ein Element pro Anweisung.

Massenvorgänge in der Tat sind die „verallgemeinerte“ Lösung alle Vorgänge zu verarbeiten, dass die Arbeit aus „mehrere Operationen“ zu sein, und da es mehr Anwendungen hierfür sind als nur mutiple Array-Elemente mit dem gleichen Wert aktualisiert wird, dann hat sie von natürlich wurde bereits umgesetzt, und es ist derzeit der beste Ansatz, um dieses Problem zu lösen.

Ich bin das erstaunt immer noch nicht beantwortet wurde in Mongo. Insgesamt Mongo scheint nicht groß zu sein, wenn sie mit Subanordnungen handelt. Sie können nicht Subanordnungen einfach zum Beispiel zählen.

habe ich erste Lösung Javier. Lesen des Arrays in Ereignisse dann eine Schleife durch und bauen die 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 in eine Funktion abstrahiert werden, um einen Rückruf für den bedingten Test mit

Ich habe für eine Lösung dieses Problems suchen, um die neuesten Treiber für C # 3.6 verwenden und hier ist das Update ich schließlich ließ sich auf. Der Schlüssel hier ist mit "$ []" , die nach MongoDB ist neu ab Version 3.6. Siehe https: //docs.mongodb. com / manual / reference / Operator / update / Positions-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});
}

Für mehr Kontext meiner ursprünglichen Beitrag sehen hier: Remove Array-Element aus allen Dokumenten mit MongoDB C # Treiber

Ich habe versucht, die folgende und seine Arbeits in Ordnung.

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

// Callback-Funktion bei NodeJS

Der Faden ist sehr alt, aber ich kam nach Antworten suchen hier daher neue Lösung.

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

Nach der Abfrage funktionieren würde, für die Frage hier gefragt. 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
)

Hope dies hilft jemand wie ich.

Tatsächlich gilt der Speicherbefehl nur für eine Instanz der Document-Klasse.Das hat viele Methoden und Attribute.So können Sie verwenden mager() Funktion zur Reduzierung der Arbeitsbelastung.Siehe hier. https://hashnode.com/post/why-are-mongoose-mongodb-odm-lean-queries-faster-than-normal-queries-cillvawhq0062kj53asxoyn7j

Ein weiteres Problem mit der Speicherfunktion besteht darin, dass bei gleichzeitiger Mehrfachspeicherung Datenkonflikte entstehen.Modell.Update wird Daten konsistent machen.So aktualisieren Sie mehrere Elemente im Array eines Dokuments.Verwenden Sie Ihre vertraute Programmiersprache und versuchen Sie etwas wie Folgendes. Ich verwende dabei Mongoose:

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

$ Operator [] wählt alle verschachtelten Array ..You alle Array-Elemente aktualisieren kann mit '$ []

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

Referenz

Bitte beachten Sie, dass einige Antworten in diesem Thread Verwendung $ darauf hindeutet [] ist falsch.

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

Der obige Code wird aktualisiert „behandelt“ auf 0 für alle Elemente in „Event“ Array, unabhängig von seinem „Profil“ Wert. Die Abfrage {"events.profile":10} ist nur das gesamte Dokument zu filtern, die Dokumente nicht in der Anordnung. In dieser Situation ist es ein Muss, um den Einsatz $[elem] mit arrayFilters den Zustand der Array-Elemente angeben, so Antwort Neil Lunns korrekt ist.

Ich will nur eine andere Lösung hinzuzufügen, die für mich gearbeitet und ist ziemlich einfach. Hier ist es nur ein Array von Tags (Strings) so einen Tag zu aktualisieren namens „test“ auf „geändert“, nur dies tun:

myDocuments.find({tags: "test" }, {fields: {_id: 1}}).forEach(function (doc) {
    myDocuments.update(
        {_id: doc._id, tags: "test"}, 
        {$set:{'tags.$': "changed"}});
    });
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top