MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Aktualizowanie tablicy zagnieżdżonej wewnątrz tablicy mongodb

MongoDB 3.6 i nowsze

W MongoDB 3.6 i nowszych dostępna jest nowa funkcja, która pozwala aktualizować zagnieżdżone tablice za pomocą filtrowanego pozycyjnie $\[<identifier>\] składnia w celu dopasowania określonych elementów i zastosowania różnych warunków za pomocą arrayFilters w oświadczeniu o aktualizacji:

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

Dla MongoDB 3.4 i starszych:

Jak @wdberkeley wspomniał w swoim komentarzu:

MongoDB nie obsługuje dopasowywania do więcej niż jednego poziomu tablicy. Rozważ zmianę modelu dokumentu tak, aby każdy dokument reprezentował operację z informacjami wspólnymi dla zestawu operacji zduplikowanych w dokumentach operacyjnych.

Zgadzam się z powyższym i zalecam przeprojektowanie schematu, ponieważ silnik MongoDB nie obsługuje wielu operatorów pozycyjnych (Patrz Wielokrotne użycie pozycyjnego $ operator do aktualizacji zagnieżdżonych tablic )

Jeśli jednak znasz indeks tablicy operacji, która zawiera obiekt parametrów, który ma zostać zaktualizowany, zapytanie aktualizujące będzie wyglądało następująco:

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

EDYTUJ:

Jeśli chcesz utworzyć $set warunki w locie, tj. coś, co pomogłoby uzyskać indeksy obiektów, a następnie odpowiednio je zmodyfikować, a następnie rozważ użycie MapReduce .

Obecnie wydaje się, że nie jest to możliwe przy użyciu struktury agregacji. Istnieje nierozwiązany otwarty problem z JIRA powiązane z nim. Możliwe jest jednak obejście tego problemu dzięki MapReduce . Podstawową ideą MapReduce jest to, że używa JavaScript jako języka zapytań, ale jest to dość wolniejsze niż struktura agregacji i nie powinno być używane do analizy danych w czasie rzeczywistym.

W swojej operacji MapReduce musisz zdefiniować kilka kroków, tj. krok mapowania (który mapuje operację do każdego dokumentu w kolekcji, a operacja może albo nic nie robić, albo emitować jakiś obiekt z kluczami i przewidywanymi wartościami) i krok redukujący ( który pobiera listę emitowanych wartości i redukuje ją do jednego elementu).

W przypadku kroku mapy najlepiej byłoby uzyskać dla każdego dokumentu w kolekcji indeks dla każdej operations pole tablicy i inny klucz zawierający $set klawisze.

Krokiem redukcji będzie funkcja (która nic nie robi) zdefiniowana po prostu jako var reduce = function() {};

Ostatnim krokiem w operacji MapReduce będzie utworzenie oddzielnej operacji zbierania, która zawiera obiekt tablicy emitowanych operacji wraz z polem z $set warunki. Ta kolekcja może być okresowo aktualizowana po uruchomieniu operacji MapReduce na oryginalnej kolekcji. W sumie ta metoda MapReduce wyglądałaby następująco:

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

Zapytanie o zbiór danych wyjściowych operations z operacji MapReduce zazwyczaj daje wynik:

db.operations.findOne()

Wyjście :

{
    "_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"
        }
    }
}

Następnie możesz użyć kursora z db.operations.find() metoda iteracji i odpowiedniej aktualizacji kolekcji:

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


  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Wyszukiwanie zagnieżdżone MongoDB z 3 poziomami

  2. Problem z instalacją sterownika MongoDb PHP na wamp

  3. Przechowywanie wyliczeń jako ciągów w MongoDB

  4. Zapytanie Mongoose/mongoDB łączy się… ale pochodzę z tła sql

  5. Dopasowywanie ObjectId do String dla $graphLookup