Isso não é tão simples quanto você pode pensar, e na verdade é interessante que você tenha dividido sua análise em três partes. Porque, adivinhem? Isso é exatamente o que você deve Faz. Vamos considerar as etapas:
1. Insira um documento se não existir
db.collection.update(
{
"clientId":"123456"
},
{
"$setOnInsert": {
"clientId": "123456",
"devices": [{
"deviceId": "321",
"deviceType" : "kindle",
"notification" : false
}]
}
},
{ "upsert": true }
)
Então o que você quer fazer é inserir um novo documento onde o "clientId" não existe atualmente. Isso pode ser feito como um "upsert" para evitar possíveis confrontos de chaves exclusivas e mesmo quando não houver restrição "única", a natureza "upsert" disso garante que você crie apenas o "novo" documento quando ele não for encontrado. Também há
$setOnInsert
aqui porque você não deseja fazer qualquer coisa com um documento que é "encontrado" neste momento. Observe aqui que não há não tente corresponder ao elemento na matriz. Isso ocorre porque você provavelmente não deseja "criar" um novo documento apenas porque um existente não possui "aquele" elemento de matriz. O que nos leva ao próximo passo.
2. Atualize o conteúdo do documento onde ele existir
db.collection.update(
{
"clientId":"123456",
"devices": { "$elemMatch": { "deviceId" : "321" } }
},
{
"$set": {
"devices.$.deviceType" : "kindle",
"devices.$.notification" : false
}
}
)
Agora, aqui você quer realmente tentar "combinar" o documento com o "clientId" que faz contém um elemento na matriz que também corresponde ao "deviceId" que você está procurando. Assim, especificando uma condição para corresponder, você obtém o uso da posição posicional
$
operador para colocar os campos na posição "correspondente". Como acima, isso corresponderia a um coisa ou nada então ou a atualização foi feita ou não foi. Então, isso se move para nossa parte final da cascata aqui:
3. Adicione o elemento array onde ele não existe
db.collection.update(
{
"clientId":"123456"
},
{
"$addToset": { "devices": {
"deviceId" : "321",
"deviceType" : "kindle",
"notification" : false
}}
}
)
Então isso é importante a última etapa. A razão é que, se uma das operações anteriores fez "criar" ou "atualizar" o documento existente, então o uso de
$addToSet
aqui garante certeza você não está "enviando" outro documento para a matriz com o mesmo "deviceId", mas outros valores diferentes. Se um desses estágios funcionasse, então isso veria todos os valores desse elemento já existirem, e não adicionaria outro. Se você tentasse fazer isso em uma ordem diferente, no caso de apresentar você teria dois documentos na matriz com o mesmo "deviceId", mas valores diferentes para "deviceType" e "notification". Então é por isso que vem por último.
Conclusão
Infelizmente, não há uma maneira simples de combiná-los como um Operação. Os operadores simplesmente não existem para que isso possa ser feito em uma única instrução e, portanto, você deve execute três operações de atualização para fazer o que você deseja. Também conforme declarado, o pedido de aplicação para essas atualizações é importante para que você obtenha o resultado desejado.
Embora isso ainda não exista nas versões de "produção" atuais, a próxima versão (2.6 e superior até o momento) tem uma maneira de "agrupar" essas solicitações com uma nova sintaxe para atualizar:
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
}}
}
}
]
)
Então, enquanto isso ainda essencialmente três operações, pelo menos você pode enviá-las pelo fio só uma vez