Dit is niet zo eenvoudig als je zou denken, en eigenlijk is het interessant dat je je analyse hiervan in drie delen hebt opgedeeld. Want, raad eens? Dat is precies wat u moet doen. Laten we de stappen eens bekijken:
1. Een document invoegen als er geen bestaat
db.collection.update(
{
"clientId":"123456"
},
{
"$setOnInsert": {
"clientId": "123456",
"devices": [{
"deviceId": "321",
"deviceType" : "kindle",
"notification" : false
}]
}
},
{ "upsert": true }
)
Dus wat je wilt doen is invoegen een nieuw document waar de "clientId" momenteel niet bestaat. Dit kan worden gedaan als een "upsert" om mogelijke unieke sleutelconflicten te voorkomen en zelfs als er geen "unieke" beperking is, zorgt de "upsert"-aard hiervan ervoor dat u alleen het "nieuwe" document maakt wanneer het niet werd gevonden. Er is ook $setOnInsert
hier omdat je niet iets wilt doen aan een document dat op dit moment is "gevonden".
Merk op dat er geen . is probeer het element in de array te matchen. Dit komt omdat u waarschijnlijk geen nieuw document wilt "maken", alleen omdat een bestaand document "dat" array-element niet had. Dat brengt ons bij de volgende stap.
2. Werk de inhoud van het document bij waar het bestaat
db.collection.update(
{
"clientId":"123456",
"devices": { "$elemMatch": { "deviceId" : "321" } }
},
{
"$set": {
"devices.$.deviceType" : "kindle",
"devices.$.notification" : false
}
}
)
Nu wil je hier het document daadwerkelijk proberen te "matchen" voor de "clientId" die doet bevatten een element in de array dat ook overeenkomt met de "deviceId" die u zoekt. Dus door een voorwaarde op te geven die moet overeenkomen, krijgt u het gebruik van de positionele $
operator om de velden in de "overeenkomstige" positie te zetten.
Zoals hierboven, zou dit ofwel overeenkomen met één ding of niets dus de update is gedaan of niet. Dus dat gaat naar ons laatste deel van de cascade hier:
3. Voeg het array-element toe waar het niet bestaat
db.collection.update(
{
"clientId":"123456"
},
{
"$addToset": { "devices": {
"deviceId" : "321",
"deviceType" : "kindle",
"notification" : false
}}
}
)
Dit is dus belangrijk de laatste fase. De reden hiervoor is dat als een van de voorgaande bewerkingen deed "create" of "update" het bestaande document, dan het gebruik van $addToSet
hier zorgt zeker u "duwt" geen ander document naar de array met dezelfde "deviceId" maar andere verschillende waarden. Als één van die fasen werkte, dan zou dit zien dat alle waarden van dat element al bestaan, en zou er dan geen nieuwe worden toegevoegd.
Als je zou proberen dat in een andere volgorde te doen, zou je in het geval dat je presenteert twee . hebben documenten in de array met dezelfde "deviceId", maar verschillende waarden voor "deviceType" en "notification". Dus daarom komt het als laatste.
Conclusie
Er is dus helaas geen eenvoudige manier om deze te combineren als één operatie. De operatoren bestaan gewoon niet, zodat dit in een enkele instructie zou kunnen worden gedaan en daarom moet u moet voer drie uit update-bewerkingen om te doen wat u wilt. Ook zoals vermeld, de bestelling van toepassing voor die updates is belangrijk zodat u het gewenste resultaat krijgt.
Hoewel dit nog niet bestaat in de huidige "productie"-releases, heeft de komende release (2.6 en hoger op het moment van schrijven) een manier om deze verzoeken te "batch" met een nieuwe syntaxis om bij te werken:
db.runCommand(
"update": "collection",
"updates": [
{
"q": { "clientId":"123456" },
"u": {
"$setOnInsert": {
"clientId": "123456",
"devices": [{
"deviceId": "321",
"deviceType" : "kindle",
"notification" : false
}]
},
"upsert": true
},
{
"q": {
"clientId":"123456",
"devices": { "$elemMatch": { "deviceId" : "321" } }
},
"u": {
"$set": {
"devices.$.deviceType" : "kindle",
"devices.$.notification" : false
}
}
},
{
"q": { "clientId":"123456" },
"u": {
"$addToset": { "devices": {
"deviceId" : "321",
"deviceType" : "kindle",
"notification" : false
}}
}
}
]
)
Dus zolang dat nog is in wezen drie bewerkingen, je kunt ze tenminste over de draad sturen slechts één keer