El año 2015 se despidió dejándonos disponible la última versión 3.2 de MongoDB con muchas novedades importantes que aumentan su número de funcionalidades enriqueciendo, más si cabe, esta base de datos. Con esta nueva actualización MongoDB mejora tanto el desarrollo como las herramientas que facilitan su uso por parte de los usuarios.

Entre las nuevas características ofrecidas en la versión 3.2 de MongoDB se encuentra la validación de documentos. Nos permite crear criterios que deben cumplir los documentos para poder ser introducidos o actualizados en una colección.

Para llevarlo a cabo se utilizan validadores con los que describimos pautas que deben cumplir los documentos en lenguaje MQL como si se tratase del criterio de una búsqueda o actualización en MongoDB.

Por ejemplo, si tenemos una colección de usuarios en la que todos los documentos deben cumplir unas determinadas propiedades:

Que exista un campo llamado username de tipo string

Que tengan una dirección de correo de dominio @paradigmadigital

Que disponga de un campo status cuyos posibles valores sean: pending, partial validation, registered...

Declararíamos el siguiente validador utilizando el operador db.createCollection():


db.createCollection("users",
{
validator: {$and:
  [
    {"username":{$type: "string"} },
    {"username":{$exists: true} },
    {"email": {$regex: /@paradigma.local$/} },
    {"status":{$in: ["pending","partial validation","registered" ]
} }
  ]
 }
})

Donde el primer parámetro es el nombre de la colección y el segundo es el documento que describe el validador. Si intentamos introducir un documento que no cumpla el validador obtendremos un mensaje como el del siguiente ejemplo:


db.users.insert(
  {"username":1,
  email:"evalero@paradigma.local"
  })
WriteResult({
     "nInserted" : 0,
     "writeError" : {
          "code" : 121,
          "errmsg" : "Document failed validation"
     }
})

Vemos que indica que en el documento ha fallado la validación, pero no indica el campo que ha violado el validador. Se prevé que en futuras releases de MongoDB el validador nos indique el criterio que nuestro documento no ha cumplido.

Los validadores también disponen de una opción que afecta al aplicar actualizaciones sobre los documentos existentes en la colección. Por una parte tenemos el parámetro validationLevel, cuando es configurado con el parámetro strict aplica el validador tanto para las inserciones como para las actualizaciones sobre los documentos existentes. Sin embargo, si lo ajustamos al valor moderate, el validador es aplicado sobre las inserciones pero no se aplicaría sobre los documentos actualizados que violan las restricciones del validador.

Si en la colección usuarios tenemos los siguientes documentos:


{
      "_id" : ObjectId("5712d1df6fac6a2d1442e8ec"),
      "username" : "evalero",
      "email" : "evalero@paradigma.local",
      "status" : "registered"
}
{
      "_id" : ObjectId("5712d2226fac6a2d1442e8ed"),
      "username" : "mongodbFan",
      "email" : "mongodbrules@gmail.com",
      "status" : "pending"
}

Procedemos a modificar el validador anteriormente mostrado, pero añadiendo el valor validationLevel con valor moderate sobre la misma colección:


db.runCommand( {
   collMod: "users",
   validator: {$and:
     [
       {"username":{$type: "string"} },
       {"username":{$exists: true} },
       {"email": {$regex: /@paradigma.local$/} }
     ]
    },
    validationLevel: "moderate"
} )

Y actualizamos el documento cuyo nombre de usuario es “mongodbFan”:


db.users.update (
    {"username": "mongodbFan"},
    {$set:
      {"status":"registered"}
    }
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

El documento, a pesar de no cumplir con el criterio del dominio del correo paradigma.local, nos ha permitido actualizarlo. No ocurriría lo mismo si el valor de validationLevel fuera strict.

Adicionalmente, también podemos definir un validador sobre una colección e indicarle qué acción debe levar a cabo al recibir un documento inválido. Para ello, a la hora de declarar el validador podemos configurar el parámetro validationAction, cuyo valor por defecto es error e imposibilita la inserción del documento. Sin embargo, puede configurarse con el valor warn y permite la inserción del documento, aunque dejará una traza en los logs del servidor de base de datos indicando que ha sido insertado un documento que no ha cumplido el validador. Por ejemplo, modificamos el validador de la colección anteriormente mostrada con valor warn para el parámetro validationAction:


db.runCommand( {
   collMod: "users",
   validator: {$and:
     [
       {"username":{$type: "string"} },
       {"username":{$exists: true} },
       {"email": {$regex: /@paradigma.local$/} }
    ]
   },
    validationLevel: "moderate",
    validationAction: "warn"
} )

Y ahora procedemos a actualizar otro documento de la colección:


db.users.update({username:"evalero"},{$set:{email:"evalero@otrodominio.com"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

La inserción se ha llevado a cabo en la base de datos, aunque en el log del servidor observaremos una entrada como la siguiente:


2016-04-17T02:36:46.583+0200 W STORAGE [conn1] Document would fail validation collection: validator.users doc: { _id: ObjectId('5712d1df6fac6a2d1442e8ec'), username: "evalero", email: "evalero@otrodominio.com", status: "registered" }

Por último, si deseamos saber la información sobre los validadores aplicados sobre las colecciones de una base de datos podemos utilizar el comando db.getCollectionInfos():


db.getCollectionInfos()
[
       {
               "name" : "users",
               "options" : {
                    "validator" : {
                         "$and" : [
                              {
                                    "username" : {
                                         "$type" : "string"
                                    }
                              },
                              {
                                    "username" : {
                                         "$exists" : true
                                    }
                              },
                              {
                                    "email" : {
                                    "$regex" :
/@paradigma.local$/
                                    }
                              }
                     ]
                },
                "validationLevel" : "moderate",
                "validationAction" : "warn"
           }
      }
]

Esta nueva feature es muy útil para poder evitar inconsistencias de datos en nuestra estructura de los documentos de una colección que puedan desembocar en un bug en la aplicación. Sin embargo, esta funcionalidad debería usarse como complemento a una validación realizada a través de aplicación, ya que si no obligaría a tener que llegar hasta la base de datos para saber si el documento tiene el formato que esperamos almacenar y, por tanto, podríamos penalizar el rendimiento de nuestra aplicación.

Además, ya que el validador no indica sobre qué campo estamos violando la validación, es interesante utilizar validadores desde la aplicación, como por ejemplo JSON Schema, para que el usuario que ha introducido el dato erróneo tenga feedback para poder solucionar el problema con sus datos.

La validación de documentos es de gran utilidad en entornos productivos para no tener problemas con los datos introducidos en las colecciones de nuestra base de datos. Además puede ser de apoyo para las migraciones del modelo de nuestros datos, ya que podemos aplicar el validador con el nivel de validación y acción a tomar que necesitemos según el tipo de cambio o actualización del esquema que estemos llevando a cabo.

Otro caso de uso realmente interesante es cuando distintos equipos de desarrollo están trabajando sobre una misma base de datos. Utilizando validadores se evitará en gran medida los posibles errores y duplicidades de campos ya que se obtiene unas reglas únicas que deberán cumplir los documentos de cada colección.

Probablemente en el futuro encontraremos aún más madurez sobre esta feature y nos devuelva más información a la hora de incumplir los validadores o nos permita hacer validaciones más complejas comparando una clave del documento con otra (por ejemplo {idioma: {$eq : idiomaSeleccionado}} )

Fuentes:

https://docs.mongodb.org/manual/core/document-validation/

https://www.mongodb.com/blog/post/document-validation-part-1-adding-just-the-right-amount-of-control-over-your-documents

https://www.mongodb.com/blog/post/document-validation-part-2-putting-it-all-together-a-tutorial

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete