By

Empezando con MongoDB

Normalmente muchos de los que nos dedicamos a la informática, cuando escuchamos el término base de datos o database (en inglés) pensamos directamente en un tipo de base de datos, las bases de datos relacionales, como MySQL, PostgreSQL, etc; quizás porque son las más comunes y utilizadas durante mucho tiempo. Pero esto está cambiando.

Desde hace un tiempo hasta ahora han ido proliferando otro tipo de base de datos conocidas como NoSQL y seguro que conocéis algunas como: Memcached, una caché tipo clave-valor en RAM, su variante MemcacheDB que almacena datos clave-valor en disco usando BerkeleyDB; Redis, otra base de datos de tipo clave-valor; o MongoDB que a diferencia de las anteriores, está orientada a documentos.

¿Qué es MongoDB?

Primero, ¿Qué significa NoSQL? El término NoSQL se utiliza para indicar que dicha base de datos no utiliza el sistema relacional tan ampliamente utilizado. Las bases de datos NoSQL no se construyen sobre tablas y normalmente tampoco utilizan el lenguaje SQL para realizar consultas.

Muchos sistemas NoSQL utilizan una arquitectura distribuida y tolerante a fallos, lo que permite mantener los datos en varios servidores de forma redundante. De esta forma es bastante fácil escalar el sistema añadiendo más servidores. Normalmente este tipo de base de datos escala horizontalmente permitiendo administrar grandes cantidades de datos.

El nombre de MongoDB proviene de “humongous”, que significa enorme en inglés, y es una base de datos NoSQL software libre, escalable y de alto rendimiento escrita en C++.

Algunas de las características más importantes de MongoDB son:

  1. Almacenamiento orientado a documentos (document-oriented en inglés).
  2. Replicación y Alta Disponibilidad.
  3. Soporte de índices.
  4. Consultas, también basadas en documentos.
  5. Auto-Sharding, permitiendo escalar horizontalmente.
  6. GridFS, que permite almacenar ficheros de cualquier tamaño sin necesidad de complicar el entorno.

¿Orientado a documentos?

Si hubiera que destacar una de las características anteriormente citadas, en mi opinión, la más importante ahora mismo sería la de orientado a documentos o document-oriented, ya que se trata de un concepto fundamental para entender cómo funciona y cómo trabajar con MongoDB.

El almacenamiento de los datos en MongoDB utiliza documentos JSON (JavaScript Object Notation), contando con un esquema dinámico y totalmente flexible. De hecho se dice que MongoDB es schemaless (sin esquema).

¿Pero qué es un documento JSON?

De forma rápida y sencilla, un documento JSON no es más que descripción de un objecto en formato JSON, un formato muy rápido para el intercambio de datos y muy legible para el humano (human-readable en inglés).

Un documento JSON podria tener la siguiente forma:

{"username":"mviera", "age": 26, "city": "Sevilla"}

Este documento describe un usuario cuyo “username” es “mviera”, con una edad de 26 años y cuya ciudad es “Sevilla”.

En JSON hay 6 tipos de datos diferentes:

  • Number
  • String
  • Boolean (true o false)
  • Array
  • Object
  • null

Podéis aprender más sobre JSON en http://json.org/json-es.html

¿Cómo se instala?

La instalación de MongoDB es bastante sencilla. La versión actual de MongoDB es 2.2.3, en el momento en que escribo este artículo.

Su instalación se puede llevar a cabo a través del sistema de paquetería de nuestra distribución, APT, Aptitude, Yum… En Debian el nombre del paquete que contiene el servidor de MongoDB se llaman mongodb-server y el paquete que contiene el cliente y otras utilidades de MongoDB se llama mongodb-clients. Así que si estamos en un sistema Debian o derivado, podemos instalarlo de la siguiente forma:

[email protected]:~$ sudo apt-get update
[email protected]:~$ sudo apt-get install mongodb-server mongodb-clients

También podemos descargar los binarios desde la propia web oficial de MongoDB www.mongodb.org o el código fuente si lo que queremos es compilar nuestro propio MongoDB. Hay binarios disponibles para OS X, Linux, Windows para plataformas de 32 y 64bits; y Solaris 64 bits.

Si lo que queremos es realizar un despliegue en producción, recomendaría instalar utilizando el sistema de paquetería del sistema con el que contaremos con futuras actualizaciones de seguridad, etc. Si el objetivo es aprender MongoDB, podemos instalarlo tanto a través del sistema de paquetería, como descargando los binarios desde la web de MongoDB.

En este caso voy a descargar los binarios de MongoDB desde la sección de descargas de la web oficial, ya que lo utilizaré para aprender y enseñaros MongoDB. Son binarios precompilados, así que no será necesario instalar ningún paquete adicional en nuestro sistema.

Para ello, descargamos el tarball que se ajuste a nuestro sistema, en este caso, Linux x86-64:

[email protected]:~$ wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.2.3.tgz
[email protected]:~$ tar xzf mongodb-linux-x86_64-2.2.3.tgz                                         
[email protected]:~$ ln -s mongodb-linux-x86_64-2.2.3 mongodb                   
[email protected]:~$ cd mongodb
[email protected]:~/mongodb$ 

Nota: he creado un enlace simbólico llamado mongodb para evitar tener acceder a un directorio con un nombre tan largo y complejo.

Si listamos el contenido del directorio recién descomprimido, deberíamos tener algo similar a los siguiente:

[email protected]:~/mongodb$ tree 
.
|-- GNU-AGPL-3.0
|-- README
|-- THIRD-PARTY-NOTICES
`-- bin
    |-- bsondump
    |-- mongo
    |-- mongod
    |-- mongodump
    |-- mongoexport
    |-- mongofiles
    |-- mongoimport
    |-- mongooplog
    |-- mongoperf
    |-- mongorestore
    |-- mongos
    |-- mongosniff
    |-- mongostat
    `-- mongotop

1 directory, 17 files

Entre ellos se encuentran el fichero de licencia GNU-AGPL-3.0, el ficheroREADME y THIRD-PARTY-NOTICES; y el directorio bin/ que contiene todos los binarios y utilidades necesarios para empezar con MongoDB. Aunque hay una buena selección de utilidades, de momento sólo vamos a utilizar mongod y mongo para familiarizarnos con MongoDB.

Si la instalación de MongoDB se ha realizado utilizando el sistema de paquetería del sistema, estas utilidades y herramientas se encontrarán en los directorios habituales como /usr/bin. En caso de Debian, es posible consultar los ficheros instalados con la siguiente instrucción:

[email protected]:~$ dpkg -L mongodb-server mongodb-clients

Primera toma de contacto

Una vez tenemos MongoDB instalado en nuestro sistema, lo primero que debemos hacer es iniciar la base de datos. Para ello utilizaremos mongod. Si se ha instalado utilizando paquetería, es posible que se haya iniciado la base de datos automáticamente, especialmente en distribuciones basadas en Debian.

Antes de ejecutarlo se necesita especificar el directorio donde se alojará la base de datos. Por defecto, MongoDB buscará el directorio /data/db y si no lo encuentra, se producirá un error como el siguiente:

*********************************************************************
 ERROR: dbpath (/data/db/) does not exist.
 Create this directory or give existing directory in --dbpath.
 See http://dochub.mongodb.org/core/startingandstoppingmongo
*********************************************************************

Así que para evitar dicho error, crearemos un directorio local llamado data en la ruta actual y especificaremos dicho directorio con el parámetro – dbpath de la siguiente forma:

[email protected]:~/mongodb$ mkdir data 
[email protected]:~/mongodb$ ./bin/mongod --dbpath data

Si todo ha ido bien, deberíamos ver algo similar a la siguiente salida:

[email protected]:~/mongodb$ ./bin/mongod --dbpath data/
Wed Feb  6 01:36:44 [initandlisten] MongoDB starting : pid=1082 port=27017 dbpath=data/ 64-bit host=mongodb
Wed Feb  6 01:36:44 [initandlisten] db version v2.2.3, pdfile version 4.5
Wed Feb  6 01:36:44 [initandlisten] git version: f570771a5d8a3846eb7586eaffcf4c2f4a96bf08
Wed Feb  6 01:36:44 [initandlisten] build info: Linux ip-10-2-29-40 2.6.21.7-2.ec2.v1.2.fc8xen ...
Wed Feb  6 01:36:44 [initandlisten] options: { dbpath: "data/" }
Wed Feb  6 01:36:44 [initandlisten] journal dir=data/journal
Wed Feb  6 01:36:44 [initandlisten] recover : no journal files present, no recovery needed
Wed Feb  6 01:36:44 [websvr] admin web console waiting for connections on port 28017
Wed Feb  6 01:36:44 [initandlisten] waiting for connections on port 27017

Como se puede observar, MongoDB se encuentra funcionando en el puerto 27017 por defecto, su directorio de almacenamiento de la base de datos se encuentra en dbpath=data/ y además ofrece una consola de administración en el puerto 28017, a través de la cual podemos visualizar el log, listar bases de datos, etc.

Si echamos un vistazo al directorio data/ deberíamos ver algo similar a lo siguiente:

[email protected]:~/mongodb$ ls -l data/
total 8
drwxrwxr-x 2 mviera mviera 4096 Feb  6 01:36 journal
-rwxrwxr-x 1 mviera mviera    5 Feb  6 01:36 mongod.lock

Actualmente, sólo tendremos un directorio journal/ para recuperaciones de datos en caso de desastre y el fichero mongod.lock que almacena el PID del proceso mongod.

Interactuando con MongoDB

Una vez tenemos nuestra base de datos MongoDB iniciada, el siguiente paso será conectar a ella utilizando la consola o shell, en inglés, de Mongo. Para ello utilizaremos mongo ejecutando la siguiente instrucción:

[email protected]:~/mongodb$ ./bin/mongo
MongoDB shell version: 2.2.3
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
    http://docs.mongodb.org/
Questions? Try the support group
    http://groups.google.com/group/mongodb-user
> 

Si todo va bien, espero que sí, deberíamos ver el prompt de MongoDB. Por defecto Mongo, si no se le especifica lo contrario, siempre conectará a una base de datos test en la que podremos realizar todas las pruebas que queramos.

La shell de Mongo es una shell interactiva de JavaScript, por lo que podremos hacer uso de código JavaScript en caso de que nos sea necesario. Es decir, podemos realizar operaciones básicas de JavaScript como las siguientes en la shell de Mongo:

> 2 + 3
5
> 
> 10 * 2
20
> 
> var double = function (n1) { return n1 * n1 }
> double(2)
4
> double
function (n1) {
    return n1 * n1;
}
> stuff = [1, 2, 3, 4]
[ 1, 2, 3, 4 ]
> for (n in stuff) { print( parseInt(n) * 2 ) }
0
2
4
6
> var x = {name: "mviera", age: 26}
> x.age
26
> x.city = "Sevilla"
Sevilla
> x
{ "name" : "mviera", "age" : 26, "city" : "Sevilla" }

IMPORTANTE: Estas instrucciones NO son de MongoDB, es puro JavaScript. La consola de MongoDB es una shell interactiva de JavaScript.

Familiarización con el entorno…

Bien. Estamos conectados a la consola de Mongo, así que nuestro siguiente paso será crear una base de datos. Podemos comprobar cuál es la base de datos que estamos usando actualmente llamando al objeto db:

> db
test
> 

Crear una base de datos es tan fácil como ejecutar lo siguiente:

> use mydb
switched to db mydb
> db
mydb
> 

Podemos observar que el valor del objeto db ha cambiado a mydb. Lo siguiente será crear una colección, llamado collection en inglés. Las colecciones son los contenedores de los documentos en MongoDB. Serían las conocidas tablas que contienen los datos en las bases de datos relacionales.

Podemos crear una colección de prueba de la siguiente forma:

> db.createCollection("test")
{ "ok" : 1 }
> 
> show collections
system.indexes
test

Pero en MongoDB no es necesario crear una colección antes de introducir datos por primera vez, ya que Mongo comprobará previamente si existe la colección y si no, la creará automáticamente por nosotros.

> db.test2.insert({username:"mviera"})
> 
> show collections
system.indexes
test
test2

Como se puede observar, la colección test2 se ha creado automáticamente al crear el documento {username:"mviera"} dentro de él. Con esto ya he introducido rápidamente la forma de introducir datos en una colección de MongoDB. De todas formas, la sintaxis de uso es la siguiente:

db.<nombre_coleccion>.verbo()

Donde verbo() puede ser:

  • insert: para insertar documentos en la colección.
  • find: para buscar o seleccionar documentos dentro de la colección.
  • count: para contar el total de documentos dentro de una colección.
  • update: para actualizar uno o varios documentos dentro de una colección.
  • remove: para eliminar documentos de la colección.
  • drop: para eliminar una colección.

Hagamos unos ejercicios

Nuestra base de datos actual se llama mydb:

> db
mydb
> 

y actualmente nuestra base de datos sólo contiene tres colecciones: system.indexes (creada por MongoDB automáticamente), test y test2:

> show collections
system.indexes
test
test2
> 

Crearemos tres documentos que describirán usuarios dentro de una colección que se llamará users. Los datos a almacenar en los documentos serán: username, age (edad), y ciudad. Para ello utilizaremos el método insert y podemos hacerlo de varias formas:

  1. Directamente especificando el documento JSON en el método insert():

    > db.users.insert({username:"mviera",age:26,city:"Sevilla"})
    > db.users.insert({username:"robot", age:32, city:"Cadiz"})
    > db.users.insert({username:"testuser", age:20, city:"Malaga"})
    
  2. Almacenar los documentos en variables JavaScript y posteriormente utilizar dicha variable en insert():

    > user1 = {username:"mviera",age:26,city:"Sevilla"}
    { "username" : "mviera", "age" : 26, "city" : "Sevilla" }
    > user2 = {username:"robot", age:32, city:"Cadiz"}
    { "username" : "robot", "age" : 32, "city" : "Cadiz" }
    > user3 = {username:"testuser", age:20, city:"Malaga"}
    { "username" : "testuser", "age" : 20, "city" : "Malaga" }
    > 
    > 
    > db.users.insert(user1)
    > db.users.insert(user2)
    > db.users.insert(user3)
    
  3. Utilizando un for loop de JavaScript en la shell de Mongo para automatizar la tarea:

    > users = [user1, user2, user3]
    > for (i = 0; i < users.length; i++) { db.users.insert(users[i]) }
    

Nota: Realmente en los tres casos hemos hecho lo mismo, pero solamente quería mostrar la potencia que ofrece la shell de Mongo, una shell de JavaScript.

Si consultamos de nuevo las colecciones disponibles en nuestra base de datos mydb, podremos observar que MongoDB ha creado la colección users por nosotros, y de forma automática:

> show collections
system.indexes
test
test2
users
> 

Después de esto, deberíamos ser capaces de saber cuántos documentos hay en la colección users, pero podemos cerciorarnos utilizando el método count en nuestra colección:

> db.users.count()
3
> 

¡Perfecto! Eso quiere decir que hasta ahora ha ido todo de maravilla. Ya solo nos falta poder seleccionar dichos documentos, es decir, poder recuperar dicha información de la base de datos. Esto lo conseguiremos con el método find, ejecutando lo siguiente:

> db.users.find()
{ "_id" : ObjectId("51118e261caf692fdfc89517"), "username" : "mviera", "age" : 26, "city" : "Sevilla" }
{ "_id" : ObjectId("51118e261caf692fdfc89518"), "username" : "robot", "age" : 32, "city" : "Cadiz" }
{ "_id" : ObjectId("51118e261caf692fdfc89519"), "username" : "testuser", "age" : 20, "city" : "Malaga" }
> 

¡Bien! Pero… seguro que os estáis preguntando por ese campo llamado _id… ¿Qué significa?¿Por qué está ahí?

En MongoDB todo documento tiene que tener un identificador único dentro de la colección que puede ser especificado explicitamente con el campo _id dentro del documento, y si no se especifica ninguno, Mongo creará uno automáticamente por nosotros sin que tengamos que preocuparnos de ello.

De esta forma, podemos introducir un nuevo documento que sí tenga especificado un _id por nosotros:

> db.users.insert({_id: 101, username:"iloveyou", age: 12, likes: "destroy your computer"})
>

Y si ahora volvemos a recuperar todos los documentos de la colección users, debería aparecer el nuevo documento en la lista:

> db.users.find()
{ "_id" : ObjectId("51118e261caf692fdfc89517"), "username" : "mviera", "age" : 26, "city" : "Sevilla" }
{ "_id" : ObjectId("51118e261caf692fdfc89518"), "username" : "robot", "age" : 32, "city" : "Cadiz" }
{ "_id" : ObjectId("51118e261caf692fdfc89519"), "username" : "testuser", "age" : 20, "city" : "Malaga" }
{ "_id" : 101, "username" : "iloveyou", "age" : 12, "likes" : "destroy your computer" }
>

¿Recordáis lo que comentaba al principio sobre schemaless? Si se observa el listado de documentos anterior, se puede ver que los campos de los tres primeros documentos son username, age y city. Pero sin embargo, el nuevo documento introducido no solo no utiliza el campo city sino que además incluye uno nuevo llamado likes.

Esto es debido a la flexibilidad que ofrece MongoDB en sus esquemas, es decir, no todos los documentos tienen que tener los mismo campos, sino que pueden incluirse o no ciertos campos según convenga. De hecho, podría haberse incluido el campo city con un valor null pero si no lo incluimos, en el futuro ahorraremos espacio en la base de datos.

Retrospectiva

Después de todo esto, algo habrá ocurrido en nuestro directorio data/ donde se alojan las bases de datos. Si listamos el contenido del directorio, deberíamos ver algo como lo siguiente:

[email protected]:~/mongodb$ ls -lh data/
total 209M
drwxrwxr-x 2 mviera mviera 4.0K Feb  6 02:40 _tmp
drwxrwxr-x 2 mviera mviera 4.0K Feb  6 03:22 journal
-rwxrwxr-x 1 mviera mviera    0 Feb  6 03:22 mongod.lock
-rw------- 1 mviera mviera  64M Feb  6 03:08 mydb.0
-rw------- 1 mviera mviera 128M Feb  6 02:12 mydb.1
-rw------- 1 mviera mviera  16M Feb  6 03:08 mydb.ns

Podemos observar que se han creado dos ficheros mydb.0 y mydb.1 que contienen los datos; y otro fichero llamado mydb.ns que contiene el namespace. Si no estoy equivocado, y si lo estoy, por favor corregidme, MongoDB creará nuevos ficheros cuando los actuales ocupen un tamaño de 2G, con el fin de facilitar el traslado de estos fichero a través de la red, etc.

Apaga y vámonos

Por último y para terminar el post, os comentaré cómo podemos para el servidor Mongo de forma correcta. Es muy sencillo, solamente tenemos que cambiar a la base de datos llamada admin:

> use admin
switched to db admin
> db
admin
> 

y ejecutar el método shutdownServer de la siguiente forma:

> db.shutdownServer()
Wed Feb  6 03:29:30 DBClientCursor::init call() failed
Wed Feb  6 03:29:30 query failed : admin.$cmd { shutdown: 1.0 } to: 127.0.0.1:27017
server should be down...

¡Y esto es todo por ahora! Espero que el post haya sido de vuestro agrado y agradeceros a aquellos que hayáis llegado leyendo hasta esta linea. ¡Gracias!

Con respecto a MongoDB es tipo de base de datos nueva para mi y que me está gustando bastante. Estoy estudiándola y aprendiendo a través de un curso de 10gen, la empresa que la desarrolla. El curso es totalmente gratuito, no se si es posible apuntarse a estas alturas, ya que lleva tres semanas, pero os recomiendo que lo tengáis en cuenta para próximas ediciones. Podeis encontrarlo en https://education.10gen.com

¡No olvideis comentar vuestras dudas o sugerencias!

Un saludo, Manu.