Ce n'est pas vraiment aussi simple que vous pourriez le penser, et en fait, il est intéressant que vous ayez divisé votre analyse en trois parties. Parce que, devinez quoi ? C'est exactement ce que vous devez faire. Considérons les étapes :
1. Insérer un document s'il n'en existe pas
db.collection.update(
{
"clientId":"123456"
},
{
"$setOnInsert": {
"clientId": "123456",
"devices": [{
"deviceId": "321",
"deviceType" : "kindle",
"notification" : false
}]
}
},
{ "upsert": true }
)
Donc, ce que vous voulez faire, c'est insérer un nouveau document où le "clientId" n'existe pas actuellement. Cela peut être fait comme un "upsert" pour éviter d'éventuels conflits de clés uniques et même lorsqu'il n'y a pas de contrainte "unique", alors la nature "upsert" de cela garantit que vous ne créez le "nouveau" document que lorsqu'il n'a pas été trouvé. Il y a aussi $setOnInsert
ici parce que vous ne le faites pas voulez faire quoi que ce soit à un document qui est "trouvé" à ce stade.
Notez ici qu'il n'y a non tenter de faire correspondre l'élément dans le tableau. C'est parce que vous ne voulez probablement pas "créer" un nouveau document simplement parce qu'un document existant n'avait pas "cet" élément de tableau. Ce qui nous amène à l'étape suivante.
2. Mettre à jour le contenu du document là où il existe
db.collection.update(
{
"clientId":"123456",
"devices": { "$elemMatch": { "deviceId" : "321" } }
},
{
"$set": {
"devices.$.deviceType" : "kindle",
"devices.$.notification" : false
}
}
)
Maintenant, ici, vous voulez réellement essayer de "faire correspondre" le document pour le "clientId" qui fait contenir un élément dans le tableau qui correspond également au "deviceId" que vous recherchez. Ainsi, en spécifiant une condition à respecter, vous obtenez l'utilisation du $
positionnel opérateur afin de mettre les champs en position "correspondant".
Comme ci-dessus, cela allait soit correspondre à un chose ou rien donc soit la mise à jour a été faite, soit elle ne l'a pas été. Cela passe donc à notre dernière partie de la cascade ici :
3. Ajouter l'élément de tableau là où il n'existe pas
db.collection.update(
{
"clientId":"123456"
},
{
"$addToset": { "devices": {
"deviceId" : "321",
"deviceType" : "kindle",
"notification" : false
}}
}
)
C'est donc important la dernière étape. La raison étant que si l'une des opérations précédentes a fait "créer" ou "mettre à jour" le document existant, puis l'utilisation de $addToSet
ici rend sûr vous ne "poussez" pas un autre document vers le tableau avec le même "deviceId" mais d'autres valeurs différentes. Si un de ces étapes ont fonctionné, alors cela verrait toutes les valeurs de cet élément exister déjà, et n'en ajouterait alors pas une autre.
Si vous essayiez de le faire dans un ordre différent, dans le cas que vous présentez, vous auriez deux documents dans le tableau avec le même "deviceId", mais des valeurs différentes pour "deviceType" et "notification". C'est pourquoi il vient en dernier.
Conclusion
Donc, malheureusement, il n'y a pas de moyen simple de les combiner en un opération. Les opérateurs n'existent tout simplement pas pour que cela puisse être fait dans une seule instruction et donc vous devez effectuer trois mettre à jour les opérations afin de faire ce que vous voulez. Aussi comme indiqué, la commande d'application pour ces mises à jour est important afin d'obtenir le résultat souhaité.
Bien que cela n'existe pas encore dans les versions de "production" actuelles, la prochaine version (2.6 et versions ultérieures au moment de la rédaction) a un moyen de "regrouper" ces requêtes avec une nouvelle syntaxe à mettre à jour :
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
}}
}
}
]
)
Alors pendant que c'est toujours essentiellement trois opérations, au moins vous pouvez les envoyer sur le fil juste une fois