O processo aqui é realmente bastante simples, varia apenas em onde você deseja "encontrar ou criar" os elementos no array.
Primeiro, supondo que os elementos de cada chave já estejam no lugar, o caso simples é consultar o elemento e atualizar com o índice retornado por meio do posicional
$
operador:db.collection.update(
{
"_id": docId,
"attrs": { "$elemMatch": { "key": "A1", "type": "T1" } }
}
{ "$set": { "attrs.$.value": "20" }
)
Isso só modificará o elemento correspondente sem afetar os outros.
No segundo caso em que "localizar ou criar" é necessário e a chave específica pode não existir, você usa "duas" instruções de atualização. Mas a API de operações em massa permite que você faça isso em uma única solicitação ao servidor com uma única resposta:
var bulk = db.collection.initializeOrderedBulkOp();
// Try to update where exists
bulk.find({
"_id": docId,
"attrs": { "$elemMatch": { "key": "A1", "type": "T2" } }
}).updateOne({
"$set": { "attrs.$.value": "30" }
});
// Try to add where does noes not exist
bulk.find({
"_id": docId,
"attrs": { "$not": { "$elemMatch": { "key": "A1", "type": "T2" } } }
}).updateOne({
"$push": { "attrs": { "key": "A1", "type": "T2", "value": "30" } }
});
bulk.execute();
A lógica básica é que primeiro a tentativa de atualização é feita para corresponder um elemento com os valores necessários, exatamente como feito antes. A outra condição testa onde o elemento não é encontrado revertendo a lógica de correspondência com
$not
. No caso em que o elemento da matriz não foi encontrado, um novo é válido para adição via
$push
. Devo acrescentar que, como estamos procurando especificamente por correspondências negativas aqui, é sempre uma boa ideia corresponder ao "documento" que você pretende atualizar por algum identificador exclusivo, como o
_id
chave. Embora seja possível com atualizações "múltiplas", você precisa ter cuidado com o que está fazendo. Portanto, no caso de executar o processo "localizar ou criar", o elemento que não foi correspondido é adicionado à matriz corretamente, sem interferir em outros elementos, também a atualização anterior para uma correspondência esperada é aplicada da mesma maneira:
{
"_id" : ObjectId("55b570f339db998cde23369d"),
"attrs" : [
{
"key" : "A1",
"type" : "T1",
"value" : "20"
},
{
"key" : "A2",
"type" : "T2",
"value" : "14"
},
{
"key" : "A1",
"type" : "T2",
"value" : "30"
}
]
}
Este é um padrão simples de seguir e, claro, as Operações em Massa aqui removem qualquer sobrecarga envolvida no envio e recebimento de várias solicitações de e para o servidor. Tudo isso funciona felizmente sem interferir em outros elementos que podem ou não existir.
Além disso, existem os benefícios extras de manter os dados em uma matriz para facilitar a consulta e a análise, conforme suportado pelos operadores padrão, sem a necessidade de reverter para o processamento do servidor JavaScript para percorrer os elementos.