MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

mongodb $addToSet para um campo não array ao atualizar no upsert


O que você está tentando fazer aqui é adicionar um novo item a um array apenas onde o item não existir e também criar um novo documento onde ele não existir. Você escolhe $addToSet porque você quer que os itens sejam únicos, mas na verdade você quer que eles sejam únicos apenas por "a".

Então $addToset não fará isso, e você precisa "testar" o elemento que está presente. Mas o verdadeiro problema aqui é que não é possível fazer isso e "upert" ao mesmo tempo. A lógica não pode funcionar, pois um novo documento será criado sempre que o elemento da matriz não for encontrado, em vez de anexar ao elemento da matriz como você deseja.

Os erros de operação atuais por design como $addToSet não pode ser usado para "criar" uma matriz, mas apenas para "adicionar" membros a uma matriz existente. Mas, como já foi dito, você tem outros problemas em alcançar a lógica.

O que você precisa aqui é uma sequência de operações de atualização que cada uma "tenta" realizar sua ação esperada. Isso só pode ser feito com várias instruções:
// attempt "upsert" where document does not exist
// do not alter the document if this is an update
db.test.update(
    { "name": "abc" },
    { "$setOnInsert": { "config": [{ "a": 1, "b": 2 }] }},
    { "upsert": true }
)

// $push the element where "a": 1 does not exist
db.test.update(
    { "name": "abc", "config.a": { "$ne": 1 } },
    { "$push": { "config": { "a": 1, "b": 2 } }}
)

// $set the element where "a": 1 does exist
db.test.update(
    { "name": "abc", "config.a": 1 },
    { "$set": { "config.$.b": 2 } }
)

Em uma primeira iteração, a primeira instrução "informará" o documento e criará o array com itens. A segunda instrução não corresponderá ao documento porque o elemento "a" tem o valor especificado. A terceira instrução corresponderá ao documento, mas não o alterará em uma operação de gravação porque os valores não foram alterados.

Se você agora alterar a entrada para "b": 3 você obtém respostas diferentes, mas o resultado desejado:
db.test.update(
    { "name": "abc" },
    { "$setOnInsert": { "config": [{ "a": 1, "b": 3 }] }},
    { "upsert": true }
)

db.test.update(
    { "name": "abc", "config.a": { "$ne": 1 } },
    { "$push": { "config": { "a": 1, "b": 3 } }}
)

db.test.update(
    { "name": "abc", "config.a": 1 },
    { "$set": { "config.$.b": 3 } }
)

Então agora a primeira declaração corresponde a um documento com "name": "abc" mas não faz nada, pois as únicas operações válidas são em "inserir". A segunda instrução não corresponde porque "a" corresponde à condição. A terceira declaração corresponde ao valor de "a" e altera "b" no elemento correspondente para o valor desejado.

Subseqüentemente, alterar "a" para outro valor que não existe na matriz permite que 1 e 3 não façam nada, mas a segunda instrução adiciona outro membro à matriz, mantendo o conteúdo exclusivo por suas chaves "a".

Além disso, enviar uma declaração sem alterações dos dados existentes resultará, obviamente, em uma resposta que diz que nada foi alterado em todas as contas.

É assim que você faz suas operações. Você pode fazer isso com Bulk "ordenado" operações para que haja apenas uma única solicitação e resposta do servidor com a resposta válida para modificada ou criada.