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

Mettre à jour un tableau imbriqué avec MongoDB

Portée générale et explication

Il y a quelques choses qui ne vont pas avec ce que vous faites ici. Tout d'abord, vos conditions de requête. Vous faites référence à plusieurs _id valeurs dont vous ne devriez pas avoir besoin, et dont au moins une n'est pas au niveau supérieur.

Afin d'entrer dans une valeur "imbriquée" et en supposant également que _id est unique et n'apparaîtra dans aucun autre document, votre formulaire de requête devrait ressembler à ceci :

Model.update(
    { "array1.array2._id": "123" },
    { "$push": { "array1.0.array2.$.answeredBy": "success" } },
    function(err,numAffected) {
       // something with the result in here
    }
);

Maintenant, cela fonctionnerait réellement, mais ce n'est vraiment qu'un coup de chance car il y a de très bonnes raisons pour lesquelles cela ne devrait pas fonctionner pour vous.

La lecture importante est dans la documentation officielle pour le positionnel $ opérateur sous le sujet "Nested Arrays". Ce que cela dit est :

L'opérateur positionnel $ ne peut pas être utilisé pour les requêtes qui traversent plus d'un tableau, telles que les requêtes qui traversent des tableaux imbriqués dans d'autres tableaux, car le remplacement de l'espace réservé $ est une valeur unique

Plus précisément, cela signifie que l'élément qui sera mis en correspondance et renvoyé dans l'espace réservé de position est la valeur de l'index du premier tableau correspondant. Cela signifie dans votre cas l'index correspondant sur le tableau de niveau "supérieur".

Donc, si vous regardez la notation de la requête comme indiqué, nous avons "codé en dur" le premier ( ou index 0 ) dans le tableau de niveau supérieur, et il se trouve que l'élément correspondant dans "array2" est également l'entrée d'index zéro.

Pour le démontrer, vous pouvez modifier le _id correspondant valeur à "124" et le résultat sera $push une nouvelle entrée sur l'élément avec _id "123" car ils sont tous les deux dans l'entrée d'index zéro de "array1" et c'est la valeur renvoyée à l'espace réservé.

C'est donc le problème général avec les tableaux imbriqués. Vous pouvez supprimer l'un des niveaux et vous pourrez toujours $push au bon élément dans votre tableau "top", mais il y aurait toujours plusieurs niveaux.

Essayez d'éviter d'imbriquer les tableaux car vous rencontrerez des problèmes de mise à jour comme indiqué.

Le cas général consiste à "aplatir" les choses que vous "pensez" être des "niveaux" et à créer en fait ces "attributs" sur les éléments de détail finaux. Par exemple, la forme "aplatie" de la structure dans la question devrait ressembler à :

 {
   "answers": [
     { "by": "success", "type2": "123", "type1": "12" }
   ]
 }

Ou même lorsque l'acceptation du tableau interne est $push uniquement, et jamais mis à jour :

 {
   "array": [
     { "type1": "12", "type2": "123", "answeredBy": ["success"] },
     { "type1": "12", "type2": "124", "answeredBy": [] }
   ]
 }

Qui se prêtent tous deux à des mises à jour atomiques dans le cadre du $ positionnel opérateur

MongoDB 3.6 et supérieur

Depuis MongoDB 3.6, de nouvelles fonctionnalités sont disponibles pour travailler avec des tableaux imbriqués. Ceci utilise le filtre positionnel $[<identifier>] syntaxe afin de faire correspondre les éléments spécifiques et d'appliquer différentes conditions via arrayFilters dans la déclaration de mise à jour :

Model.update(
  {
    "_id": 1,
    "array1": {
      "$elemMatch": {
        "_id": "12","array2._id": "123"
      }
    }
  },
  {
    "$push": { "array1.$[outer].array2.$[inner].answeredBy": "success" }
  },
  {
    "arrayFilters": [{ "outer._id": "12" },{ "inner._id": "123" }] 
  }
)

Les "arrayFilters" comme passé aux options pour .update() ou même .updateOne() , .updateMany() , .findOneAndUpdate() ou .bulkWrite() La méthode spécifie les conditions à respecter sur l'identifiant donné dans l'instruction de mise à jour. Tous les éléments qui correspondent à la condition donnée seront mis à jour.

Étant donné que la structure est "imbriquée", nous utilisons en fait "plusieurs filtres" comme indiqué avec un "tableau" de définitions de filtres, comme indiqué. L'"identifiant" marqué est utilisé dans la correspondance avec le filtre positionnel $[<identifier>] syntaxe réellement utilisée dans le bloc de mise à jour de l'instruction. Dans ce cas inner et outer sont les identifiants utilisés pour chaque condition comme spécifié avec la chaîne imbriquée.

Cette nouvelle extension rend possible la mise à jour du contenu des tableaux imbriqués, mais cela n'aide pas vraiment à "interroger" ces données, donc les mêmes mises en garde s'appliquent, comme expliqué précédemment.

En général, vous "voulez" vraiment exprimer comme des "attributs", même si votre cerveau pense initialement "imbriquer", c'est généralement une réaction à la façon dont vous pensez que les "parties relationnelles précédentes" se rejoignent. En réalité, vous avez vraiment besoin de plus de dénormalisation.

Voir également Comment mettre à jour plusieurs éléments de tableau dans mongodb, car ces nouveaux opérateurs de mise à jour correspondent et mettent à jour "plusieurs éléments de tableau" plutôt que simplement le premier , qui était l'action précédente des mises à jour de position.

REMARQUE Un peu ironiquement, puisque cela est spécifié dans l'argument "options" pour .update() et comme les méthodes, la syntaxe est généralement compatible avec toutes les versions récentes du pilote.

Cependant, ce n'est pas le cas du mongo shell, puisque la façon dont la méthode y est implémentée (" ironiquement pour la compatibilité ascendante " ) le arrayFilters l'argument n'est pas reconnu et supprimé par une méthode interne qui analyse les options afin de fournir une "compatibilité descendante" avec les versions antérieures du serveur MongoDB et un "héritage" .update() Syntaxe d'appel API.

Donc, si vous voulez utiliser la commande dans le mongo shell ou d'autres produits "à base de shell" (notamment Robo 3T), vous avez besoin d'une dernière version de la branche de développement ou de la version de production à partir de 3.6 ou supérieure.

Voir aussi positional all $[] qui met également à jour "plusieurs éléments de tableau" mais sans s'appliquer aux conditions spécifiées et s'applique à tous éléments du tableau où il s'agit de l'action souhaitée.