MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Актуализиране на вложен масив вътре в масива mongodb

MongoDB 3.6 и по-нова версия

С MongoDB 3.6 и по-нова версия идва нова функция, която ви позволява да актуализирате вложени масиви, като използвате филтрирания по позиция $\[<identifier>\] синтаксис, за да се съпоставят конкретните елементи и да се прилагат различни условия чрез arrayFilters в изявлението за актуализиране:

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

За MongoDB 3.4 и по-стари:

Както @wdberkeley спомена в коментара си:

MongoDB не поддържа съвпадение в повече от едно ниво на масив. Помислете за промяна на модела на вашия документ, така че всеки документ да представлява операция, с информация, обща за набор от операции, дублирани в оперативните документи.

Съгласен съм с горното и бих препоръчал да преработите схемата си, тъй като механизмът на MongoDB не поддържа множество позиционни оператори (вижте Многократно използване на позиционния $ оператор за актуализиране на вложени масивии )

Въпреки това, ако знаете индекса на масива от операции, който има обекта на параметрите, който трябва да бъде актуализиран предварително, тогава заявката за актуализиране ще бъде:

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

РЕДАКТИРАНЕ:

Ако искате да създадете $set условия в движение, т.е. нещо, което ще ви помогне да получите индексите за обектите и след това да промените съответно, след което помислете за използването на MapReduce .

Понастоящем това изглежда не е възможно с помощта на рамката за агрегиране. Има неразрешен отворен проблем JIRA свързан с него. Въпреки това е възможно заобиколно решение с MapReduce . Основната идея с MapReduce е, че той използва JavaScript като свой език за заявки, но това е доста по-бавно от рамката за агрегиране и не трябва да се използва за анализ на данни в реално време.

Във вашата операция MapReduce трябва да дефинирате няколко стъпки, т.е. стъпката на картографиране (която преобразува операция във всеки документ в колекцията и операцията може или да не прави нищо, или да излъчва някакъв обект с ключове и прогнозирани стойности) и стъпка за намаляване ( който взема списъка с излъчени стойности и го свежда до един елемент).

За стъпката на картата в идеалния случай бихте искали да получите за всеки документ в колекцията, индекса за всяка operations поле на масив и друг ключ, който съдържа $set ключове.

Вашата стъпка за намаляване ще бъде функция (която не прави нищо), просто дефинирана като var reduce = function() {};

Последната стъпка във вашата операция MapReduce ще създаде отделни операции за събиране, които съдържат обекта на масива на излъчените операции заедно с поле с $set условия. Тази колекция може да се актуализира периодично, когато стартирате операцията MapReduce върху оригиналната колекция. Като цяло този метод MapReduce ще изглежда така:

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

Запитване на изходната колекция operations от операцията MapReduce обикновено ще ви даде резултат:

db.operations.findOne()

Изход :

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

След това можете да използвате курсора от db.operations.find() метод за повторение и съответно актуализиране на вашата колекция:

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. как да освободя кеширането, което се използва от Mongodb?

  2. Mongodb Aggregation Framework:$group използва ли индекс?

  3. Добавяне/изваждане на дни към ISODate в MongoDB Shell

  4. Как да управлявате документи в MongoDB

  5. Обновяване/поправяне на Mongoose?