MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

mongodb $addToSet à un champ non-tableau lors de la mise à jour sur upsert

Ce que vous essayez de faire ici est d'ajouter un nouvel élément à un tableau uniquement là où l'élément n'existe pas et de créer également un nouveau document là où il n'existe pas. Vous choisissez $addToSet parce que vous voulez que les éléments soient uniques, mais en fait vous voulez vraiment qu'ils soient uniques par "a" seulement.

Donc $addToset ne le fera pas, et vous devez plutôt "tester" l'élément présent. Mais le vrai problème ici est qu'il n'est pas possible de faire cela et "upsert" en même temps. La logique ne peut pas fonctionner car un nouveau document sera créé chaque fois que l'élément de tableau n'a pas été trouvé, plutôt que d'être ajouté à l'élément de tableau comme vous le souhaitez.

Les erreurs d'opération actuelles par conception en tant que $addToSet ne peut pas être utilisé pour "créer" un tableau, mais uniquement pour "ajouter" des membres à un tableau existant. Mais comme indiqué déjà, vous avez d'autres problèmes avec la réalisation de la logique.

Ce dont vous avez besoin ici, c'est d'une séquence d'opérations de mise à jour qui « essaient » chacune d'effectuer l'action attendue. Cela ne peut être fait qu'avec plusieurs instructions :

// 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 } }
)

Lors d'une première itération, la première instruction "upsert" le document et crée le tableau avec des éléments. La deuxième instruction ne correspondra pas au document car l'élément "a" a la valeur qui a été spécifiée. La troisième instruction correspondra au document mais ne le modifiera pas lors d'une opération d'écriture car les valeurs n'ont pas changé.

Si vous changez maintenant l'entrée en "b": 3 vous obtenez des réponses différentes mais le résultat souhaité :

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 } }
)

Alors maintenant, la première instruction correspond à un document avec "name": "abc" mais ne fait rien puisque les seules opérations valides sont sur "insert". La deuxième instruction ne correspond pas car "a" correspond à la condition. La troisième instruction correspond à la valeur de "a" et remplace "b" dans l'élément correspondant par la valeur souhaitée.

Changer ensuite "a" en une autre valeur qui n'existe pas dans le tableau permet à 1 et 3 de ne rien faire, mais la deuxième instruction ajoute un autre membre au tableau en gardant le contenu unique par leurs clés "a".

De plus, soumettre une déclaration sans modification des données existantes entraînera bien sûr une réponse indiquant que rien n'a changé sur tous les comptes.

C'est comme ça que vous faites vos opérations. Vous pouvez le faire avec "ordonné" Bulk opérations afin qu'il n'y ait qu'une seule demande et réponse du serveur avec la réponse valide à modifié ou créé.