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

Aktualisieren des verschachtelten Arrays im Array mongodb

MongoDB 3.6 und neuer

Mit MongoDB 3.6 und höher gibt es eine neue Funktion, mit der Sie verschachtelte Arrays aktualisieren können, indem Sie den positionsgefilterten $\[<identifier>\] verwenden Syntax, um die spezifischen Elemente abzugleichen und unterschiedliche Bedingungen durch arrayFilters anzuwenden in der Update-Anweisung:

const { oid, pid } = req.params;
const { name, oName, description, type } = req.body; 

collection.update(
    {
        "_id": 1,
        "operations": {
            "$elemMatch": {
                oid, "parameters.pid": pid
            }
        }
    },
    { "$set": { 
        "operations.$[outer].parameters.$[inner].name": name,
        "operations.$[outer].parameters.$[inner].description": description,
        "operations.$[outer].parameters.$[inner].oName": oName,
        "operations.$[outer].parameters.$[inner].type": type 
    } },
    { "arrayFilters": [
        { "outer.oid": oid },
        { "inner.pid": pid }
    ] }, (err, result) => {
    if (err) {
        console.log('Error updating service: ' + err);
        res.send({'error':'An error has occurred'});
    } else {
        // console.log('' + result + ' document(s) updated');
        res.send(result);
    }
});

Für MongoDB 3.4 und älter:

Wie @wdberkeley in seinem Kommentar erwähnte:

MongoDB unterstützt keinen Abgleich in mehr als einer Ebene eines Arrays. Ziehen Sie in Betracht, Ihr Dokumentmodell so zu ändern, dass jedes Dokument eine Operation darstellt, wobei Informationen, die einer Reihe von Operationen gemeinsam sind, in den Operationsdokumenten dupliziert werden.

Ich stimme dem oben Gesagten zu und würde empfehlen, Ihr Schema neu zu entwerfen, da die MongoDB-Engine mehrere Positionsoperatoren nicht unterstützt (siehe Mehrfachverwendung des Positionsoperators $ Operator zum Aktualisieren verschachtelter Arrays )

Wenn Sie jedoch den Index des Operations-Arrays kennen, das das zu aktualisierende Parameterobjekt enthält, lautet die Aktualisierungsabfrage:

db.collection.update(
    {
        "_id" : "04", 
        "operations.parameters.pid": "011"
    }, 
    {
        "$set": { 
            "operations.0.parameters.$.name": "foo",
            "operations.0.parameters.$.description": "bar", 
            "operations.0.parameters.$.type": "foo" 
        }
    }
)

BEARBEITEN:

Wenn Sie die Datei $set erstellen möchten Bedingungen im laufenden Betrieb, d.h. etwas, das Ihnen helfen würde, die Indizes für die Objekte zu erhalten und dann entsprechend zu ändern, dann ziehen Sie die Verwendung von MapReduce in Betracht .

Derzeit scheint dies mit dem Aggregation Framework nicht möglich zu sein. Es gibt ein ungelöstes offenes JIRA-Problem daran geknüpft. Mit MapReduce ist jedoch ein Workaround möglich . Die Grundidee von MapReduce ist, dass es JavaScript als Abfragesprache verwendet, aber dies ist tendenziell ziemlich langsamer als das Aggregations-Framework und sollte nicht für die Echtzeit-Datenanalyse verwendet werden.

In Ihrer MapReduce-Operation müssen Sie einige Schritte definieren, d. h. den Zuordnungsschritt (der eine Operation jedem Dokument in der Sammlung zuordnet, und die Operation kann entweder nichts tun oder ein Objekt mit Schlüsseln und projizierten Werten ausgeben) und den Reduktionsschritt ( die die Liste der ausgegebenen Werte auf ein einziges Element reduziert).

Für den Zuordnungsschritt möchten Sie idealerweise für jedes Dokument in der Sammlung den Index für jede operations erhalten Array-Feld und ein weiterer Schlüssel, der den $set enthält Tasten.

Ihr Reduzierschritt wäre eine Funktion (die nichts tut), die einfach als var reduce = function() {}; definiert ist

Der letzte Schritt in Ihrer MapReduce-Operation erstellt dann eine separate Sammlungsoperation, die das ausgegebene Operations-Array-Objekt zusammen mit einem Feld mit dem $set enthält Bedingungen. Diese Sammlung kann regelmäßig aktualisiert werden, wenn Sie die MapReduce-Operation für die ursprüngliche Sammlung ausführen. Insgesamt würde diese MapReduce-Methode wie folgt aussehen:

var map = function(){
    for(var i = 0; i < this.operations.length; i++){
        emit( 
            {
                "_id": this._id, 
                "index": i 
            }, 
            {
                "index": i, 
                "operations": this.operations[i],            
                "update": {
                    "name": "operations." + i.toString() + ".parameters.$.name",
                    "description": "operations." + i.toString() + ".parameters.$.description",
                    "type": "operations." + i.toString() + ".parameters.$.type"
                }                    
            }
        );
    }
};

var reduce = function(){};

db.collection.mapReduce(
    map,
    reduce,
    {
        "out": {
            "replace": "operations"
        }
    }
);

Abfragen der operations der Ausgabesammlung aus der MapReduce-Operation liefert normalerweise das Ergebnis:

db.operations.findOne()

Ausgabe :

{
    "_id" : {
        "_id" : "03",
        "index" : 0
    },
    "value" : {
        "index" : 0,
        "operations" : {
            "_id" : "96",
            "oName" : "test op 52222222222",
            "sid" : "04",
            "name" : "test op 52222222222",
            "oid" : "99",
            "description" : "testing",
            "returntype" : "test",
            "parameters" : [ 
                {
                    "oName" : "Param1",
                    "name" : "foo",
                    "pid" : "011",
                    "type" : "foo",
                    "description" : "bar",
                    "value" : ""
                }, 
                {
                    "oName" : "Param2",
                    "name" : "Param2",
                    "pid" : "012",
                    "type" : "58222",
                    "description" : "testing",
                    "value" : ""
                }
            ]
        },
        "update" : {
            "name" : "operations.0.parameters.$.name",
            "description" : "operations.0.parameters.$.description",
            "type" : "operations.0.parameters.$.type"
        }
    }
}

Sie können dann den Cursor aus db.operations.find() verwenden Methode zum Iterieren und Aktualisieren Ihrer Sammlung entsprechend:

var oid = req.params.operations;
var pid = req.params.parameters;
var cur = db.operations.find({"_id._id": oid, "value.operations.parameters.pid": pid });

// Iterate through results and update using the update query object set dynamically by using the array-index syntax.
while (cur.hasNext()) {
    var doc = cur.next();
    var update = { "$set": {} };
    // set the update query object
    update["$set"][doc.value.update.name] = req.body.name;
    update["$set"][doc.value.update.description] = req.body.description;
    update["$set"][doc.value.update.type] = req.body.type;

    db.collection.update(
        {
            "_id" : oid, 
            "operations.parameters.pid": pid
        }, 
        update 
    );
};