En esta nueva entrega de la serie dedica a las bases de datos NoSQL vamos a ver que es MongoDB como instalarla y como empezar a trabajar con ella. Después del par de posts dedicados a CouchDB, vamos a darle cancha a otra de las bases de datos NoSQL que más repercusión está teniendo ultimamente, MongoDB.
Introducción
El nombre viene del término inglés "humongous" (colosal) y podemos definirla como una BD documental sin esquema, escalable y de alto rendimiento. Así es como nos la presentan en la entradilla de su página oficial, pero para entendernos la podríamos llamar la "MySQL de las bases de datos NoSQL". Vale, el apelativo tal vez parezca algo gratuito pero en cierto modo tiene sentido al ver que es una BD rapidísima, sencilla en la funcionalidad ofrecida y que, como hizo MySQL hace años, se está ganando la atención de mucha gente.
De forma similar CouchDB, esta BD nos permite almacenar documentos sin un schema predefinido, pero ofrece algunas pequeñas diferencias en cuanto a la organización de la información. En MongoDB partimos del concepto de BD que podría ser análogo al que usamos en las BD de toda la vida y además también tenemos "colecciones" que serían como las tablas. Pero a diferencia de MySQL o similares, aquí podremos meter documentos con distintos campos dentro de la misma colección, de modo que esta estructura tiene más un sentido de categorizacion de los elementos que contiene sin obligar a que estos tengan los mismos datos. P.ej. en nuestra BD para un blog podríamos tener una colección "autores" y otra "posts"...
Es importante destacar que MongoDB ofrece un rendimiento en inserciones mucho mayor que CouchDB (e incluso algo mejor que MySQL). Vamos, que va como una moto... Pero esto es a costa de sacrificar ciertos "detalles" como p.ej las transacciones. Así que si estáis pensando en implementar un sistema que precise de estas es mas sencillo usar una base de datos relacional que las soporte (p.ej. MySQL con MyISAM no valdría).
Y como resultado colateral la durabilidad no se ve asegurada con un log de transacciones como el que se puede usar en BD relacionales. ¿Y que quiere decir esto?, pues que cuando pete la base de datos podremos repararla pero es posible que perdamos algunas de las ultimas modificaciones (aquellas que han sido realizadas pero todavia no han sido enviadas a disco). Seguro que a alguno os ha entrado el sudor frío, pero resulta que si estáis usando MySQL con el backend MyISAM estáis asumiendo un riesgo parecido :)
Para evitarnos problemas en estos casos los autores de MongoDB nos recomiendan usar algun tipo de replicación en otra maquina, de forma que tengamos una BD que realize las mismas escrituras que la principal. De esta forma tambien ganamos en tener una copia viva de nuestra BD que puede reemplazar a la principal si esta cae. O servir como otra fuente de lectura de datos para aportar mas rendimiento todavía. Lo bueno es que la configuración de esta replicación es bastante sencilla.
Primeros pasos
Pero mejor será que la veamos en funcionamiento, de esta forma nos daremos cuenta de lo poco que cuesta empezar a trabajar con MongoDB. Lo primero es bajarse el paquete correspondiente de la sección de descargas (http://www.mongodb.org/display/DOCS/Downloads) de la página web oficial. En este paquete nos viene ya todo compilado y preparado para ejecutar directamente el servidor de base de datos. Vamos a ello.
Lanzamos el ejecutable "bin/mongod" pasandole el parametro dbpath donde le indicamos el directorio donde nos guardará los datos (debe estar creado antes). P.ej:
Hecho lo cual deberíamos ver por la salida estándar los mensajes del servidor tal que así:
Si vemos algún mensaje de error tal vez sea porque el directorio no está creado o no se lo estamos pasando bien (en nuestro ejemplo "data" está justo en el directorio descomprimido de MongoDB aunque tal vez convendría uno más tipico como p.ej. /var/lib/mongodb)
Ahora abrimos otro terminal y lanzamos el shell "bin/mongo" desde el que podremos realizar tareas de mantenimiento y consulta sobre las bases de datos. Veremos algo así:
Si tecleamos "help" podemos ver algunos comandos disponibles, pero lo mas sencillo es que vayamos viendo los mas importantes poco a poco.
Una cosa interesante es que el shell esta basado en un motor Javascript (SpiderMonkey ahora, es posible que se use el V8 de Google en un futuro) de forma que podemos ejecutar codigo javascript intercalado con nuestras órdenes.
Como hemos visto en el mensaje inicial se esta usando la base de datos "test", si queremos podemos cambiar a otra usando la orden "use":
Si os fijáis al listar las BDs existentes con "show dbs" no nos muestra la "test" ni la "prueba" a la que acabamos de cambiar. Esto es así porque MongoDB crea las bases de datos (y las colecciones) cuando insertamos algo en ellas. Hasta que lo hagamos no veremos ninguna de estas estructuras.
Trabajando con documentos
Para insertar documentos en nuestra BD usaremos la orden "db.coleccion.insert":
MongoDB soporta varios tipos para sus datos. Tenemos los tipicos del formato JSON (string, integer, boolean, double, null, array, object) asi como varios especiales (date, object id, binary data, regular expression, code).
Teniendo en cuenta esto podríamos insertar un registro como este:
Como tenemos un interprete Javascript todo para nosotros podemos hacer como como esta:
Para poder consultar los datos de nuestra BD podemos usar la orden "db.coleccion.find":
En caso de que tengamos muchos resultados MongoDB solo muestra los 10 primeros y podemos ir viendo los grupos restantes con la orden "it".
Aquí es interesante ver como MongoDB nos ha generado un campo "_id" para cada una de los documentos insertados. Este campo id es autogenerado por defecto y está indexado (hace que las busquedas por él sean mucho mas rapidas). Si ya tenemos un código que nos pueda servir como identificador se lo podemos pasar en la inserción y MongoDB lo usará.
Lo interesante es que a find le podemos pasar un documento de forma que MongoDB nos devolverá aquellos documentos cuyos valores coincidan. Esto es lo que se conoce como "query by example". P.ej:
Un detalle importante es que podemos utilizar el tipo "regular expression" en estas consultas:
Por supuesto podemos pasarle un documento con varios campos al "find" y nos devolveria los documentos para los que coinciden todos ellos.
Además de por campos especificos podemos consultar con unos operadores especiales, entre los que se encuentran:
- $gt: Valores mayores que el que le pasamos.
- $lt: Valores menores que el que le pasamos.
- $gte: Valores mayores o iguales que el que le pasamos.
- $lte: Valores menores o iguales que el que le pasamos.
- $ne: Distinto
- $in: Si el valor esta entre los que le pasamos como array.
Aparte hay más operadores de consulta que podemos ver en la sección de la documentación correspondiente (consultas avanzadas)
Veamos unos ejemplos:
Después del "find" podemos usar varias funciones que nos permiten obtener distintos resultados.
P.ej. si queremos saber el numero de resultados obtenidos usamos "count":
Con limit y skip podemos obtener un rango de resultados determinado. Limit nos dice cuantos documentos se obtienen y skip se salta los n primeros resultados de la consulta (esto es muy útil para mostrar los resultado paginados):
Además del "find" podemos usar la orden "findOne" si sólo necesitamos un resultado:
Aparte de estas facilidades de consulta tenemos la opción de utilizar consultas map-reduce (si habéis leido los posts de CouchDB os sonará). Y si necesitamos que las busquedas sobre un campo determinado sean lo más rápidas posibles podemos crear índices con la orden "db.coleccion.ensureIndex":
Si necesitamos modificar un objeto podemos recuperarlo con una consulta, realizar los cambios que deseemos y utilizar el comando "db.coleccion.save":
Aquí hay que tener cuidado con un detalle de Javascript: el mes que se le pasa al objeto date va de 0 a 11 (0=Enero, 1=Febrero...).
Como podéis imaginar esta es un forma poco óptima ya que tenemos que realizar 2 accesos: uno para recuperar el objeto y otro para hacer "save" de la modificación. Pero por suerte, MongoDB nos ofrece una orden "db.coleccion.update" que nos permite realizar modificaciones en un sólo paso.
Esta orden tiene todos estos parámetros:
donde:
- criterio: expresión que nos permite seleccionar los documentos que se actualizarán.
- obj: objeto a modificado o bien expresión con operadores $.
- upsert: true si esto es un "upsert"
- multi: true si queremos modificar todos los resultados de "criterio" (por defecto solo se modifica el primero)
Los operadores $ son parecidos a los vistos en las consultas, pero ahora permiten realizar acciones específicas de modificación de datos. Por ejemplo, tenemos $inc que nos permite incrementar el valor del campo, $set si queremos cambiar un valor, $push para introducir un nuevo elemento en un array, etc...
El parámetro "upsert" quiere decir que esta actualizacion creará un nuevo registro en caso de que el "criterio" no devuelva ningún documento.
Estas operacion realizadas con los operadores $ tienen la característica de ser atómicas lo cual quiere decir que o suceden o no suceden, sin termino medio.
Por último si queremos eliminar registros podemos utilizar la orden "db.coleccion.remove":
No hay joins... ¡socorro!
El hecho de no disponer joins es parte fundamental de la escalabilidad de muchos sistema NoSQL. Por ello la información se guarda de una forma menos normalizada que en una BD relacional y se puede acceder a parte de ella sin necesidad de joins. Esto tiene mucho sentido si pensamos que la información puede estar distribuida en varias máquinas, y si hicieramos join contra los datos que están en otra maquina esto anularía en parte el beneficio de rendimiento obtenido. Además si tenemos que mostrar un post con sus comentarios podemos obtenerlo todo leyendo un sólo documento de disco, con los beneficios consiguientes de estar leyendo todos los datos secuencialmente.
Por ejemplo, si tenemos un blog podemos guardar los comentarios dentro de cada post (esto iría en una colección llamada "posts"):
Lo bueno es que podemos realizar consultas sobre los datos de estos documentos embebidos de forma que podemos obtener los posts que contengan un comentario hecho por Manolo:
Claro, estamos acostumbrados a partir todo en tablas, pero el hecho de disponer de un tipo array y poder embeber subdocumentos dentro de otros nos permite eliminar algunas de las tablas que crearíamos en una típica aplicación sobre una BD relacional. De todas formas, lo normal es que haya varias colecciones, asi que nos toca guardarnos un campo que apunte al elemento al que queremos enganchar (normalmente el id).
En nuestro ejemplo de blog podriamos tener otra coleccion 'autores', donde guardariamos los autores identificados por su nick (angel en el ejemplo), de forma que podemos cruzar facilmente los datos con la coleccion 'posts'.
Accediendo desde nuestro código
En los ejemplos mostrados anteriormente hemos estado usando el shell de la BD, pero lo que queremos es acceder a los datos desde nuestro código. Para ello MongoDB nos ofrece drivers para los principales lenguajes de programación, ofreciendo una comunicación lo más eficiente posible a nuestro servidor Mongo.
Por ejemplo, asi sería un programa en Python (usando el driver pymongo):





Comentarios
Hola Gabriel,
Para añadir nuevos elementos a un array se usa la orden "push".
Suponiendo que el documento a actualizar este en una coleccion llamada "ejemplo" :
db.ejemplo.update( {'autor':'angel', 'titulo':'Mi primer post...'}, {$push: {'comentarios': {'autor':'Perico', 'texto':'Que bien'}}})
Recuerda que la primera parte del update sirve para seleccionar el documento/s a actualizar y la segunda contiene los datos de la actualizacion.
Gracias.
Hola muy buen intro... Pero como haria para poderle agregar mas comentarios, al key comentario, edl ultimo ejemplo.
Hola Pablo,
A bote pronto no he encontrado nada que recomiende ir por este camino:
community.jboss.org/message/530214#530214
Aparte de esto es posible que si usaras EC2 te encontraras con limitaciones si trabajas con las instancias "small". El problema es que estas son de 32 bits y MongoDB te limita a un máximo de 2.5Gb de datos para este tipo de procesador. Por supuesto esta limitación no se aplica para maquinas de 64 bits, asi que lo más seguro es que tuvieras que trabajar con instancias mas grandes (y caras...)
blog.mongodb.org/post/137788967/32-bit-limitations
En tu caso te topas con nos de los problemas de estas soluciones nuevas, que todavía no están tan integradas en herramientas como las BD SQL.
Lo más importante es no perder la visión "ingenieril" del tema... al final no deja de ser una herramienta con la que ganas algunas cosas, pero pierdes otras. Como dicen los anglosajones "there is no silver bullet"
Esta link ya lo he puesto en un comentario del post sobre CouchDB, pero merece la pena volverlo a poner:
www.slideshare.net/ezmobius/where-do-i-put-this-data-lessql
Tengo una consulta , tenemos una applicacion trabajando en este momento con jbpm , ya que no estamos en produccion , hemos trabajado toalmente con hibernate , si tenemos la idea de ir a una base noSQL.
Unsa de las posibilidades es en el momento de ir a produccion , instalar en un ambiente de amazon EC2 usando MongoDB.
Entonces , tengo un problema , como puedo trabajar con mangoDB y jbpm , ya que jbpm trabaja con hibernate.
Se puede crear un dialect para mongoDB ?
Si alguien se topo con algo asi , le agradeceria su ayuda.
Saludos
Gracias a ti por el comment.
Lo de lo limitar el numero de resultados se hace con el "limit" (esto esta en el tutorial) y para la ordenación se puede usar "sort". Un ejemplo:
db.perros.find().sort({'nombre':1})
Y si queremos ordenar por orden descendente:
db.perros.find().sort({'nombre':-1})
Gracias por el tutorial!
Suscripción de noticias RSS para comentarios de esta entrada.