Classer et mettre en quarantaine les enregistrements de matériel

MongoDBBeginner
Pratiquer maintenant

Introduction

FIELDNOTE loue de petits kits de matériel. Son catalogue MongoDB hérité contient des quantités en stock utiles aux côtés d’anciennes chaînes de caractères et de valeurs incertaines. Une quantité connue de zéro signifie qu’un kit est indisponible ; une quantité manquante signifie que le magasin ne sait pas. Transformer les deux en zéro masquerait un problème de données. Votre travail consiste à distinguer ces cas et à préserver les enregistrements peu fiables pour un examen ultérieur par le fournisseur.

Vous enregistrerez une politique de classification réutilisable et un script de quarantaine qui conserve le document d’origine complet avant de le retirer de la collection active. Vous devez savoir naviguer dans un terminal et connaître les documents MongoDB, les champs imbriqués, find et les mises à jour ordinaires. Les constructions JavaScript, règles de schéma et détails BSON utilisés ici sont expliqués avant utilisation. MongoDB, son shell et les données fictives du catalogue sont prêts. Utilisez Terminal tout au long du TP. La durée est une estimation de l’auteur comprenant la lecture et les erreurs ordinaires.

Rédigez policy.js et quarantine.js dans /home/labex/project/catalog-desk. Préservez chaque identifiant d’origine, chaque champ complet et son type, les extensions sans rapport, l’identité des collections et les outils fournis. Gardez les enregistrements valides et réparables inchangés dans ce TP. Ne supprimez/reconstruisez pas les collections et n’écrasez pas un original déjà en quarantaine. Travaillez uniquement sur le catalogue de ce cours. Il s’agit d’une fenêtre de maintenance contrôlée sans écritures concurrentes ; la copie et la suppression d’un enregistrement sont des opérations séparées, pas une transaction couvrant tout le catalogue.

Lire et classer le catalogue hérité

Dans cette étape, vous lirez un enregistrement ordinaire utilisable, distinguerez les types des quantités stockées et enregistrerez une politique de classification explicite. Commencez dans Terminal. cd change de répertoire ; ce chemin absolu permet aussi de revenir au bon endroit après être entré dans le mauvais répertoire :

cd /home/labex/project/catalog-desk

./catalog est le lanceur de connexion fourni dans ce répertoire ; ./ sélectionne l’exécutable local. Sa commande show lit les véritables documents stockés. Donnez-lui un identifiant de document :

./catalog show KIT-101

Repérez name: "Tripod", stock.rack: "A" et stock.available contenant "$numberInt": "12". La sortie utilise le format Extended JSON canonique : une représentation textuelle avec des marqueurs de type BSON explicites. BSON est le format de stockage des documents MongoDB. $numberInt identifie un entier sur 32 bits ; $numberLong un entier sur 64 bits. Ces marqueurs dans l’affichage décrivent les nombres stockés, pas des champs de stock imbriqués ordinaires à ajouter.

Le magasin accepte l’un ou l’autre type entier pour une quantité disponible entre 0 et 50 inclus. Les champs d’origine stock.rack et supplier sont des informations utiles à conserver. _id identifie l’enregistrement. Deux enregistrements nommés Tripod restent des enregistrements différents.

Examinez le véritable type de chaque quantité côté serveur :

./catalog types

Ce lecteur utilise l’expression d’agrégation MongoDB $type sur stock.available. Sa colonne actualType distingue int, long, string, null, missing, array et double. double est un type BSON à virgule flottante ; même une valeur Double 12 qui ressemble à un entier sort du contrat de ce magasin limité aux entiers. missing signifie que le champ n’existe pas ; null signifie qu’il existe avec une valeur null explicite. Un tableau tel que [12] n’est pas une quantité scalaire unique.

Lisez le catalogue complet pour relier les types aux enregistrements d’origine :

./catalog show

Utilisez ce tableau de décision avant d’écrire une requête :

Quantité stockée Décision dans ce TP Motif
Int32 12 ou Long 0 Conserver comme utilisable Entier scalaire accepté, y compris le zéro connu
Chaîne "12" ou "0" Conserver pour réparation approuvée Représentation décimale canonique dans la plage convenue
"12 kits", "012" ou "51" Mettre en quarantaine Sens inconnu, représentation non approuvée ou hors plage
Null, champ manquant, tableau ou Double 12 Mettre en quarantaine Ne respecte pas le contrat d’entier scalaire convenu

Une chaîne réparable doit contenir uniquement les chiffres ASCII canoniques d’un nombre de 0 à 50 : aucun signe, espace, zéro initial, point décimal ni suffixe d’unité. C’est un accord explicite pour ces données héritées, pas une promesse que toute chaîne ressemblant à un nombre peut être convertie sans risque. Mettre en quarantaine signifie conserver un original complet pour examen ; cela ne signifie pas inventer une quantité de remplacement.

Toutes les commandes suivantes restent dans ce Terminal et ce répertoire. Si un nom de fichier est mal saisi, ls liste les véritables noms locaux. Corrigez l’orthographe et relancez la commande ; une erreur de fichier n’est pas un refus de la base et ne nécessite pas de réinitialiser l’environnement.

Enregistrer la politique de classification

Un sélecteur MongoDB est un document décrivant les enregistrements correspondants. $jsonSchema vérifie la structure et les types BSON d’un document. required nomme les champs qui doivent exister ; il ne rend pas à lui seul leurs valeurs non null. properties décrit chaque champ, bsonType contraint son type BSON et minimum/maximum incluent les valeurs limites. La règle imbriquée stock exige séparément available. Les champs non mentionnés restent autorisés ; rack, supplier et les extensions ultérieures peuvent donc être conservés.

Utilisez ici la règle de type scalaire de JSON Schema. Un prédicat de requête ordinaire tel que {"stock.available": {$type: "int"}} peut correspondre à un tableau contenant un entier. Cela n’établirait pas la quantité scalaire requise par ce catalogue.

Vous écrirez du JavaScript pour mongosh, le shell MongoDB. const donne un nom à une valeur ; = affecte sa valeur initiale, les accolades forment un objet, les crochets un tableau et les virgules séparent les entrées. Le texte entre guillemets est une chaîne ; 0 et 50 sans guillemets sont des nombres. Un point-virgule termine une instruction. schema décrit la totalité d’un enregistrement accepté et valid l’encapsule comme sélecteur MongoDB.

La politique de chaînes approuvées possède la même structure, seule la règle de quantité change. JSON.stringify transforme cet objet de politique simple en texte JSON et JSON.parse en lit une copie distincte ; modifier la copie laisse alors la politique d’origine intacte. Cette méthode de copie concerne la politique simple, pas la préservation des enregistrements BSON stockés. stringSchema.properties.stock.properties.available accède à la règle imbriquée remplacée.

Le pattern est une expression régulière du texte autorisé. ^ ancre le début et \z signifie la fin absolue de la chaîne ; | sépare les possibilités, [1-9] autorise un chiffre de 1 à 9 et [1-4][0-9] deux chiffres de 10 à 49. Avec 0 et 50, ces possibilités couvrent exactement la plage approuvée. Les chaînes JavaScript utilisent une barre oblique inverse comme échappement ; la source écrit donc \\z pour transmettre \z à MongoDB. Cette ancre de fin absolue rejette aussi un saut de ligne final ; une ancre de fin ordinaire $ peut autoriser une correspondance avant ce saut de ligne. $nor: [valid, repairable] sélectionne les enregistrements qui ne correspondent à aucun des deux sélecteurs.

Créez le fichier de politique avec un here-document. cat > policy.js écrit le texte suivant dans le fichier et remplace son contenu précédent. Le délimiteur <<'POLICY' entre guillemets garde les signes dollar et l’autre texte JavaScript littéraux dans le shell. Terminez par POLICY seul sur une ligne. Copiez le bloc complet :

cat > policy.js <<'POLICY'
const schema = {
  bsonType: "object",
  required: ["_id", "name", "stock"],
  properties: {
    _id: { bsonType: "string", minLength: 1 },
    name: { bsonType: "string", minLength: 1 },
    stock: {
      bsonType: "object",
      required: ["available"],
      properties: {
        available: { bsonType: ["int", "long"], minimum: 0, maximum: 50 }
      }
    }
  }
};
const valid = { $jsonSchema: schema };
const stringSchema = JSON.parse(JSON.stringify(schema));
stringSchema.properties.stock.properties.available = {
  bsonType: "string",
  pattern: "^(0|[1-9]|[1-4][0-9]|50)\\z"
};
const repairable = { $jsonSchema: stringSchema };
const bad = { $nor: [valid, repairable] };
POLICY

Le classificateur fourni évalue ce fichier enregistré inchangé avec une identité de base de données en lecture seule. Il utilise ensuite vos sélecteurs valid, repairable et bad dans de véritables requêtes MongoDB :

./catalog classify policy.js

Examinez les véritables groupes de documents, pas seulement leurs tailles. valid doit contenir KIT-101 et KIT-102, y compris le Long zéro d’origine. repairable doit contenir KIT-103 et KIT-104, avec leurs chaînes d’origine inchangées. outside doit contenir KIT-105 à KIT-111, y compris les null, champ manquant, tableau et Double d’origine. L’extension complète d’examen fournisseur de KIT-105 doit rester visible.

Si une erreur de syntaxe JavaScript apparaît, le fichier n’a pas été accepté comme politique fonctionnelle. Relancez le here-document complet pour remplacer un fichier endommagé, puis relancez le classificateur. Une erreur de syntaxe indique un problème de fichier/expression ; une connexion indisponible à la base est une autre erreur et ne signifie pas que la classification n’a trouvé aucune correspondance. Aucune donnée n’a été réparée ni retirée par ce classificateur.

Préserver les originaux avant de retirer les enregistrements incertains

Dans cette étape, vous enregistrerez un script de quarantaine, observerez une véritable interruption après sa première copie et relancerez le script inchangé pour terminer. Commencez dans Terminal, dans /home/labex/project/catalog-desk.

Chaque document de quarantaine aura l’_id d’origine, un original contenant l’enregistrement stocké complet et reason: "outside-approved-catalog-contract". Le motif décrit pourquoi un examen fournisseur est nécessaire ; il n’affirme pas que le stock est nul. Une quantité d’origine peut être manquante, null, un tableau ou d’un autre type ; copier uniquement la valeur affichée ou le nom ferait donc perdre des éléments de preuve.

Il existe une autre limite : le shell peut convertir les valeurs numériques BSON stockées en nombres JavaScript ordinaires lors de la lecture. Enregistrer un nombre JavaScript ressemblant à un entier peut alors produire Int32, même si l’original était Double. Pour chaque copie et comparaison d’original, utilisez le troisième argument de find/findOne, {promoteValues: false, bsonRegExp: true}. Il conserve les enveloppes numériques BSON et les valeurs d’expression régulière au lieu de les convertir en équivalents JavaScript. Le deuxième argument {} est une projection vide qui garde chaque champ. EJSON.stringify(value, {relaxed: false}) produit une représentation Extended JSON canonique à partir de ces types préservés ; un Double 12 et un Int32 12 restent donc différents lors de la comparaison.

La boucle du script lit le groupe hors contrat une seule fois et traite chaque enregistrement séparément. sort({_id: 1}) donne des identifiants croissants lisibles et toArray() rassemble les résultats. for (const doc of ...) répète les instructions encadrées pour chaque résultat. L’opération upsert trouve un enregistrement de quarantaine par son identifiant d’origine ; upsert: true le crée s’il est absent. $setOnInsert écrit l’original et le motif uniquement lors de cette création, laissant une copie existante intacte. findOne relit la copie stockée.

if teste une condition. !== signifie différent et || signifie qu’au moins une des conditions est vraie. Un original ou motif différent doit déclencher throw Error(...), qui arrête le script avec un code de sortie non nul avant de supprimer cet enregistrement source. deleteOne retire uniquement l’identifiant source vérifié. Les enregistrements précédents peuvent déjà avoir été traités ; ce sont des opérations individuelles, pas un lot tout ou rien. Sans écritures concurrentes, une comparaison complète réussie avant chaque suppression fournit le mécanisme de reprise utilisé ici.

Écrivez le script. Le here-document QUARANTINE utilise la même syntaxe d’écriture littérale que précédemment :

cat > quarantine.js <<'QUARANTINE'
const typed = { promoteValues: false, bsonRegExp: true };
for (const doc of db.equipment.find(bad, {}, typed).sort({_id: 1}).toArray()) {
  db.quarantine.updateOne(
    {_id: doc._id},
    {$setOnInsert: {original: doc, reason: "outside-approved-catalog-contract"}},
    {upsert: true}
  );
  const saved = db.quarantine.findOne({_id: doc._id}, {}, typed);
  if (EJSON.stringify(saved.original, {relaxed: false}) !== EJSON.stringify(doc, {relaxed: false}) ||
      saved.reason !== "outside-approved-catalog-contract") {
    throw Error("Existing quarantine copy differs; keep source and investigate");
  }
  db.equipment.deleteOne({_id: doc._id});
}
QUARANTINE

Utilisez d’abord la connexion copy-only fournie. C’est une véritable identité de base autorisée à lire et copier, mais pas à supprimer. Le lanceur évalue policy.js puis quarantine.js inchangés dans un même appel du shell. Attendez-vous à une erreur native d’autorisation et à un code de sortie non nul lorsque le script essaie de supprimer pour la première fois :

./catalog copy-only policy.js quarantine.js

Le message contient not authorized et une commande delete sur equipment. La copie a réussi avant ce refus. Lisez les deux véritables enregistrements :

./catalog archive KIT-105
./catalog show KIT-105

KIT-105 existe maintenant aux deux endroits. L’original en quarantaine doit comprendre son texte de stock et l’extension complète, y compris ses marqueurs de types numériques, de date et ObjectId. La source active reste intacte. Une commande en échec n’implique donc pas l’annulation de toutes les écritures précédentes.

Relancez les fichiers enregistrés exacts avec la connexion ordinaire d’écriture du cours :

./catalog run policy.js quarantine.js

Aucune sortie n’est requise pour un script réussi. La première copie existante est vérifiée et conservée ; sa source correspondante est retirée. Les enregistrements incertains restants sont copiés, vérifiés et retirés à leur tour. Lisez les véritables résultats :

./catalog show
./catalog archive

Le catalogue actif ne doit contenir que KIT-101 à KIT-104, inchangés, y compris les deux chaînes approuvées en attente de réparation. La quarantaine doit contenir KIT-105 à KIT-111 avec les documents d’origine complets et le motif convenu. En particulier, le Double d’origine de KIT-109 affiche toujours $numberDouble et l’original de KIT-107 ne possède toujours pas de champ available.

Relancez la migration :

./catalog run policy.js quarantine.js
./catalog archive

Il ne reste aucun enregistrement hors contrat à déplacer ; les originaux enregistrés restent donc identiques. C’est une réexécution idempotente : répéter une opération terminée ne change pas son résultat métier. Une interruption après une copie peut aussi être reprise, car la clé d’archive empêche une copie en double et la comparaison refuse une copie existante incohérente. Si cette erreur de conflit apparaît, conservez la source et l’archive précédente pour investigation ; ne la « corrigez » pas en écrasant l’archive ou en supprimant la source. Le script ne garantit pas la sûreté face aux modifications concurrentes entre sa lecture et sa suppression, et ne peut pas annuler tout le lot.

Résumé

Vous avez distingué les quantités entières utilisables, les chaînes explicitement approuvées et les enregistrements incertains avec une politique MongoDB réutilisable. Vous avez ensuite préservé les originaux typés complets avant de supprimer leurs sources actives, observé un véritable échec partiel et repris le même script enregistré. Le catalogue conserve maintenant les enregistrements utilisables et réparables, tandis que la quarantaine garde les éléments nécessaires à l’examen par le fournisseur. La conversion et la validation des écritures futures sont abordées dans l’unité indépendante suivante.