MongoDB 3.6 en nieuwer
Met MongoDB 3.6 en hoger komt een nieuwe functie waarmee u geneste arrays kunt bijwerken met behulp van de positioneel gefilterde $\[<identifier>\]
syntaxis om de specifieke elementen te matchen en verschillende voorwaarden toe te passen via arrayFilters
in de update-verklaring:
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);
}
});
Voor MongoDB 3.4 en ouder:
Zoals @wdberkeley vermeldde in zijn opmerking:
MongoDB ondersteunt geen matching in meer dan één niveau van een array. Overweeg om uw documentmodel te wijzigen, zodat elk document een bewerking vertegenwoordigt, met informatie die gemeenschappelijk is voor een reeks bewerkingen die worden gedupliceerd in de bewerkingsdocumenten.
Ik ben het eens met het bovenstaande en zou aanraden om uw schema opnieuw te ontwerpen, aangezien de MongoDB-engine geen meerdere positionele operators ondersteunt (Zie Meervoudig gebruik van de positionele $
operator om geneste arrays bij te werken )
Als u echter de index kent van de operations-array met het parameterobject dat vooraf moet worden bijgewerkt, is de update-query:
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"
}
}
)
BEWERKEN:
Als u de $set
. wilt maken voorwaarden on-the-fly, d.w.z. iets dat u zou helpen de indexen voor de objecten te verkrijgen en dienovereenkomstig te wijzigen, overweeg dan om MapReduce te gebruiken .
Momenteel lijkt dit niet mogelijk te zijn met het aggregatieraamwerk. Er is een onopgelost open JIRA-probleem daaraan gekoppeld. Er is echter een tijdelijke oplossing mogelijk met MapReduce . Het basisidee van MapReduce is dat het JavaScript als zoektaal gebruikt, maar dit is meestal vrij langzamer dan het aggregatieraamwerk en mag niet worden gebruikt voor realtime gegevensanalyse.
In uw MapReduce-bewerking moet u een aantal stappen definiëren, d.w.z. de toewijzingsstap (die een bewerking toewijst aan elk document in de verzameling, en de bewerking kan niets doen of een object met sleutels en geprojecteerde waarden uitzenden) en reductiestap ( die de lijst met uitgezonden waarden neemt en deze reduceert tot een enkel element).
Voor de kaartstap zou u idealiter voor elk document in de verzameling de index willen krijgen voor elke operations
array veld en een andere sleutel die de $set
. bevat toetsen.
Uw reduceerstap zou een functie zijn (die niets doet) eenvoudig gedefinieerd als var reduce = function() {};
De laatste stap in uw MapReduce-bewerking maakt vervolgens een afzonderlijke verzamelingsbewerkingen die het uitgezonden array-object voor bewerkingen bevat, samen met een veld met de $set
voorwaarden. Deze verzameling kan periodiek worden bijgewerkt wanneer u de MapReduce-bewerking uitvoert op de oorspronkelijke verzameling. Al met al zou deze MapReduce-methode er als volgt uitzien:
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"
}
}
);
De uitvoerverzameling opvragen operations
van de MapReduce-bewerking geeft u meestal het resultaat:
db.operations.findOne()
Uitvoer :
{
"_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"
}
}
}
U kunt dan de cursor gebruiken uit de db.operations.find()
methode om uw verzameling te herhalen en dienovereenkomstig bij te werken:
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
);
};