Fermer

juin 14, 2018

JSON Schema Validation & Expressive Query Syntax dans MongoDB 3.6 –


Cet article a été publié à l'origine le MongoDB . Merci de soutenir les partenaires qui rendent SitePoint possible.

L'une des principales forces de MongoDB a toujours été l'autonomisation des développeurs: en s'appuyant sur une architecture de schéma souple, MongoDB facilite et accélère le passage des applications aux étapes de développement. Cependant, à mesure que les applications évoluent et évoluent, elles tendent à atteindre un stade stable où les changements de schémas fréquents ne sont plus critiques ou doivent être déployés de manière plus contrôlée. , pour empêcher l'insertion de données indésirables dans la base de données. Ces contrôles sont particulièrement importants lorsque plusieurs applications écrivent dans la même base de données ou lorsque les processus analytiques s'appuient sur des structures de données prédéfinies pour être précis et utiles.

MongoDB 3.2 a été la première version à introduire la validation de document des fonctionnalités que les développeurs et les administrateurs de bases de données qui sont habitués aux bases de données relationnelles restent exigeants. En tant que CTO de MongoDB, Eliot Horowitz, mis en évidence dans Validation de documents et ce que signifie schémas dynamiques :

Avec le reste des fonctionnalités 3.2 "schéma quand vous en avez besoin", MongoDB façon de garder les données propres. Ce ne sont certainement pas les derniers outils que nous allons fournir, mais plutôt une étape importante dans la façon dont MongoDB gère le schéma

Annoncer le support de la validation du schéma JSON

Fonctionnalité, MongoDB 3.6 introduit un moyen plus puissant d'appliquer des schémas dans la base de données, avec sa prise en charge de JSON Schema Validation, une spécification qui fait partie de la norme JSON Schema de l'IETF. ] La validation de schéma JSON étend la validation de documents de différentes manières, notamment la possibilité d'appliquer des schémas dans des tableaux et d'empêcher l'ajout d'attributs non approuvés. Ce sont les nouvelles fonctionnalités sur lesquelles nous nous concentrerons dans ce blog, ainsi que la possibilité de créer des règles de validation métier.

Depuis le MongoDB 3.6, le schéma JSON est la méthode recommandée pour appliquer la Schema Validation . La section suivante met en évidence les fonctionnalités et avantages de l'utilisation de JSON Schema Validation

Passage de la validation de document à la validation de schéma JSON

Nous allons commencer par créer une collection orders le tutoriel du Document Validation du blog ):

 db.createCollection ("orders", {
  validateur: {
    item: {$ type: "chaîne"},
    prix: {$ type: "décimal"}
  }
});

Avec cette configuration de validation de document, nous nous assurons non seulement que les attributs et sont présents dans tout document mais aussi est une chaîne et prix un point décimal (le type recommandé pour toutes les devises et pourcentages). Par conséquent, l'élément suivant ne peut pas être inséré (en raison de l'attribut "rogue" price ):

 db.orders.insert ({
    "_id": 6666,
    "item": "jkl",
    "prix": "voyous",
    "quantité: 1 });

Cependant, le document suivant pourrait être inséré (notez l'attribut "pryce" mal orthographié):

 db.orders.insert ({
    "_id": 6667,
    "item": "jkl",
    "price": NumberDecimal ("15.5"),
    "pryce": "voyou"});

Avant MongoDB 3.6, vous ne pouviez pas empêcher l'ajout d'attributs mal orthographiés ou non autorisés. Voyons comment JSON Schema Validation peut empêcher ce comportement. Pour ce faire, nous allons utiliser un nouvel opérateur, $ jsonSchema :

 db.runCommand ({
  collMod: "commandes",
  validateur: {
    $ jsonSchema: {
      bsonType: "objet",
      requis: ["item", "price"],
      Propriétés: {

       article: {
            bsonType: "chaîne"
       },
       prix: {
          bsonType: "décimal"
        }
      }
    }
  }
});

Le schéma JSON ci-dessus est l'équivalent exact de la règle de validation de document que nous avons définie précédemment dans la collection . Vérifions que notre schéma a bien été mis à jour pour utiliser le nouvel opérateur $ jsonSchema en utilisant la méthode db.getCollectionInfos () dans le shell Mongo:

 db.getCollectionInfos ({ nom: "ordres"})

Cette commande affiche une mine d'informations sur la collection . Par souci de lisibilité, voici la section qui inclut le schéma JSON:

 ...
"options": {
    "validateur": {
        "$ jsonSchema": {
            "bsonType": "objet",
            "requis": [
                "item",
                "price"
            ],
            "Propriétés" : {
                "article" : {
                    "bsonType": "chaîne"
                },
                "prix" : {
                    "bsonType": "décimal"
                }
            }
        }
    },
    "validationLevel": "strict",
    "validationAction": "erreur"
}
...

Maintenant, enrichissons un peu notre schéma JSON pour mieux utiliser ses puissantes fonctionnalités:

 db.runCommand ({
  collMod: "commandes",
  validateur: {
    $ jsonSchema: {
      bsonType: "objet",
       additionalProperties: false ,
      requis: ["item", "price"],
      Propriétés: {
        _id: {} ,
       article: {
            bsonType: "chaîne",
            description: "'item' doit être une chaîne et est obligatoire"
        },
        prix: {
          bsonType: "décimal",
          description: "'prix' doit être une décimale et est requis"
        },
        quantité: {
           bsonType: ["int", "long"],
          minimum: 1,
          maximum: 100,
          exclusiveMaximum: vrai,
          la description:
            "'quantité' doit être un entier court ou long compris entre 1 et 99"
        }
      }
    }
  }
});

Passons en revue les ajouts apportés à notre schéma:

  • Tout d'abord, notez l'utilisation de l'attribut additionalProperties: false : il nous empêche d'ajouter des attributs autres que ceux mentionnés dans le ] propriétés section. Par exemple, il ne sera plus possible d'insérer des données contenant un attribut pryce mal orthographié. Par conséquent, l'utilisation de additionalProperties: false au niveau racine du document rend également obligatoire la déclaration de la propriété _id : si notre code d'insertion la définit explicitement ou non, est un champ requis par MongoDB et créerait automatiquement, s'il n'est pas présent. Ainsi, nous devons l'inclure explicitement dans la section propriétés de notre schéma
  • Deuxièmement, nous avons choisi de déclarer l'attribut quantité comme un entier court ou long entre 1 et 99 (en utilisant les attributs minimum maximum et maximumMaximum . Bien sûr, étant donné que notre schéma n'autorise que les entiers inférieurs à 100, nous aurions simplement pu définir la propriété bsonType sur int . Mais l'ajout de long comme type valide rend le code de l'application plus flexible, en particulier s'il peut être envisagé de lever la restriction maximum
  • Enfin, notez que la description attribut (présent dans les élément prix et quantité déclarations d'attribut) est entièrement facultatif et n'a aucun effet sur le schéma en dehors de la documentation du schéma pour le lecteur.

Avec le schéma ci-dessus, les documents suivants peuvent être insérés dans notre collection de commandes:

 db.orders.insert ({
    "item": "jkl",
    "prix": NumberDecimal (15.50),
    "quantité": NumberInt (99)
  });

  db.orders.insert ({
    "item": "jklm",
    "prix": NumberDecimal (15.50),
    "quantité": Nombre Long (99)
  });

Cependant, les documents suivants ne sont plus considérés comme valides:

 db.orders.insert ({
    "item": "jkl",
    "prix": NumberDecimal (15.50),
     "quantité": NumberInt (100) 
  });
  db.orders.insert ({
    "item": "jkl",
    "prix": NumberDecimal (15.50),
     "quantité": "98" 
  });
  db.orders.insert ({
    "item": "jkl",
     "pryce": NumberDecimal (15.50), 
    "quantité": NumberInt (99)
  });

Vous avez probablement remarqué que nos commandes ci-dessus sont apparemment étranges: elles ne contiennent qu'un seul article. Plus réaliste, un ordre se compose de plusieurs éléments et une structure JSON possible pourrait être la suivante:

 {
    _id: 10000,
    total: NumberDecimal (141),
    TVA: 0,20,
    totalWithVAT: NumberDecimal (169),
    lineitems: [
        {
            sku: "MDBTS001",
            name: "MongoDB Stitch T-shirt",
            quantity: NumberInt(10),
            unit_price:NumberDecimal(9)
        },
        {
            sku: "MDBTS002",
            quantity: NumberInt(5),
            unit_price: NumberDecimal(10)
        }
    ]
}

Avec MongoDB 3.6, nous pouvons maintenant contrôler la structure du tableau lineitems par exemple avec le schéma JSON suivant:

 db.runCommand ({
    collMod: "commandes",
    validateur: {
      $ jsonSchema: {
        bsonType: "objet",
        requis: ["lineitems"],
        Propriétés: {
        lineitems: {
               bsonType: ["array"]
              minItems: 1,
              maxItems: 10,
              articles: {
                  requis: ["unit_price", "sku", "quantity"],
                  bsonType: "objet",
                  additionalProperties: false,
                  Propriétés: {
                      sku: {
                        bsonType: "chaîne",
                        description: "'sku' doit être une chaîne de caractères et est obligatoire"
                      },
                      prénom: {
                        bsonType: "chaîne",
                        description: "'nom' doit être une chaîne"
                      },
                      prix unitaire: {
                        bsonType: "décimal",
                        description: "'unit_price' doit être une décimale et est obligatoire"
                      },
                      quantité: {
                        bsonType: ["int", "long"],
                        minimum: 0,
                        maximum: 100,
                        exclusiveMaximum: vrai,
                        la description:
                          "'quantité' doit être un entier court ou long dans [0, 100)"
                      },
                  }
              }
          }
        }
      }
    }
  });

Avec le schéma ci-dessus, nous appliquons que tout ordre inséré ou mis à jour dans la collection de commandes contient un tableau lineitems de 1 à 10 documents qui ont tous sku unit_price et quantité attributs (dont la quantité doit être un nombre entier)

Le schéma empêcherait d'insérer le document suivant, mal formé:

 db.orders.insert ({
        total: NumberDecimal (141),
        TVA: NombreDécimal (0,20),
        totalWithVAT: NumberDecimal (169),
        lineitems: [
            {
                sku: "MDBTS001",
                nom: "MongoDB Stitch T-shirt",
                quantité: NumberInt (10),
                prix: NumberDecimal (9) // cela devrait être 'prix_unitaire'
            },
            {
                name: "MDBTS002", // manque une propriété 'sku'
                quantité: NumberInt (5),
                unit_price: NumberDecimal (10)
            }
        ]
})

Mais cela permettrait d'insérer le document suivant, conforme au schéma:

 db.orders.insert ({
        total: NumberDecimal (141),
        TVA: NombreDécimal (0,20),
        totalWithVAT: NumberDecimal (169),
        lineitems: [
            {
                sku: "MDBTS001",
                name: "MongoDB Stitch T-shirt",
                quantity: NumberInt(10),
                unit_price: NumberDecimal(9)
            },
            {
                sku: "MDBTS002",
                quantity: NumberInt(5),
                unit_price: NumberDecimal(10)
            }
        ]
})

Cependant, si vous faites attention à l'ordre ci-dessus, vous remarquerez qu'il contient quelques erreurs:

  1. La valeur de l'attribut totalWithVAT est incorrecte (elle doit être égale à 141 * 1.20 = 169.2)
  2. La valeur d'attribut totale est incorrecte (elle doit être égale à la somme de chaque sous-total de l'élément de ligne, soit 10 * 9 + 10 * 5 = 140)

n'importe quel moyen d'appliquer que total et totalWithVAT valeurs soient correctes en utilisant des règles de validation de base de données, sans compter uniquement sur la logique d'application?

Introducing MongoDB Expressive Query Syntax

les règles de validation sont désormais possibles grâce à la syntaxe de requête expressive, une nouvelle fonctionnalité de MongoDB 3.6

L'un des objectifs de la syntaxe de requête expressive est d'amener la puissance des expressions d'agrégation de MongoDB à MongoDB ] langage de requête .Un cas d'utilisation intéressant est l'abilit y pour composer des règles de validation dynamiques qui calculent et comparent plusieurs valeurs d'attribut au moment de l'exécution. En utilisant le nouvel opérateur $ expr il est possible de valider la valeur de l'attribut totalWithVAT avec l'expression de validation suivante:

 $ expr: {
   $ eq: [
     "$totalWithVAT",
     {$multiply: [
       "$total", 
       {$sum: [1, "$VAT"]}
     ]}
   ]
}

L'expression ci-dessus vérifie que la valeur d'attribut totalWithVAT est égale à total * (1 + TVA) . Dans sa forme compacte, voici comment nous pourrions l'utiliser comme règle de validation, à côté de notre validation du schéma JSON:

 db.runCommand ({
    collMod: "commandes",
    validateur: {
  $ expr: {$ eq: [
           "$totalWithVAT",
           {$multiply:["$total", {$sum:[1,"$VAT"]}}
             ]} ,
      $ jsonSchema: {
        bsonType: "objet",
        requis: ["lineitems"],
        Propriétés: {
          lineitems: {
              bsonType: ["array"],
              minItems: 1,
              maxItems: 10,
              articles: {
                  requis: ["unit_price", "sku", "quantity"],
                  bsonType: "objet",
                  additionalProperties: false,
                  Propriétés: {
                      sku: {
                        bsonType: "chaîne",
                        description: "'sku' doit être une chaîne de caractères et est obligatoire"
                      },
                      prénom: {
                        bsonType: "chaîne",
                        description: "'nom' doit être une chaîne"
                      },
                      prix unitaire: {
                        bsonType: "décimal",
                        description: "'unit_price' doit être une décimale et est obligatoire"
                      },
                      quantité: {
                        bsonType: ["int", "long"],
                        minimum: 0,
                        maximum: 100,
                        exclusiveMaximum: vrai,
                        la description:
                          "'quantité' doit être un entier court ou long dans [0, 100)"
                      },
                  }
              }
          }
        }
      }
    }
  });

Avec le validateur ci-dessus, l'opération d'insertion suivante n'est plus possible:

 db.orders.insert ({
        total: NumberDecimal (141),
        TVA: NombreDécimal (0,20),
        totalWithVAT: NumberDecimal (169),
        lineitems: [
            {
                sku: "MDBTS001",
                nom: "MongoDB Stitch T-shirt",
                quantité: NumberInt (10),
                Unit_price: NumberDecimal (9)
            },
            {
                sku: "MDBTS002",
                quantité: NumberInt (5),
                unit_price: NumberDecimal (10)
            }
        ]
})

A la place, la valeur totalWithVAT doit être ajustée conformément à notre nouvelle règle de validation de TVA:

 db.orders.insert ({
    total: NumberDecimal (141),
    TVA: NombreDécimal (0,20),
     totalWithVAT: NumberDecimal (169.2) ,
    lineitems: [
            {
                sku: "MDBTS001",
                name: "MongoDB Stitch T-shirt",
                quantity: NumberInt(10),
                unit_price: NumberDecimal(9)
            },
            {
                sku: "MDBTS002",
                quantity: NumberInt(5),
                unit_price: NumberDecimal(10)
            }
        ]
})

Si nous voulons également nous assurer que la valeur totale est la somme de chaque valeur de l'article (c'est-à-dire quantité unit_price *), l'expression suivante doit être utilisée: [19659012] $ expr: {
    $ eq: [
« $total »,
{$sum: {
$map: {
« input »: « $lineitems »,
« as »: « item »,
« in »: {
« $multiply »: [
« $$item.quantity »,
« $$item.unit_price »
]
             }
          }
       }}
    ]
  }

L'expression ci-dessus utilise l'opérateur $ map pour calculer le sous-total de chaque élément de ligne, puis somme tous ces sous-totaux, et enfin le compare à la valeur totale . Pour s'assurer que les règles de validation Total et VAT sont vérifiées, nous devons les combiner en utilisant les opérateurs $ et . Enfin, notre validateur de collection peut être mis à jour avec la commande suivante:

 db.runCommand ({
    collMod: "commandes",
    validateur: {
      $ expr: {$ et: [
          {$eq:[ 
            "$totalWithVAT",
                   {$multiply:["$total", {$sum:[1,"$VAT"]}]}
          ]},
          {$ eq: [
                   "$total", 
                {$sum: {$map: {
                    "input": "$lineitems",
                    "as": "item",
                    "in":{"$multiply":["$$item.quantity","$$item.unit_price"]}
                   }}}
             ]}
        ]},
      $ jsonSchema: {
        bsonType: "objet",
        requis: ["lineitems", "total", "VAT", "totalWithVAT"],
        Propriétés: {
          total: {bsonType: "décimal"},
          TVA: {bsonType: "décimal"},
          totalWithVAT: {bsonType: "decimal"},
          lineitems: {
              bsonType: ["array"],
              minItems: 1,
              maxItems: 10,
              articles: {
                  requis: ["unit_price", "sku", "quantity"],
                  bsonType: "objet",
                  additionalProperties: false,
                  Propriétés: {
                      sku: {bsonType: "chaîne"},
                      nom: {bsonType: "chaîne"},
                      unit_price: {bsonType: "decimal"},
                      quantité: {
                        bsonType: ["int", "long"],
                        minimum: 0,
                        maximum: 100,
                        exclusiveMaximum: true

                      },
                  }
              }
          }
        }
      }
    }
  });

Par conséquent, nous devons mettre à jour les propriétés total et totalWithVAT pour nous conformer à notre schéma mis à jour et aux règles de validation métier (sans modifier le tableau lineitems ): [19659012] db.orders.insert ({
      total: NumberDecimal (140),
      TVA: NombreDécimal (0,20),
      totalWithVAT: NumberDecimal (168),
      lineitems: [
{
sku: « MDBTS001 »,
name: « MongoDB Stitch T-shirt »,
quantity: NumberInt(10),
unit_price: NumberDecimal(9)
},
{
sku: « MDBTS002 »,
quantity: NumberInt(5),
unit_price: NumberDecimal(10)
}
]
  })

Prochaines étapes

Avec l'introduction de JSON Schema Validation dans MongoDB 3.6, les administrateurs de bases de données sont maintenant mieux équipés pour répondre aux exigences de gouvernance des données émanant des responsables de la conformité ou des régulateurs tout en bénéficiant de l'architecture flexible de MongoDB. , les développeurs trouveront la nouvelle syntaxe de requête expressive utile pour simplifier la base de code de leur application en déplaçant la logique métier de la couche application vers la couche base de données.

Si vous voulez en savoir plus sur tout nouveau dans MongoDB 3.6, téléchargez notre ] Quoi de neuf guide .

Si vous voulez approfondir le côté technique, visitez les pages Schema Validation et Expressive Query Syntax dans notre documentation officielle. ] Si vous souhaitez acquérir une expérience pratique plus pratique, jetez un coup d'œil à ce laboratoire pratique JSON Schema Validation . Vous pouvez l'essayer immédiatement sur le service de base de données MongoDB Atlas qui prend en charge MongoDB 3.6 depuis sa date de disponibilité générale.

Enfin, inscrivez-vous à notre formation MongoDB 3.6 gratuite [19459005




Source link