Clasificar y poner en cuarentena registros de equipamiento

MongoDBBeginner
Practicar Ahora

Introducción

FIELDNOTE alquila pequeños kits de equipamiento. Su catálogo heredado de MongoDB contiene recuentos útiles de existencias junto con cadenas antiguas y valores inciertos. Un recuento conocido de cero significa que un kit no está disponible; un recuento ausente significa que la tienda no lo sabe. Convertir ambos en cero ocultaría un problema de datos. Tu trabajo consiste en separar estos casos y conservar los registros poco fiables para una revisión posterior del proveedor.

Guardarás una política reutilizable de clasificación y un script de cuarentena que conserve el documento original completo antes de eliminarlo de la colección activa. Necesitas conocimientos básicos para moverte por la terminal y familiaridad con documentos de MongoDB, campos anidados, find y actualizaciones ordinarias. Las construcciones JavaScript, las reglas de esquema y los detalles BSON usados aquí se explican antes de utilizarlos. MongoDB, su intérprete y los datos sintéticos de catálogo están preparados. Usa Terminal durante todo el laboratorio. La duración es una estimación del autor que incluye lectura y errores habituales.

Escribe policy.js y quarantine.js en /home/labex/project/catalog-desk. Conserva cada ID original, cada campo y tipo original completo, las extensiones ajenas a la tarea, la identidad de las colecciones y las herramientas proporcionadas. Mantén sin cambios los registros válidos y reparables en este laboratorio. No elimines ni reconstruyas colecciones ni sobrescribas un original existente en cuarentena. Trabaja solo sobre el catálogo de este curso. Esta es una ventana de mantenimiento controlada sin escritores concurrentes; copiar y borrar un registro son operaciones separadas, no una transacción que abarque todo el catálogo.

Leer y clasificar el catálogo heredado

En este paso, leerás un registro normal utilizable, distinguirás los tipos de recuento almacenados y guardarás una política explícita de clasificación. Empieza en Terminal. cd cambia de directorio; esta ruta absoluta también permite recuperarte de entrar en el directorio equivocado:

cd /home/labex/project/catalog-desk

./catalog es el ejecutor de conexión proporcionado en este directorio; ./ selecciona el ejecutable local. Su comando show lee los documentos realmente almacenados. Dale un ID de documento:

./catalog show KIT-101

Busca name: "Tripod", stock.rack: "A" y stock.available con "$numberInt": "12". La salida usa Extended JSON canónico: una representación textual con etiquetas explícitas de tipo BSON. BSON es el formato de documentos almacenados de MongoDB. $numberInt identifica un entero de 32 bits; $numberLong, un entero de 64 bits. Estas etiquetas de la salida describen números almacenados, no campos anidados ordinarios de existencias que debas añadir.

La tienda acepta cualquiera de los dos tipos enteros para un recuento disponible entre 0 y 50, ambos incluidos. Los campos originales stock.rack y supplier son información útil y deben conservarse. _id identifica el registro. Dos registros llamados Tripod siguen siendo registros diferentes.

Inspecciona el tipo real del servidor para cada recuento:

./catalog types

Este lector usa la expresión de agregación $type de MongoDB sobre stock.available. Su columna actualType distingue int, long, string, null, missing, array y double. double es un tipo BSON de coma flotante; incluso un valor Double 12 de apariencia entera queda fuera del contrato de esta tienda que solo admite enteros. missing significa que el campo no existe; null, que existe con un valor null explícito. Un array como [12] no es un recuento escalar.

Lee el catálogo completo para relacionar los tipos con los registros originales:

./catalog show

Usa esta tabla de decisiones antes de escribir una consulta:

Recuento almacenado Decisión en este laboratorio Motivo
Int32 12 o Long 0 Conservar como utilizable Entero escalar aceptado, incluido el cero conocido
Cadena "12" o "0" Conservar para reparación aprobada Representación decimal canónica en el intervalo acordado
"12 kits", "012" o "51" Poner en cuarentena Significado desconocido, representación no aprobada o fuera de intervalo
Null, ausente, array o Double 12 Poner en cuarentena No cumple el contrato acordado de entero escalar

Una cadena reparable debe contener solo los dígitos ASCII canónicos de un número de 0 a 50: sin signo, espacios, cero inicial, punto decimal ni sufijo de unidad. Es un acuerdo explícito para estos datos heredados, no una promesa de que toda cadena de apariencia numérica pueda convertirse con seguridad. Cuarentena significa conservar un original completo para revisarlo; no significa inventar un recuento sustitutivo.

Todos los comandos posteriores permanecen en esta Terminal y este directorio. Si escribes mal un nombre de archivo, ls enumera los nombres locales reales. Corrige la escritura y vuelve a ejecutar el comando; un error de archivo no es un rechazo de la base de datos y no requiere reiniciar el entorno.

Guardar la política de clasificación

Un selector de MongoDB es un documento que describe qué registros coinciden. $jsonSchema comprueba la forma y los tipos BSON de un documento. required enumera campos que deben existir; por sí solo no hace que sus valores no sean null. properties describe cada campo, bsonType restringe su tipo BSON y minimum/maximum incluyen sus valores límite. La regla anidada stock requiere por separado available. Los campos no mencionados siguen permitidos, de modo que pueden conservarse rack, supplier y extensiones posteriores.

Usa aquí la regla de tipo escalar de JSON Schema. Un predicado ordinario de consulta como {"stock.available": {$type: "int"}} puede coincidir con un array que contenga un entero. Eso no demostraría el recuento escalar que necesita este catálogo.

Escribirás JavaScript para mongosh, el intérprete de MongoDB. const da nombre a un valor; = asigna su valor inicial, las llaves forman un objeto, los corchetes forman un array y las comas separan entradas. El texto entre comillas es una cadena; 0 y 50 sin comillas son números. Un punto y coma termina una instrucción. schema describe un registro completo aceptado y valid lo envuelve como selector de MongoDB.

La política de cadenas aprobadas tiene la misma forma y solo cambia la regla del recuento. JSON.stringify convierte este objeto sencillo de política en texto JSON y JSON.parse lee una copia separada; cambiar la copia deja entonces intacta la política original. Este método de copia sirve para la política sencilla, no para conservar registros BSON almacenados. stringSchema.properties.stock.properties.available accede a la regla anidada que se reemplaza.

El pattern es una expresión regular para el texto permitido. ^ ancla el inicio y \z significa el final absoluto de la cadena; | separa alternativas, [1-9] permite un dígito de 1 a 9 y [1-4][0-9] permite dos dígitos de 10 a 49. Junto con 0 y 50, estas alternativas cubren exactamente el intervalo aprobado. Las cadenas JavaScript usan una barra invertida como escape, por lo que la fuente escribe \\z para pasar \z a MongoDB. Este anclaje de final absoluto también rechaza un salto de línea final; un anclaje ordinario de final $ puede permitir una coincidencia antes de ese salto de línea. $nor: [valid, repairable] selecciona registros que no coinciden con ninguno de los dos selectores.

Crea el archivo de política con un here-document. cat > policy.js escribe el texto siguiente en el archivo y reemplaza su contenido anterior. El delimitador entre comillas <<'POLICY' conserva literalmente los signos de dólar y el resto del texto JavaScript en el intérprete del sistema. Termina con POLICY solo en una línea. Copia el bloque completo:

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

El clasificador proporcionado evalúa este archivo guardado sin cambios usando una identidad de base de datos de solo lectura. Después usa tus selectores valid, repairable y bad en consultas reales de MongoDB:

./catalog classify policy.js

Inspecciona los grupos reales de documentos, no solo sus tamaños. valid debe contener KIT-101 y KIT-102, incluido el cero Long original. repairable debe contener KIT-103 y KIT-104 con sus cadenas originales sin cambios. outside debe contener de KIT-105 a KIT-111, incluidos el null original, el campo ausente, el array y el Double. La extensión completa de revisión del proveedor de KIT-105 debe seguir visible.

Si aparece un error de sintaxis JavaScript, el archivo no se ha aceptado como política funcional. Repite el here-document completo para reemplazar un archivo dañado y ejecuta después de nuevo el clasificador. Un error de sintaxis indica un problema de archivo o expresión; una conexión de base de datos no disponible es un error diferente y no significa que la clasificación no devolviera coincidencias. Este clasificador no ha reparado ni eliminado ningún dato.

Conservar los originales antes de eliminar registros inciertos

En este paso, guardarás un script de cuarentena, observarás una interrupción real después de su primera copia y volverás a ejecutar el script sin cambios para terminar. Empieza en Terminal, en /home/labex/project/catalog-desk.

Cada documento de cuarentena tendrá el _id original, un original que contiene el registro almacenado completo y reason: "outside-approved-catalog-contract". El motivo describe por qué se necesita revisión del proveedor; no afirma que las existencias sean cero. Un recuento original puede estar ausente, ser null, un array u otro tipo, por lo que copiar solo el valor mostrado o el nombre perdería pruebas.

Hay otro límite: al leer, el intérprete puede convertir valores numéricos BSON almacenados en números JavaScript ordinarios. Guardar un número JavaScript de apariencia entera puede producir entonces Int32 aunque el original fuera Double. Para cada copia y comparación de originales, usa el tercer argumento de find/findOne, {promoteValues: false, bsonRegExp: true}. Conserva los envoltorios numéricos BSON y los valores de expresión regular en lugar de convertirlos en equivalentes JavaScript. El segundo argumento {} es una proyección vacía que conserva todos los campos. EJSON.stringify(value, {relaxed: false}) produce Extended JSON canónico a partir de estos tipos conservados, de modo que un Double 12 y un Int32 12 siguen siendo diferentes durante la comparación.

El bucle del script lee una vez el grupo de registros que no cumplen ni el contrato válido ni el reparable y trata cada registro por separado. sort({_id: 1}) da ID ascendentes legibles y toArray() reúne los resultados. for (const doc of ...) repite las instrucciones encerradas para cada resultado. La operación upsert busca un registro de cuarentena por su ID original; upsert: true lo crea si no existe. $setOnInsert escribe el original y el motivo solo al crear ese registro, dejando intacta una copia existente. findOne vuelve a leer la copia almacenada.

if comprueba una condición. !== significa distinto y || que cualquiera de las dos condiciones es verdadera. Un original o motivo diferente debe provocar throw Error(...), que detiene el script con salida distinta de cero antes de borrar ese registro fuente. deleteOne elimina únicamente el ID fuente verificado. Los registros anteriores pueden haberse completado ya; son operaciones individuales, no un lote de todo o nada. Sin escritores concurrentes, una comparación completa correcta antes de cada borrado proporciona la vía de reintento usada aquí.

Escribe el script. El here-document QUARANTINE usa la misma sintaxis de escritura literal que antes:

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

Usa primero la conexión proporcionada copy-only. Es una identidad real de base de datos que permite leer y copiar, pero no borrar. El ejecutor evalúa policy.js y después quarantine.js sin cambios en una invocación del intérprete. Espera un error nativo de autorización y una salida distinta de cero cuando el script intente borrar por primera vez:

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

El mensaje incluye not authorized y un comando delete sobre equipment. La copia tuvo éxito antes de ese rechazo. Lee ambos registros reales:

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

KIT-105 existe ahora en ambos lugares. El original en cuarentena debe incluir su texto de existencias y la extension completa, incluidas sus etiquetas numéricas, de fecha y ObjectId. La fuente activa sigue intacta. Por ello, un comando fallido no implica que se hayan deshecho todas las escrituras anteriores.

Vuelve a ejecutar los archivos guardados exactos usando la conexión ordinaria de escritura del curso:

./catalog run policy.js quarantine.js

Un script correcto no requiere salida. La primera copia existente se verifica y se conserva; se elimina su fuente correspondiente. Los registros inciertos restantes se copian, verifican y eliminan por turnos. Lee los resultados reales:

./catalog show
./catalog archive

El catálogo activo debe contener solo de KIT-101 a KIT-104, sin cambios, incluidas las dos cadenas aprobadas pendientes de reparación. La cuarentena debe contener de KIT-105 a KIT-111 con documentos originales completos y el motivo acordado. En particular, el Double original de KIT-109 sigue mostrando $numberDouble y el original de KIT-107 sigue sin campo available.

Ejecuta de nuevo la migración:

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

No quedan registros fuera del contrato válido o reparable que mover a cuarentena, por lo que los originales guardados permanecen iguales. Esto es una repetición idempotente: repetir una operación completada no cambia su resultado de negocio. También se puede reanudar una interrupción posterior a una copia porque la clave del registro archivado impide una copia duplicada y la comparación rechaza una copia existente inconsistente. Si aparece ese error de conflicto, conserva la fuente y la copia archivada anterior para investigar; no lo «corrijas» sobrescribiendo esa copia ni borrando la fuente. El script no establece seguridad ante ediciones concurrentes entre su lectura y borrado, ni puede revertir el lote entero.

Resumen

Separaste los recuentos enteros utilizables, las cadenas explícitamente aprobadas y los registros inciertos con una política reutilizable de MongoDB. Después conservaste originales completos con sus tipos antes de borrar sus fuentes activas, observaste un fallo parcial real y reanudaste el mismo script guardado. El catálogo conserva ahora los registros utilizables y reparables mientras la cuarentena retiene pruebas para la revisión del proveedor. La conversión y la validación de escrituras futuras corresponden a la siguiente unidad independiente.