Los gestores de bases de datos relaciones parecían algo eterno e inamovible en al informática por mucho que aparecieran nuevos lenguajes y entornos de programación. Pero las cosas cambian. Ahora las grandes BBDD no son sola las del gobierno y los bancos, ahora hay muchas aplicaciones que presentan un perfil distinto al de las aplicaciones tradicionales: son aplicaciones que también manejan muchos datos pero en los que sobre todo se hace mucha consulta. Y para acelerar al máximo el servicio de esas consultas el modo tradicional de servir los datos ha cambiado. El modelo entidad relación salta por los aires y se habla de cosas como noSQL.

Aparte de eso, hay gestores de BD que facilitan el desarrollo de aplicaciones web en las que el peso del programa recae sobre el cliente. Hablamos de aplicaciones javascript cómo no. Mongo habla Javascript y guarda los datos en formato JSON, y eso le pone las cosas a huevo a Javascript. Este gestor, de todas formas, no es más que una pieza del puzzle, y en este post digamos que nos damos un primer baño en esta tecnología desde el punto de vista de quien está acostumbrado a lidiar con BD relacionales y busca las equivalencias. Bueno, "Talk! Talk is for lovers Merlin!", vamos a entrar en harina.

Inicio mongo

En mi caso está instalado en un linux a través del gestor de paquetes, sin problema. Si no eres capaz de instalarte un mongo en tu equipo, en serio, deja la informática y serás mucho más féliz. No insistas, esto no es lo tuyo. Para el resto
Iniciamos el servidor:

linux:~# /etc/init.d/mongodb start
Rather than invoking init scripts through /etc/init.d, use the service(8)
utility, e.g. service mongodb start

Since the script you are attempting to invoke has been converted to an
Upstart job, you may also use the start(8) utility, e.g. start mongodb
mongodb start/running, process 1950
linux:~#
El cliente: mongo

Para conectarnos al gestor de mongo podemos poner simplemente mongo o especificar una url o un BBDD, tal y como dice la ayuda:

linux:~# mongo -h
MongoDB shell version: 1.2.2
usage: /usr/lib/mongodb/mongo [options] [db address] [file names (ending in .js)]
db address can be:
  foo                   foo database on local machine
  192.169.0.5/foo       foo database on 192.168.0.5 machine
  192.169.0.5:9999/foo  foo database on 192.168.0.5 machine on port 9999

Al no especificar nada se conectará a la máquina local. Podriamos iniciar la sesión indicando una BBDD

linux:~# mongo test
MongoDB shell version: 1.2.2
url: test
connecting to: test
type "exit" to exit
type "help" for help
>

O bien podemos indicar la BBDD una vez iniciada la sesión:
linux:~# mongo
MongoDB shell version: 1.2.2
url: test
connecting to: test
type "exit" to exit
type "help" for help
> use test;
switched to db test
>

En una primera sesión, vamos a insertar un registro y luego a hacer una consulta. Esto se hace de una manera peculiar. Primero definimos una variable en la que creamos el registro. Luego insertamos esa variable en una "tabla" llamada tecnologias y finalmente hacemos una consulta para ver si efectivamente se ha metido:

>
> tecnologia={ nombre: 'angular', descripcion: 'Framework javascript', creación: new Date('01/01/2012')};
{
	"nombre" : "angular",
	"descripcion" : "Framework javascript",
	"creación" : "Sun Jan 01 2012 00:00:00 GMT+0100 (CET)"
}
> db.tecnologias.insert(tecnologia);
> db.tecnologias.find();
{ "_id" : ObjectId("51fe59da1b8c7a4eeb00eeb0"), "nombre" : "angular", "descripcion" : "Framework javascript", "creación" : "Sun Jan 01 2012 00:00:00 GMT+0100 (CET)" }
>

¿Qué formato es este? Esa sintaxis para definir variables, llamar a métodos es JAVASCRIPT. Y el formato de los datos es JSON. Con eso igual ya te empiezas a oler el percal. Mongo nos facilita enormemente el desarrollo de aplicaciones donde el peso lo lleva el lado del cliente.

Ortodoxos del E/R abandonad toda doctrina

Vale hemos creado una especie de registro, lo hemos insertado en una tabla que no estaba previamente definida ni nada... con un par! Eso no es todo, ahora mira:

> tecnologia={ nombre:'backbone',descripcion:'Framework javascript', creación: new Date('01/02/2012'), lenguaje: 'javascript'}
{
	"nombre" : "backbone",
	"descripcion" : "Framework javascript",
	"creación" : "Mon Jan 02 2012 00:00:00 GMT+0100 (CET)",
	"lenguaje" : "javascript"
}
> db.tecnologias.insert(tecnologia);
> db.tecnologias.find();
{ "_id" : ObjectId("51fe59da1b8c7a4eeb00eeb0"), "nombre" : "angular", "descripcion" : "Framework javascript", "creación" : "Sun Jan 01 2012 00:00:00 GMT+0100 (CET)" }
{ "_id" : ObjectId("51fe5a771b8c7a4eeb00eeb1"), "nombre" : "backbone", "descripcion" : "Framework javascript", "creación" : "Mon Jan 02 2012 00:00:00 GMT+0100 (CET)", "lenguaje" : "javascript" }
> tecnologia={ nombre:'txootx',descripcion:'Lenguaje de programación OO', autores: ['Mikel','Pello'], creación: new Date('01/02/2000'), lenguaje: 'txootx'};
> db.tecnologias.insert(tecnologia);
>

¿Pero esto qué es? ¿Ahora metemos un registro que tiene otro campo más? Y luego... ¿un registro con un campo de varios valores? Tanta herejía provocará sangre en los ojos de los más puretas. Pues sí amigos iba a decir que Mongo tiene más tragaderas que HTML5 pero dejémoslo en que es muy fléxible. Olvida la rigidez de los SGBD relaciones de toda la vida. Esto es una fieshta, pero eso esí, la carga de controlar estos formatos variables va a recaer sobre el código del programador, lo cual también se ha considerado un comportamiento propio de aquellos que llevan la marca del CAOS. Obviamente, el campo _id es generado por Mongo de forma automática.

Consultas

Para hacer consultas sobre las tablas podemos usar la función find. Un find sin parámetros como por ejemplo db.tecnologias.find(); sería equivalente a un select * from tabla. Si queremos meter parámetros de búsqueda podemos meterlos en una lista en la llamada a find, y en caso de querer controlar qué campos se ven o no ponemos una lista asignando 1 o 0. Veamos varios ejemplos:

> db.tecnologias.find({lenguaje: "java"});
{ "_id" : ObjectId("51fe630f1b8c7a4eeb00eec4"), "nombre" : "java", "descripcion" : "Lenguaje de programación OO", "creación" : "Sun Jan 12 1992 00:00:00 GMT+0100 (CET)", "lenguaje" : "java" }
> db.tecnologias.find({lenguaje: "javascript", nombre: "mongoDB"});
{ "_id" : ObjectId("51fe630f1b8c7a4eeb00eec3"), "nombre" : "mongoDB", "descripcion" : "Base de datos noSQL", "creación" : "Sat Jan 02 2010 00:00:00 GMT+0100 (CET)", "lenguaje" : "javascript" }
> db.tecnologias.find({lenguaje: "java"},{descripcion: 1});
{ "_id" : ObjectId("51fe630f1b8c7a4eeb00eec4"), "descripcion" : "Lenguaje de programación OO" }
>

Podemos (¡y debemos!) añadir un índice para acelerar las consultas. Basta con indicar el campo y listo. El número de registros que tendrá que analizar mongo será mucho menor.

 > db.tecnologias.ensureIndex({lenguaje:1});
Modificar

Modificar se hace con la función (oh sorpresa) update. Y tiene cuatro parámetros:
db.books.update({criterios_de_busqueda},{$set:{campos_a_modificar}},upsert_flag,multiple_flag);
Los criterios de búsqueda son como los que se pueden poner en las consultas. El segundo parámetro es donde establecemos los nuevos valores para aquellos registros que cumplen los criterios. El upsert es un flag boolean que en caso de ser true lo que hace es modificar si es posible y si no insertar nuevo valor (según ya tengamos valores igual no nos interesa). Y el multiple es para que modifique todos los registros no solo uno. Veamos un ejemplo. Vamos a cambiar el campo lenguaje a aquellos que sean javascript para llamarles ECMAscript.

db.tecnologias.update({lenguaje:"javascript"},{$set:{lenguaje: "ECMAScript"}},false,true);

También podemos alterar todos los registros para añadirles un campo que no tienen! Por ejemplo teníamos un registro inicial que no tenía el campo lenguaje. Ahora podemos alterar eso con esta orden update que incorpora ese campo (vacío) en caso de que no lo tenga.

> db.tecnologias.update({lenguaje:null},{$set:{lenguaje:""}},true,true);
Eliminar

En el caso de querer eliminar usamos la función remove. Hay que tener ojo ya que si ejecutamos db.tecnologias.remove(); a secas ¡nos cargamos todo!. Al igual que en la consulta podemos meter parámetros de búsqueda.

> db.tecnologias.find();
{ "_id" : ObjectId("51fe59da1b8c7a4eeb00eeb0"), "nombre" : "angular", "descripcion" : "Framework javascript", "creación" : "Sun Jan 01 2012 00:00:00 GMT+0100 (CET)" }
{ "_id" : ObjectId("51fe5a771b8c7a4eeb00eeb1"), "nombre" : "backbone", "descripcion" : "Framework javascript", "creación" : "Mon Jan 02 2012 00:00:00 GMT+0100 (CET)", "lenguaje" : "javascript" }
{ "_id" : ObjectId("51fe5d291b8c7a4eeb00eeb2"), "nombre" : "mongoDB", "descripcion" : "Base de datos noSQL", "creación" : "Sat Jan 02 2010 00:00:00 GMT+0100 (CET)", "lenguaje" : "javascript" }
{ "_id" : ObjectId("51fe5d291b8c7a4eeb00eeb3"), "nombre" : "java", "descripcion" : "Lenguaje de programación OO", "creación" : "Sun Jan 12 1992 00:00:00 GMT+0100 (CET)", "lenguaje" : "java" }
> db.tecnologias.remove({lenguaje: "java"});
> db.tecnologias.find();
{ "_id" : ObjectId("51fe59da1b8c7a4eeb00eeb0"), "nombre" : "angular", "descripcion" : "Framework javascript", "creación" : "Sun Jan 01 2012 00:00:00 GMT+0100 (CET)" }
{ "_id" : ObjectId("51fe5a771b8c7a4eeb00eeb1"), "nombre" : "backbone", "descripcion" : "Framework javascript", "creación" : "Mon Jan 02 2012 00:00:00 GMT+0100 (CET)", "lenguaje" : "javascript" }
{ "_id" : ObjectId("51fe5d291b8c7a4eeb00eeb2"), "nombre" : "mongoDB", "descripcion" : "Base de datos noSQL", "creación" : "Sat Jan 02 2010 00:00:00 GMT+0100 (CET)", "lenguaje" : "javascript" }

Volcar un fichero js

Al igual que al iniciar una sesión en mysql podiamos volcar una fichero SQL para ejecutar órdenes en modo batch, con el cliente mongo podemos hacer los mismo, solo que en lugar de pasarle un fichero sql le pasamos un fichero... js. Podemos tener un fichero con el siguiente contenido (con comentarios js):


// ojo que con esto nos cargamos todo
db.tecnologias.remove();

/**
* Y ahora insertamos revistros.
*/
tecnologia={ nombre:'mongoDB',descripcion:'Base de datos noSQL', creación: new Date('01/02/2010'), lenguaje: 'javascript'};
db.tecnologias.insert(tecnologia);
tecnologia={ nombre:'java',descripcion:'Lenguaje de programación OO', creación: new Date('01/12/1992'), lenguaje: 'java'};
db.tecnologias.insert(tecnologia);
tecnologia={nombre:'php',descripcion:'PHP: PHP hypertext preprocesor', creación: new Date('01/12/1990'), lenguaje: 'php'};
db.tecnologias.insert(tecnologia);
tecnologia={ nombre:'c++',descripcion:'El mejor lenguaje de todos', creación: new Date('01/12/1970'), lenguaje: 'c++'};
db.tecnologias.insert(tecnologia);
tecnologia={ nombre:'c#',descripcion:'Lenguaje de programación C#', creación: new Date('01/12/1999'), lenguaje: 'C#'};
db.tecnologias.insert(tecnologia);

Podemos ejecutar todo eso en nuestra BD mongo de forma muy fácil, ojo, sin necesidad de redirección.

linux:~# mongo test scriptmongo.js
MongoDB shell version: 1.2.2
url: test
connecting to: test
linux:~#
Dump/Restore

Como decía Robert de Niro en Ronin "nunca me meto en un sitio del que no sé cómo voy a salir", y en asuntos de sistemas conviene tener la misma forma de pensar ante cualquier contingencia que puede surgir, y surgirá. Siempre nos quedaremos más tranquilos si podemos hacer un volcado de la BD para backup o para su traslado. Por defecto mongo los guarda en un directorio dump en el mismo sitio donde ejecutamos el dump

linux:~# mongodump -d test
connected to: 127.0.0.1
DATABASE: test	 to 	dump/test
	test.test to dump/test/test.bson
		 3 objects
	test.system.indexes to dump/test/system.indexes.bson
		 3 objects
	test.tecnologias to dump/test/tecnologias.bson
		 5 objects
	test.pruebas to dump/test/pruebas.bson
		 0 objects
linux:~#

Ese formato bson son una especie de datos binarios. Si queremos hacer un restore basta con ejecutar:

linux:~# mongorestore -d test dump/test/tecnologias.bson
connected to: 127.0.0.1
dump/test/tecnologias.bson
	 going into namespace [test.tecnologias]
	 5 objects
linux:~#

Lo podemos hacer sobre datos ya existentes y no los duplica NI LOS MODIFICA. Mongo controla.

Export/Import: Formato json

Podemos volcar los datos en formato json y redirigirlos a un fichero en formato JSON

linux:~# mongoexport -d test -c tecnologias > tecnologias.json
connected to: 127.0.0.1
linux:~# more tecnologias.json
{ "_id" : { "$oid" : "51fe630f1b8c7a4eeb00eec3" }, "nombre" : "mongoDB", "descripcion" : "Base de datos noSQ
L", "creación" : { "$date" : 1262386800000 }, "lenguaje" : "javascript" }
{ "_id" : { "$oid" : "51fe630f1b8c7a4eeb00eec4" }, "nombre" : "java", "descripcion" : "Lenguaje de programac
ión OO", "creación" : { "$date" : 695170800000 }, "lenguaje" : "java" }
{ "_id" : { "$oid" : "51fe630f1b8c7a4eeb00eec5" }, "nombre" : "php", "descripcion" : "PHP: PHP hypertext pre
procesor", "creación" : { "$date" : 632098800000 }, "lenguaje" : "php" }
{ "_id" : { "$oid" : "51fe630f1b8c7a4eeb00eec6" }, "nombre" : "c++", "descripcion" : "El mejor lenguaje de t
odos", "creación" : { "$date" : 946800000 }, "lenguaje" : "c++" }
{ "_id" : { "$oid" : "51fe630f1b8c7a4eeb00eec7" }, "nombre" : "c#", "descripcion" : "Lenguaje de programació
n C#", "creación" : { "$date" : 916095600000 }, "lenguaje" : "C#" }
exported 5 records
linux:~#

La operación contraria no tiene misterios. Por cierto, igual que con el restore si hacemos el import 2 veces no duplica los datos ni nada.

linux:~# mongoimport -d test -c tecnologias  tecnologias.json
connected to: 127.0.0.1
imported 5 objects
linux:~#
Esto no es más que la punta del iceberg. Échale un ojo a MongoDB porque ya bastantes décadas llevamos con SQL.