Вы находитесь на странице: 1из 123

MongoDB desde Cero: Introduccin e

Instalacin
Las bases de datos relacionales estn pasando de moda, los desarrolladores
optan cada vez ms por opciones novedosas de NoSQL debido a sus altos niveles
de rendimiento y fcil escalabilidad. Hace unas semanas hablamos de las
bondades deRedis; sin embargo algunos andan temerosos por tener poco tiempo y
prefieren una solucin con un poco ms de reputacin, es por esto que esta
semana hablaremos de las base de datos NoSQL ms utilizada, MongoDB.

Qu es MongoDB?
Es una base de datos NoSQL de cdigo abierto, este tipo de soluciones se basan
en el principio de almacenar los datos en una estructura tipo llave-valor; MongoDB
por su lado se enfoca especficamente en que los valores de estas llaves
(llamadas colecciones) son estructuras tipo JSON (llamados documentos), es
decir objetos Javascript, lenguaje sobre el cual se basa esta solucin de base de
datos. Esto facilitar su manipulacin a muchos que ya conozcan el lenguaje.
MongoDB posee varias estrategias de manejo de datos que la han posicionado
donde se encuentra hoy en da, tales como sus procesos de divisin de datos en
distintos equipos fsicos o tambin conocido como clusterizacin, tambin el caso
similar de documentos muy grandes que superen el limite estipulado de 16MB se
aplica una estrategia llamada GridFS que automticamente divide el documento en
pedazos y los almacena por separado, al recuperar el documento el driver se
encarga de armar automticamente el documento nuevamente.
La estructura de almacenamiento es tan flexible que uno de los hechos
importantes que se comparten al introducir esta base de datos es que:
Distintos documentos en la misma coleccin no deben tener
obligatoriamente los mismos campos o estructura. Inclusive documentos
con campos en comn no tienen necesariamente que tener el mismo tipo
de dato.

Cmo lo instalo?
Bien, ya estamos ansiosos y desesperados, procedamos con la instalacin.
Mac OS X
Si haz ledo varios de nuestros Cmo Lo Hago, sabrs que el mtodo por
excelencia para instalar en Mac OS X esHombrew

1 $ brew install mongodb
Para gestionar el servicio de MongoDB basta con ejecutar:
1 $ brew services [ start | stop | restart ] mongodb
Windows
Dirigete a la pgina de oficial de MongoDB y descarga el comprimido segn la
arquitectura de tu sistema operativo.
A partir de la versin 2.2, MongoDB no es compatible con Windows XP.
Si tienes Windows Server 2008 o 7, debes instalar esta
actualizacin para evitar un problema conocido con archivos mapeados
en memoria.
Luego lo descomprimiremos y, segn las recomendaciones, creamos un
directorio mongodb en el directorio raz C: donde colocaremos el contenido del
comprimido, luego crearemos en C: un directorio data y dentro de este un
directorio db, aqu ser donde MongoDB almacenar la informacin de las bases
de datos.
Para hacer que MongoDB funcione como un servicio primero crea el
directorio log dentro de C:\mongodb\ y luego ejecutaremos el siguiente comando
para crear el archivo de configuracin asociado:
1 $ echo logpath=C:\mongodb\log\mongo.log > C:\mongodb\mongod.cfg
Luego instalemos el servicio:
1 $ C:\mongodb\bin\mongod.exe --config C:\mongodb\mongod.cfg --install
Ahora para gestionar el servicio de MongoDB basta con ejecutar:
1 $ net [ start | stop ] MongoDB

Cmo lo configuro?
Buscaremos y editaremos el archivo de configuracin.
Mac OS X
1 /usr/local/etc/mongod.conf
Sistemas Debian, Fedora, CentOS y RedHat
1 /etc/mongodb.conf
Windows
1 C:\mongodb\mongod.cfg
Hablemos de algunas de las variables de mayor uso en este archivo.
port especificacin del puerto donde escucha la base de datos.
bind_ip permite delimitar especificamente qu IPs pueden interactuar con la
base de datos.
maxConns cantidad mxima de conexiones que sern aceptados, el valor por
defecto depende de la cantidad de descriptores de archivos que maneja el
sistema operativo.
objcheck habilitado por defecto, obliga a mongod a verificar cada peticin
para asegurar que la estructura de los documentos que insertan los clientes
sea siempre vlida. Cabe destacar que para documentos complejos esta
opcin puede afectar un poco el rendimiento.
fork inhabilitado por defecto, permite ejecutar mongod como un daemon.
auth inhabilitado por defecto, permite limitar el acceso remoto a la base de
datos al implementar un mecanismo de autenticacin.
dbpath especifica el directorio donde la instancia de base de datos almacena
toda su informacin.
directoryperdb inhabilitado por defecto, ofrece la opcin de que la
informacin de cada base de datos presente en la instancia se almacene en
carpetas separadas.
journal al habilitarse permite que las operaciones realizadas sobre la data
sean almacenadas en una bitcora para en caso de ocurrir una falla, el
sistema sea capaz de reconstruir la informacin que haya podido perderse.
smallfiles inhabilitado por defecto, ofrece la opcin de que los archivos
creados sean ms pequeos y, por ende, ms fciles de entender, procesar
y monitorear en varias ocasiones.
syncdelay especifica el lapso en segundos que tardar la instancia en pasar
la informacin en la bitcora a persistencia.
Tambin se ofrecen varias opciones para el uso de SSL, configuracin de
replicacin y clusterizacin lo cual tocaremos a medida que avance el curso.

Entrar a la consola
Bien ahora ya estamos listos para entrar a la base de datos y comenzar a jugar
con ella. Para ello luego de tener el servicio de MongoDB corriendo, ejecutaremos
el comando mongo y esto nos llevar a la consola interna de la instancia.
Para ir a la consola de MongoDB en sistemas Windows, si seguiste las
instrucciones en el curso anterior, luego de iniciar el servicio, ejecuta el
archivoC:\mongodb\bin\mongo.exe

Conclusin
Esta semana vimos solo un abreboca de lo que es capaz MongoDB, lo instalamos
y vimos algunos detalles sobre su configuracin, que la desesperacin no te
impida llegar a la semana que viene para aprender a utilizarlo. Hasta entonces.


MongoDB desde Cero: Operaciones
Bsicas
Luego que hemos instalado MongoDB en la entrada pasada, seguramente querrs
comenzar a realizar inserciones y queriespara probar las ventajas de esta solucin
y poner tus habilidades en prctica, comencemos con algunas operaciones
bsicas para saber como manipular los datos en MongoDB.

Creacin de registros - .insert()
Las operaciones son como funciones de Javascript, as que llamaremos al objeto
base de datos db y crearemos una nueva propiedad o lo que se asemejara al
concepto de tabla con el nombre de autores y le asociaremos su valor
correspondiente (un objeto autor), es decir, una coleccin con
un documento asociado:
1
2
3
4
5
> db.autores.insert({
nombre : 'Jonathan',
apellido : 'Wiesel',
secciones : ['Como lo hago' , 'MongoDB']
});

Los documentos se definen como los objetos Javascript, u objetos JSON.
Inclusive es posible declarar el documento como un objeto, almacenarlo en una
variable y posteriormente insertarlo de la siguiente manera:
1
2
3
4
5
6
7
> var autorDelPost = {
nombre : 'Jonathan',
apellido : 'Wiesel',
secciones : ['Como lo hago' , 'MongoDB']
};

> db.autores.insert(autorDelPost);
Ahora si ejecutamos el comando show collections podremos ver que se
encuentra nuestra nueva coleccin de autores:
1
2
autores
...
Agreguemos un par de autores ms:

1
2
3
4
5
6
7
8
9
10
11
12
> db.autores.insert({
nombre : 'Oscar',
apellido : 'Gonzalez',
secciones : ['iOS' , 'Objective C' , 'NodeJS' ],
socialAdmin : true
});
> db.autores.insert({
nombre : 'Alberto',
apellido : 'Grespan',
secciones : 'Git',
genero : "M"
});
Veamos que insertamos nuevos documentos en la coleccin de autores que tienen
otra estructura, en MongoDB esto es completamente posible y es una de sus
ventajas.

Bsqueda de registros - .find()
Hagamos un query o una busqueda de todos registros en la coleccin de autores.
1
2
3
4
5
> db.autores.find();

{ "_id" : ObjectId("5232344a2ad290346881464a"), "nombre" : "Jonathan", "apellido" : "Wiesel", "secciones" :
[ "Como lo hago", "Noticias" ] }
{ "_id" : ObjectId("523236022ad290346881464b"), "nombre" : "Oscar", "apellido" : "Gonzalez", "secciones" :
[ "iOS", "Objective C", "NodeJS" ], "socialAdmin" : true }
{ "_id" : ObjectId("5232383a2ad290346881464c"), "nombre" : "Alberto", "apellido" : "Grespan", "secciones" :
"Git", "genero" : "M" }
Notemos que la bsqueda nos arroja los objetos resultantes, en este caso los
documentos de los 3 autores que insertamos acompaados del identificador nico
que crea MongoDB, este campo _id se toma adems como indice por defecto.
Si lo deseas puedes manualmente especificar el valor del
campo _id cuando estas insertando los registros con el
comando db.coleccion.insert(); sin embargo ten en cuenta que debes
asegurar que este valor sea nico, de lo contrario los registros con dicho
campo duplicado resultarn en error por clave primaria duplicada.
Una bsqueda como la anterior sera similar en SQL a:
SELECT * FROM autores
Filtros
Digamos que ahora queremos hacer la bsqueda pero filtrada por los algn
parmetro. Para esto slo debemos pasar el filtro deseado a la funcin find(),
busquemos a los administradores sociales para probar:
1
2
3
> db.autores.find({ socialAdmin: true });

{ "_id" : ObjectId("523236022ad290346881464b"), "nombre" : "Oscar", "apellido" : "Gonzalez", "secciones" :
[ "iOS", "Objective C", "NodeJS" ], "socialAdmin" : true }
En SQL sera similar a:
SELECT * FROM autores WHERE socialAdmin = true
Probemos ahora filtrar por varias condiciones, primero probemos con filtros donde
TODOS se deben cumplir:
1
2
3
> db.autores.find({ genero: 'M', secciones: 'Git' });

{ "_id" : ObjectId("5232383a2ad290346881464c"), "nombre" : "Alberto", "apellido" : "Grespan", "secciones" :
"Git", "genero" : "M" }
Es importante destacar que si el documento resultante hubiese tenido en
la propiedadsecciones un arreglo en lugar de una cadena de caracteres,
si dicho arreglo tuviese el valorGit tambin cumple la condicin.
En SQL sera similar a:
SELECT * FROM autores WHERE genero = 'M' AND secciones = 'Git'
Veamos ahora un ejemplo un poco ms avanzado de filtros con condiciones donde
queremos que solo ALGUNA de ellas se cumpla:
1
2
3
4
5
6
7
8
9
> db.autores.find({
$or: [
{socialAdmin : true},
{genero: 'M'}
]
});

{ "_id" : ObjectId("523236022ad290346881464b"), "nombre" : "Oscar", "apellido" : "Gonzalez", "secciones" :
[ "iOS", "Objective C", "NodeJS" ], "socialAdmin" : true }
{ "_id" : ObjectId("5232383a2ad290346881464c"), "nombre" : "Alberto", "apellido" : "Grespan", "secciones" :
"Git", "genero" : "M" }
En este caso estamos filtrando por aquellos autores que son administradores
sociales aquellos que tengan el campo gnero con el carcter M.
En SQL sera similar a:
SELECT * FROM autores WHERE socialAdmin = true OR genero = 'M'
Limitar y Ordenar
Si quisiramos limitar los resultados a un nmero mximo especificado de registros
es tan fcil como agregar .limit(#) al final del comando .find():
1
2
3
> db.autores.find().limit(1)

{ "_id" : ObjectId("5232344a2ad290346881464a"), "nombre" : "Jonathan", "apellido" : "Wiesel", "secciones" :
[ "Como lo hago", "MongoDB" ] }
En SQL sera similar a:
SELECT * FROM autores LIMIT 1
La misma modalidad sigue la funcionalidad de ordenar los registros por un campo
en particular, el cual servir de argumento a la funcin .sort():
1
2
> db.autores.find().sort({apellido : 1})

{ "_id" : ObjectId("523236022ad290346881464b"), "nombre" : "Oscar", "apellido" : "Gonzalez", "secciones" :
3
4
5
[ "iOS", "Objective C", "NodeJS" ], "socialAdmin" : true }
{ "_id" : ObjectId("5232383a2ad290346881464c"), "nombre" : "Alberto", "apellido" : "Grespan", "secciones" :
"Git", "genero" : "M" }
{ "_id" : ObjectId("5232344a2ad290346881464a"), "nombre" : "Jonathan", "apellido" : "Wiesel", "secciones" :
[ "Como lo hago", "MongoDB" ] }
El nmero 1 que acompaa al argumento de ordenamiento es el tipo de
orden, 1 para descendiente y -1 para ascendente
En SQL sera similar a:
SELECT * FROM autores ORDER BY apellido DESC
Tambin podemos combinar ambas funciones tan solo llamando una despus de
otra:
1
2
3
> db.autores.find().sort({apellido : 1}).limit(1)

{ "_id" : ObjectId("523236022ad290346881464b"), "nombre" : "Oscar", "apellido" : "Gonzalez", "secciones" :
[ "iOS", "Objective C", "NodeJS" ], "socialAdmin" : true }
Otros filtros
Existen varios operadores ms para filtrar las bsquedas; eso lo dejaremos para
ms adelante para no sobrecargarte de informacin tan rpido, pero no te
preocupes que no lo pasaremos por alto.

Eliminacin de registros - .remove() y .drop()
Si entendiste como buscar registros pues eliminarlos es igual de fcil. Para esto
existen 4 posibilidades:
Eliminar los documentos de una coleccin que cumplan alguna condicin.
Eliminar todos los documentos de una coleccin.
Eliminar la coleccin completa.
Probemos eliminandome a m de la coleccin de autores:
1 > db.autores.remove({ nombre: 'Jonathan' });
En SQL sera similar a:
DELETE FROM autores WHERE nombre = 'Jonathan'
Fcil no?. Eliminemos ahora a los dems autores:
1 > db.autores.remove();
En SQL sera similar a:
DELETE FROM autores
Ahora que la coleccin ha quedado vaca deshagamonos de ella:
1 > db.autores.drop();
En SQL sera similar a:
DROP TABLE autores

Conclusin
Seguramente te estars preguntando: Y qu pas con los update?. La
modificacin de registros involucra mltiples maneras para que puedas manipular
la informacin a tu gusto, por ello lo dejaremos para la prxima entrada, no
desesperes. Estos son los primeros pasos a tomar para que comiences a usar
MongoDB, todava queda un largo camino por delante ya que hay mucho que
aprender sobre esta magnifica solucin de base de datos.


MongoDB desde Cero: Actualizaciones
/ Updates

Como mencionamos en la entrada pasada, la parte de actualizaciones o updates la
dejamos por separado para tratar de ser un poco ms detallados y extendernos en
esta rea para que puedas dominar con mayor destreza la manipulacin de los
datos.

Estructura
Para modificar los documentos que ya se encuentran almacenados usaremos el
comando .update() el cual tiene una estructura como esta:
1
2
3
4
5
6
7
8
db.coleccion.update(
filtro,
cambio,
{
upsert: booleano,
multi: booleano
}
);


Aclaremos un poco lo que nos indica la estructura.
filtro debemos especificar como encontrar el registro que desemos modificar,
sera el mismo tipo de filtro que usamos en las bsquedas o finders.
cambio aqu especificamos los cambios que se deben hacer. Sin embargo ten en
cuenta que hay 2 tipos de cambios que se pueden hacer:
Cambiar el documento completo por otro que especifiquemos.
Modificar nada ms los campos especificados.
upsert (opcional, false por defecto) este parametro nos permite especificar en
su estado true que si el filtro no encuentra ningun resutlado entonces el cambio
debe ser insertado como un nuevo registro.
multi (opcional, false por defecto) en caso de que el filtro devuelva ms de un
resultado, si especificamos este parametro como true, el cambio se realizar a
todos los resultados, de lo contrario solo se le har al primero (al de menor Id).

Actualizacin sobrescrita (overwrite)
Bien, probemos insertando nuevo autor, el cual modificaremos luego:
1
2
3
4
> db.autores.insert({
nombre : 'Ricardo',
apellido : 'S'
});
Ahora probemos el primer caso, cambiar todo el documento, esto significa que en
lugar de cambiar solo los campos que especifiquemos, el documento ser
sobreescrito con lo que indiquemos:
1
2
3
4
5
6
7
> db.autores.update(
{nombre: 'Ricardo'},
{
nombre: 'Ricardo',
apellido: 'Sampayo',
secciones: ['Ruby','Rails'],
esAmigo: false
8
9
}
);
Notemos que como primer parmetro indicamos el filtro, en este caso que el
nombre sea Ricardo, luego indicamos el cambio que hariamos, como estamos
probando el primer caso indicamos el documento completo que queremos que
sustituya al actual. Si
hacemos db.autores.find({nombre:'Ricardo'}); podremos ver que en efecto el
documento qued como especificamos:
1
{ "_id" : ObjectId("523c91f2299e6a9984280762"), "nombre" : "Ricardo", "apellido" : "Sampayo", "secciones" :
[ "Ruby", "Rails" ], "esAmigo" : false }
Sobreescbirir el documento no cambiar su identificador nico _id.

Operadores de Modificacin
Ahora probemos cambiar los campos que deseamos, para este caso haremos uso
de lo que se denominan como operadores de modificacin.
Hablemos un poco sobre algunos de estos operadores antes de verlos en accin:
$inc incrementa en una cantidad numerica especificada el valor del campo
a en cuestin.
$rename renombrar campos del documento.
$set permite especificar los campos que van a ser modificados.
$unset eliminar campos del documento.
Referentes a arreglos:
$pop elimina el primer o ltimo valor de un arreglo.
$pull elimina los valores de un arreglo que cumplan con el filtro indicado.
$pullAll elimina los valores especificados de un arreglo.
$push agrega un elemento a un arreglo.
$addToSet agrega elementos a un arreglo solo s estos no existen ya.
$each para ser usado en conjunto con $addToSet o $push para indicar
varios elementos a ser agregados al arreglo.
Hagamos una prueba sobre nuestro nuevo documento:
1
2
> db.autores.update(
{ nombre: 'Ricardo' },
3
4
5
6
{
$set: { esAmigo: true , age : 25 }
}
);
En este caso estamos usando el operador $set para 2 propositos a la vez:
Actualizar el valor de un campo (cambiamos esAmigo de false a true).
Creamos un campo nuevo (age) asignandole el valor 25.
Supongamos que Ricardo cumpli aos en estos das, as que para incrementar el
valor de su edad lo podemos hacer as:
1
2
3
4
5
6
> db.autores.update(
{ nombre: 'Ricardo' },
{
$inc: { age : 1 }
}
);
Tambin podemos indicar nmeros negativos para decrementar.
Aqu hablamos espaol, as que cambiemos ese campo age por lo que le
corresponde:
1
2
3
4
5
6
> db.autores.update(
{ nombre: 'Ricardo' },
{
$rename: { 'age' : 'edad' }
}
);
Los que trabajamos en Codehero somos todos amigos as que no es necesario
guardar el campo esAmigo:
1
2
3
4
5
> db.autores.update(
{ nombre: 'Ricardo' },
{
$unset: { esAmigo : '' }
}
6 );
El valor que le asignes al campo a eliminar no tendr ningun efecto,
pero es necesario escribirlo por motivos de sintaxis.
Pasemos ahora a la parte de modificacin de arreglos, agreguemosle algunas
secciones extra a nuestro autor:
1
2
3
4
5
6
> db.autores.update(
{ nombre: 'Ricardo' },
{
$push: { secciones : 'jQuery' }
}
);
Si se quiere asegurar que el elemento no est duplicado se
usara $addToSet
Esto agregar al final del arreglo de secciones el elemento jQuery.
Agreguemos algunas secciones ms en un solo paso:
1
2
3
4
5
6
> db.autores.update(
{ nombre: 'Ricardo' },
{
$push: { secciones : { $each : ['Haskell','Go','ActionScript'] } }
}
);
Bueno en realidad Ricardo no maneja desde hace un tiempo ActionScript as que
eliminemos ese ultimo elemento del arreglo:
1
2
3
4
5
6
> db.autores.update(
{ nombre: 'Ricardo' },
{
$pop: { secciones : 1 }
}
);
Para eliminar el ltimo elemento se coloca 1, para el primero -1.
Ricardo hace tiempo que no nos habla sobre jQuery asi que hasta que no se
reivindique quitemoslo de sus secciones:
1
2
3
4
5
6
> db.autores.update(
{ nombre: 'Ricardo' },
{
$pull: { secciones : 'jQuery' }
}
);
Pensandolo bien, Ricardo nunca nos ha hablado de Haskell ni Go tampoco,
eliminemoslos tambin:
1
2
3
4
5
6
> db.autores.update(
{ nombre: 'Ricardo' },
{
$pullAll: { secciones : ['Haskell','Go'] }
}
);

Comando .save()
Otra manera para actualizar o insertar registros es mediante el uso del
comando .save(). Este comando recibe como parmetro nicamente un
documento.
Insertar un registro es tal cual como si hicieramos un .insert():

1
2
3
> db.autores.save({
nombre: 'Ramses'
});
En cuanto al caso de actualizacin de registros te estars preguntando:
Si solo recibe un documento, como sabe Mongo que documento debe
actualizar?
En estos casos puedes hacer el equivalente a una actualizacin sobrescrita con
tan solo indicar el _id del registro a actualizar como parte del nuevo documento.
1
2
3
4
5
6
7
8
9
10
> db.autores.find({nombre: 'Ramses'});

{ "_id" : ObjectId("5246049e7bc1a417cc91ec8c"), "nombre" : "Ramses" }

> db.autores.save({
_id: ObjectId('5246049e7bc1a417cc91ec8c')
nombre: 'Ramses',
apellido: 'Velasquez',
secciones: ['Laravel', 'PHP']
});
En esta caso particular, debido a que Mongo le asigno automticamente
ese ID autogenerado poco amigable, ese mismo es el que pusimos en
nuestro documento para que Mongo sepa que debe actualizar ese
registro, algunos recomiendan usar _id que no sean establecidos por
Mongo sino por la librera del cliente para evitar trabajar con este tipo de
identificadores poco amigables.

Conclusin
Como has podido notar, MongoDB es muy poderoso y ofrece muchas ventajas en
cuento a la modificacin de sus registros. Claro, las bsquedas tambin se pueden
tornar bastante interesantes segn nuestras necesidades y eso lo veremos ms
adelante, a medida que vamos avanzando iremos tocando la mayora de los temas
que puedan servir de ayuda para que logres dominar al mximo esta solucin
NoSQL.


MongoDB desde Cero: Modelado de
Datos

MongoDB desde Cero: Modelado
de Datos

Una de las dificultades que encuentran aquellos que se adentran al mundo del
NoSQL es al tratar de transformar su esquema de base de datos relacional para
que funcione de la mejor manera segun el enfoque NoSQL orientado a
documentos, como lo es MongoDB. Aqu aprenderemos sobre como realizar
correctamente el modelado de datos para que puedas comenzar a pensar en
migrar tus proyectos a este tipo de base de datos con la menor dificultad posible.

Tipos de Datos
Al comienzo de la serie explicamos que los documentos de MongoDB son como
objetos JSON, para ser especificos son de tipo BSON (JSON Binario), esta
estrategia permite la serializacin de documentos tipo JSON codificados
binariamente. Veamos algunos de los tipos de datos que soporta:
String Cadenas de caracteres.
Integer Nmeros enteros.
Double Nmeros con decimales.
Boolean Booleanos verdaderos o falsos.
Date Fechas.
Timestamp Estampillas de tiempo.
Null Valor nulo.
Array Arreglos de otros tipos de dato.
Object Otros documentos embebidos.
ObjectID Identificadores nicos creados por MongoDB al crear
documentos sin especificar valores para el campo_id.
Data Binaria Punteros a archivos binarios.
Javascript cdigo y funciones Javascript.

Patrones de Modelado
Existen 2 patrones principales que nos ayudarn a establecer la estructura que
tendrn los documentos para lograr relacionar datos que en una base de datos
relacional estaran en diferentes tablas.
Embeber
Este patrn se enfoca en incrustar documentos uno dentro de otro con la finalidad
de hacerlo parte del mismo registro y que la relacin sea directa.
Si tienes experiencia en bases de datos orientadas o objetos, este patrn
seguira el mismo principio para la implementacin de un TDA (Tipo de
Dato Abstracto).
Referenciar
Este patrn busca imitar el comportamiento de las claves forneas para relacionar
datos que deben estar en colecciones diferentes.
Debes tomar en cuenta cuando ests en el proceso de modelado que si
piensas hacer muchas actualizaciones sobre datos de colecciones
relacionadas (en especial modificaciones atmicas), trata en lo posible de
que aquellos datos que se vayan a modificar se encuentren en el mismo
documento.

Modelado de Relaciones
Bien, ha llegado el momento de aprender a transformar las relaciones de las tablas
en las bases de datos relacionales. Empecemos con lo ms bsico.
Relaciones 1-1.
Muchas opiniones concuerdan que las relaciones 1 a 1 deben ser finalmente
normalizadas para formar una nica tabla; sin embargo existen consideraciones
especiales donde es mejor separar los datos en tablas diferentes. Supongamos el
caso que tenemos una tabla persona y otra tabla documentos personales,
donde una persona tiene un solo juego de documentos personales y que un juego
de documentos personales solo puede pertenecer a una persona.

Si traducimos esto tal cual a lo que sabemos hasta ahora de MongoDB sera algo
as:
1
2
3
4
5
6
7
8
9
10
11
Persona = {
nombre : 'Jonathan',
apellido : 'Wiesel',
genero : 'M'
}

DocumentosPersonales = {
pasaporte : 'D123456V7',
licencia : '34567651-2342',
seguro_social : 'V-543523452'
}

Para los casos de relaciones 1-a-1 se utiliza el patrn de embeber un documento
en otro, por lo que el documento final quedara as:
1
2
3
4
5
6
7
8
9
10
Persona = {
nombre : 'Jonathan',
apellido : 'Wiesel',
genero : 'M',
documentos : {
pasaporte : 'D123456V7',
licencia : '34567651-2342',
seguro_social : 'V-543523452'
}
}
Relaciones 1-*
Supongamos ahora el caso de una tabla persona y otra tabla direccin. Donde
una persona puede poseer varias direcciones.

Por el momento no nos pondremos creativos al pensar que una direccin
puede pertenecer a ms de una persona.
Traduciendolo tal cual a MongoDB tendramos algo as:
1
2
3
4
5
Persona = {
nombre : 'Jonathan',
apellido : 'Wiesel',
genero : 'M'
}
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

Direccion1 = {
pais : 'Venezuela',
estado : 'Distrito Capital'
ciudad : 'Caracas'
urbanizacion : 'La Florida',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...
}

Direccion2 = {
pais : 'Estados Unidos',
estado : 'Florida'
ciudad : 'Miami'
urbanizacion : 'Aventura',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...
}
Ahora para transformar la relacin tenemos 2 opciones.
Podemos embeber las direcciones en el documento de la persona al establecer un
arreglo de direcciones embebidas:
1
2
3
4
5
6
Persona = {
nombre : 'Jonathan',
apellido : 'Wiesel',
genero : 'M',
direcciones : [{
pais : 'Venezuela',
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
estado : 'Distrito capital'
ciudad : 'Caracas',
urbanizacion : 'La Florida',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...
},{
pais : 'Estados Unidos',
estado : 'Florida'
ciudad : 'Miami'
urbanizacion : 'Aventura',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...
}]
}
podemos dejarlo en documentos separados. Para esta segunda opcin tenemos
2 enfoques.
Uno sera agregar un campo de referencia a direccin en persona:
1
2
3
4
5
6
7
8
9
10
Direccion1 = {
_id : 1,
pais : 'Venezuela',
estado : 'Distrito Capital',
ciudad : 'Caracas'
urbanizacion : 'La Florida',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
}

Direccion2 = {
_id : 2,
pais : 'Estados Unidos',
estado : 'Florida',
ciudad : 'Miami'
urbanizacion : 'Aventura',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...
}

Persona = {
nombre : 'Jonathan',
apellido : 'Wiesel',
genero : 'M',
direcciones : [1,2]
}
y el otro sera agregar un campo de referencia a persona en direccin:
1
2
3
4
5
6
7
8
9
10
Direccion1 = {
_id : 1,
pais : 'Venezuela',
estado : 'Distrito Capital',
ciudad : 'Caracas'
urbanizacion : 'La Florida',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...,
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
persona_id : 1
}

Direccion2 = {
_id : 2,
pais : 'Estados Unidos',
estado : 'Florida',
ciudad : 'Miami'
urbanizacion : 'Aventura',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...,
persona_id : 1
}

Persona = {
_id : 1
nombre : 'Jonathan',
apellido : 'Wiesel',
genero : 'M'
}
En lo posible trata de utilizar la opcin de embeber si los arreglos no
variarn mucho ya que al realizar la bsqueda de la persona obtienes de
una vez las direcciones, mientras que al trabajar con referencias tu
aplicacin debe manejar una lgica mltiples bsquedas para resolver
las referencias, lo que sera el equivalente a los joins.
En caso de utilizar la segunda opcin, Cual de los 2 ltimos enfoques
utilizar?. En este caso debemos tomar en cuenta que tanto puede crecer
la lista de direcciones, en caso que la tendencia sea a crecer mucho,
para evitar arreglos mutantes y en constante crecimiento
elsegundo enfoque sera el ms apropiado.
Relaciones *-*
Finalmente nos ponemos creativos a decir que, en efecto, varias personas pueden
pertenecer a la misma direccin.
Al aplicar normalizacin quedara algo as:

Para modelar este caso es muy similar al de relaciones uno a muchos con
referencia por lo que colocaremos en ambos tipos de documento un arreglo de
referencias al otro tipo. Agreguemos una persona adicional para demostrar mejor
el punto:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Direccion1 = {
_id : 1,
pais : 'Venezuela',
estado : 'Distrito Capital',
ciudad : 'Caracas'
urbanizacion : 'La Florida',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...,
personas : [1000]
}

Direccion2 = {
_id : 2,
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
pais : 'Estados Unidos',
estado : 'Florida',
ciudad : 'Miami'
urbanizacion : 'Aventura',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...,
personas : [1000,1001]
}

Persona1 = {
_id : 1000,
nombre : 'Jonathan',
apellido : 'Wiesel',
genero : 'M',
direcciones : [1,2]
}

Persona2 = {
_id : 1001,
nombre : 'Carlos',
apellido : 'Cerqueira',
genero : 'M',
direcciones : [2]
}
Seguro debes estar esperando el caso ms complejo de todos, aquellas ocasiones
donde la tabla intermedia tiene campos adicionales.

Tomando como base el ejemplo anterior, agregaremos el campo adicional usando
el patrn para embeber de la siguiente manera:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Direccion1 = {
_id : 1,
pais : 'Venezuela',
estado : 'Distrito Capital',
ciudad : 'Caracas'
urbanizacion : 'La Florida',
avenida : ...,
edificio : ...,
piso : ...,
apartamento : ...,
personas : [1000]
}

Direccion2 = {
_id : 2,
pais : 'Estados Unidos',
estado : 'Florida',
ciudad : 'Miami'
urbanizacion : 'Aventura',
avenida : ...,
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
edificio : ...,
piso : ...,
apartamento : ...,
personas : [1000,1001]
}

Persona1 = {
_id : 1000,
nombre : 'Jonathan',
apellido : 'Wiesel',
genero : 'M',
direcciones : [{
direccion_id : 1,
viveAqui : true
},{
direccion_id : 2,
viveAqui : false
}]
}

Persona2 = {
_id : 1001,
nombre : 'Carlos',
apellido : 'Cerqueira',
genero : 'M',
direcciones : [{
direccion_id : 2,
viveAqui : true
}]
}
De igual manera se pudiera embeber el campo viveAqui del lado de las
direcciones, esto depender de como planeas manipular los datos.

Conclusin
En este curso hemos aprendido a modelar los datos que conforman una base de
datos de MongoDB, con este nuevo conocimiento ya podemos evaluar la
estructura de una base de datos relacional para determinar si en efecto podemos
transformarla fcilmente a NoSQL orientado a documentos y lograr aprovechar sus
numerosas ventajas y flexibilidad.

MongoDB desde Cero: ndices. Parte I

Cuando estamos construyendo nuestro esquema de bases de datos es comn
olvidarnos de implementar ndices y solemos continuar con la siguiente tarea; sin
embargo son estos los que ayudan significativamente en el rendimiento de la base
de datos, especialmente cuando el volumen de datos va incrementando. En esta
entrada hablaremos sobre ellos y cmo implementarlos en MongoDB.

Qu es un ndice?
Un ndice en bases de datos es una estructura de datos que toma los valores de
campos particulares de una tabla (los ID por defecto) y se almacena en un espacio
de rpido acceso, esto con la intencin de que al hacer un query cuyo campo de
filtrado este indexado, el registro es localizado a partir del indice y la velocidad de
respuesta del proceso sea mucho ms rpido, tal cmo el ndice de un libro.
En el caso particular de MongoDB, al no buscar por un campo indexado el proceso
de mongod debe recorrer todo el documento de los registros de una coleccin para
hacer realizar la bsqueda, este proceso es ineficiente y requiere un alto numero
de recursos para manipular todo el volumen de informacin.
Una mala prctica que utilizan algunos es indexar la mayor cantidad de campos
que puedan lo cual termina casi duplicando en volumen la base de datos y el
proceso que busca en los ndices se torna igual de lento que buscar normalmente
ya que los ndices son almacenados en memoria y al agotarse el espacio
reservado, el almacenamiento de ndices empieza a escribirse en disco, el cual es
de acceso mucho mas lento. Es por esto que debemos resaltar y aconsejar
que slo debes crear ndices sobre campos que vayan a ser consultados con
alta frecuencia.

Tipos de ndice
Hablemos un poco sobre los diferentes tipos de ndice que podemos implementar
en MongoDB:
ndice _id el identificador principal de un registro es el ndice por defecto,
si no especificas uno al crear un documento el proceso de *mongod* le
asignar uno automaticamente de tipo ObjectID.
ndice sencillo definidos sobre un nico campo de un documento.
ndice compuesto definidos sobre varios campos de un documento, ser
tomado como un ndice nico por parte de MongoDB.
ndice mutillave utilizado en casos de que el campo a indexar pertenezca
a subdocumentos dentro de un arreglo del documento padre.
ndice geoespacial utilizado para indexar campos que sean coordenadas
de tipo GeoJSON.
ndice texto al momento de escritura se encuentra fase beta y se utiliza
para buscar contenidos de cadenas de caracteres en los documentos.
ndice tipo hash utilizado en la estrategia de llaves de
fragmento hasheadas que se lleva a cabo en los procesos de fragmentacin
de datos.
En este curso no tocaremos los ltimas 3.

Propiedades
Existen un par de propiedades interesantes de los ndices que puedes aprovechar:
Unicidad esta propiedad permite establecer el ndice como campo nico,
es decir, al declarar un ndice como uniquesobre un campo particular evitar
que existan documentos diferentes con el mismo valor para dicho campo ya
que al tratar de insertar un documento bajo estas circunstancias resultar en
un error de llave duplicada. Esto resulta muy til para campos que sirven de
identificadores externos que suelen necesitar indexacin.
Si tratas de insertar un documento que no tenga valor en este campo,
automticamente se le asignar el valor null al valor del indice para este
registro. Si tratas nuevamente de hacer lo mismo ocurrir un error de
llave duplicada ya que el ndice de valor null para dicho campo ya existe.
Dispersin esta propiedad permite establecer el filtrado de los resultados
basado en el campo indexado. Lo entenders mejor cuando lo apliquemos
ms adelante.

Preparacin
Bien, ahora que sabemos la teora es hora de poner manos a la obra. Tomemos
una situacin de ejemplo para manejar el caso.
La semana pasada jugamos unas 15 rondas de poker, creamos una
coleccin puntuaciones y anotamos las siguientes puntuaciones finales:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
nombre: 'Ricardo',
ganadas: 3,
perdidas: 3,
retiradas: 9,
dinero: 15,
mejores: ['1 tro', '2 pares'],
comentarios:[
{
hora: '22:10:10',
texto: 'Hoy ser mi da de suerte'
},{
hora: '23:50:32',
texto: 'Quizs otro da'
}
]
}

{
nombre: 'Ramses',
ganadas: 8,
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
perdidas: 4,
retiradas: 3,
dinero: 120,
mejores: ['Escalera real', 'Full house'],
comentarios:[
{
hora: '22:12:56',
texto: 'Los humillar a todos'
},{
hora: '23:55:59',
texto: 'Gracias por darme su dinero'
}
]
}

{
nombre: 'Oscar',
ganadas: 4,
perdidas: 6,
retiradas: 5,
dinero: 30,
mejores: ['Full house', '1 tro'],
comentarios:[
{
hora: '22:09:12',
texto: 'En realidad vamos a jugar nada mas para mostrar un ejemplo de Mongo?'
},{
hora: '23:59:59',
texto: 'ZZZZZZzzzzzzz'
}
]
53 }
Ya sabes como se insertan estos documentos as que no hay excusa =).
Obtener ndices
Como mencionamos anteriormente el campo _id es el ndice por defecto que crea
MongoDB. Podemos comprobarlo de la siguiente manera:
1
2
3
4
5
6
7
8
9
10
11
12
> db.puntuaciones.getIndexes()

[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "codehero.puntuaciones",
"name" : "_id_"
}
]
Por el momento podremos notar un solo documento en el arreglo que especifica lo
siguiente:
v indica la versin del ndice (versiones de MongoDB previas a la 2.0
mostrarn el valor 0).
key muestra las llaves atadas al indice, en este caso es el campo _id y el
nmero 1 nos indica el ordenamiento ascendente del campo.
ns especifica el contexto del namespace para el ndice.
name es el nombre otorgado al ndice el cual suele estar compuesto de las
llaves o campos que lo componen junto con el orden de los mismos. (para
este caso el nmero de ordenamiento no est presente debido a que se usa
el por defecto).

Creacin de ndices
Ahora creemos nuestros propios ndices.
Simples
Para este caso digamos que solo nos interesa el dinero con el que terminamos al
final, para ello solo debemos hacer lo siguiente:
1 > db.puntuaciones.ensureIndex({ dinero : 1 })
Esto crear un ndice simple ordenado ascendentemente y le permitir al proceso
de mongod realizar operaciones a travs de l cuando se le involucre como filtro.
Por ejemplo veamos quin termino con ms de 50 en dinero:
1
2
3
> db.puntuaciones.find({dinero : { $gt : 50 }})

{ "_id" : ObjectId("525a31aa4582c960d118b1de"), "nombre" : "Ramses", "ganadas" : 8, "perdidas" : 4, "retiradas"
: 3, "dinero" : 120, "mejores" : [ "Escalera real", "Full house" ], "comentarios" : [ { "hora" :
"22:12:56", "texto" : "Los humillar a todos" }, { "hora" : "23:55:59", "texto" : "Gracias por darme su
dinero" } ] }
Esta bsqueda es realizada utilizando el ndice que acabamos de crear.
Cmo saber si una operacin est utilizando un ndice? Tranquilo, en la
segunda parte lo podrs averiguar.
Compuestos
Digamos ahora que queremos enfocarnos en la bsqueda en conjunto de los
parmetros de partidas perdidas y retiradas para analizar un poco la situacin.
Para esto creemos un ndice compuesto con ambos campos:
1 > db.puntuaciones.ensureIndex({ perdidas : 1, retiradas: 1 })
En el caso de los ndices compuestos el orden en que los coloques es muy
importante.
Para el ejemplo que planteamos el orden de cada campo nos indica que MongoDB
podr utilizar el indice para soportarqueries que incluyan el campo perdidas y
aquellos que incluyan ambos campos (perdidas y retiradas); sin embargo
aquellas bsquedas que solo incluyan el campo retiradas no podrn utilizar este
ndice.
1
2
3
> db.puntuaciones.find({perdidas: {$gt:3}})
{ "_id" : ObjectId("525a31aa4582c960d118b1de"), "nombre" : "Ramses", "ganadas" : 8, "perdidas" : 4, "retiradas"
: 3, "dinero" : 120, "mejores" : [ "Escalera real", "Full house" ], "comentarios" : [ { "hora" :
"22:12:56", "texto" : "Los humillar a todos" }, { "hora" : "23:55:59", "texto" : "Gracias por darme su
4
5
6
dinero" } ] }
{ "_id" : ObjectId("525a31ae4582c960d118b1df"), "nombre" : "Oscar", "ganadas" : 4, "perdidas" : 6, "retiradas" :
5, "dinero" : 30, "mejores" : [ "Full house", "1 tro" ], "comentarios" : [ { "hora" : "22:09:12", "texto" : "En
realidad vamos a jugar nada mas para mostrar un ejemplo de Mongo?" }, { "hora" : "23:59:59", "texto" :
"ZZZZZZzzzzzzz" } ] }

> db.puntuaciones.find({perdidas: {$gt:3}, retiradas:{$gt: 3}})
{ "_id" : ObjectId("525a31ae4582c960d118b1df"), "nombre" : "Oscar", "ganadas" : 4, "perdidas" : 6, "retiradas" :
5, "dinero" : 30, "mejores" : [ "Full house", "1 tro" ], "comentarios" : [ { "hora" : "22:09:12", "texto" : "En
realidad vamos a jugar nada mas para mostrar un ejemplo de Mongo?" }, { "hora" : "23:59:59", "texto" :
"ZZZZZZzzzzzzz" } ] }
Suponiendo el caso que hubiesemos incluido el campo ganadas en el
ndice, especficamente en la posicin entre perdidas y retiradas, una
bsqueda que incluya los campos perdidas yretiradas sin duda continua
siendo posible pero ser ms ineficiente que como lo definimos
previamente.

Conclusin
El tema de los ndices es uno de gran importancia si quieres ajustar el rendimiento
de tu esquema de datos, en esta entrada aprendimos sobre ellos y comenzamos a
raspar la superficie sobre su implementacin en MongoDB. No te preocupes, en la
siguiente entrada retomaremos donde nos quedamos y te seguiremos
introduciendo a sus bondades. Solo es una semana, no desesperes.


MongoDB desde Cero: ndices. Parte II

En la entrada pasada comenzamos a hablar sobre la importancia de los ndices en
una base de datos e iniciamos el proceso de implementacin de algunos de ellos
en MongoDB, esta semana seguiremos aprendiendo sobre ellos para cubrir las
posibilidades del uso de los mismos. Recuerda retomar el ejemplo del juego de
poker de la entrada pasada.

ndices multillave
Habamos mencionado que los ndices multillave se basan en utilizacin de
arreglos. Tenemos 2 enfoques y para ello utilizaremos los arreglos que creamos en
nuestro jugadores estrella, los campos mejores (indicando sus mejores
combinaciones de cartas en la noche) y comentarios que fueron algunos de los
captados en la noche del encuentro.
Arreglos bsicos
Veamos el primer caso con los arreglos sencillos del campo mejores:
1 > db.puntuaciones.ensureIndex({ mejores : 1 });
Este ndice crear varias llaves de ndice, una para cada valor del arreglo,
algo parecido a esto:
1
2
Full house => [Ramses, Oscar]
1 trio => [Ricardo, Oscar]
3 Escalera real => [Ramses]
Por lo que es posible utilizar estos valores en las bsquedas al buscar por el
campo mejores utilizando el ndice:
1
2
3
4
> db.puntuaciones.find({ mejores:'Full house' })

{ "_id" : ObjectId("525a31aa4582c960d118b1de"), "nombre" : "Ramses", "ganadas" : 8, "perdidas" : 4, "retiradas"
: 3, "dinero" : 120, "mejores" : [ "Escalera real", "Full house" ], "comentarios" : [ { "hora" :
"22:12:56", "texto" : "Los humillar a todos" }, { "hora" : "23:55:59", "texto" : "Gracias por darme su
dinero" } ] }
{ "_id" : ObjectId("525a31ae4582c960d118b1df"), "nombre" : "Oscar", "ganadas" : 4, "perdidas" : 6, "retiradas" :
5, "dinero" : 30, "mejores" : [ "Full house", "1 tro" ], "comentarios" : [ { "hora" : "22:09:12", "texto" : "En
realidad vamos a jugar nada mas para mostrar un ejemplo de Mongo?" }, { "hora" : "23:59:59", "texto" :
"ZZZZZZzzzzzzz" } ] }
Arreglos con documentos anidados
En este caso se toma el campo del documento interno para hacerlo ndice,
digamos que nos interesan los comentarios que expresaron nuestros jugadores
durante la noche:
1 > db.puntuaciones.ensureIndex({ 'comentarios.texto' : 1 });>
Con la notacin de punto podemos especificar el campo interno del arreglo del
documento padre para que nos sirva de ndice. Lo podramos utilizar en
un query como este:
1
2
3
> db.puntuaciones.find({'comentarios.texto' : 'Los humillar a todos'})

{ "_id" : ObjectId("525a31aa4582c960d118b1de"), "nombre" : "Ramses", "ganadas" : 8, "perdidas" : 4, "retiradas"
: 3, "dinero" : 120, "mejores" : [ "Escalera real", "Full house" ], "comentarios" : [ { "hora" :
"22:12:56", "texto" : "Los humillar a todos" }, { "hora" : "23:55:59", "texto" : "Gracias por darme su
dinero" } ] }

Uso de propiedades
Propiedad de unicidad
Cmo mencionamos anteriormente, esta propiedad nos permite asegurar que el
valor de este campo sea unico. En nuestro caso crearemos un ndice para los
nombres de nuestro jugadores y adems nos aseguraremos que ningn otro
pueda llamarse igual:
1 > db.puntuaciones.ensureIndex({ nombre : 1 }, { unique : true })
Ahora si tratamos de introducir un nuevo jugador con un nombre que ya exista
obtendremos un error de ndice duplicado:
1
2
3
4
5
6
7
> var otroRicardo = {
nombre : 'Ricardo'
}

> db.puntuaciones.insert(otroRicardo)

E11000 duplicate key error index: test.puntuaciones.$nombre_1 dup key: { : "Ricardo" }
Propiedad de dispersin
Esta propiedad particular de los ndices nos permite filtrar los resultados buscados
por el campo indexado dejando por fuera aquellos registros que no poseen el
campo.
Para esto insertemos un nuevo jugador que (entre otras cosas que no nos
interesan para el ejemplo) no posea el campoganadas:
1
2
3
4
5
> var carlos = {
nombre : 'Carlos'
}

> db.puntuaciones.insert(carlos)
Ahora crearemos el ndice de dispersn sobre el campo ganadas:
1 > db.puntuaciones.ensureIndex({ ganadas : 1 }, { sparse : true })
Probemos ahora como los resultados son filtrados:
1
2
3
4
> db.puntuaciones.find().sort({ ganadas : 1 })

{ "_id" : ObjectId("525a31a74582c960d118b1dd"), "nombre" : "Ricardo", "ganadas" : 3, "perdidas" : 3, "retiradas"
: 9, "dinero" : 15, "mejores" : [ "1 tro", "2 pares" ], "comentarios" : [ { "hora" : "22:10:10", "texto" : "Hoy
ser mi da de suerte" }, { "hora" : "23:50:32", "texto" : "Quizs otro da" } ] }
{ "_id" : ObjectId("525a31ae4582c960d118b1df"), "nombre" : "Oscar", "ganadas" : 4, "perdidas" : 6, "retiradas" :
5 5, "dinero" : 30, "mejores" : [ "Full house", "1 tro" ], "comentarios" : [ { "hora" : "22:09:12", "texto" : "En
realidad vamos a jugar nada mas para mostrar un ejemplo de Mongo?" }, { "hora" : "23:59:59", "texto" :
"ZZZZZZzzzzzzz" } ] }
{ "_id" : ObjectId("525a31aa4582c960d118b1de"), "nombre" : "Ramses", "ganadas" : 8, "perdidas" : 4, "retiradas"
: 3, "dinero" : 120, "mejores" : [ "Escalera real", "Full house" ], "comentarios" : [ { "hora" : "22:12:56", "texto"
: "Los humillar a todos" }, { "hora" : "23:55:59", "texto" : "Gracias por darme su dinero" } ] }
Podemos notar que Carlos no se encuentra entre los resultados, esto se debe a
que al colocar el ndice con la propiedad de dispersin sobre el campo ganadas (el
cual es parte del filtro de la bsqueda al utilizarlo para ordenar) esto evita que
aquellos registros que no posean el campo sean excluidos de los resultados.
Si el indice para este campo no tuviese la propiedad de dispersin, en los
resultados de la bsqueda Carlos estuviese de primero, segn el patrn
de ordenamiento ascendente indicado.

Construccin en el fondo
Uno de los factores a considerar en la creacin de ndices es que mientras estos
son construidos la base de datos que los contiene se bloquea para lectura y
escritura. Es posible que al tener un volumen elevado de datos, el proceso de
construccin de ndices tome un tiempo, lo cual llevara a una interrupcin en el
servicio que usa este medio de almacenamiento. Para estos casos existe la opcin
de construccin de ndices en el fondo (o background construction). Esto evita
que la base de datos se bloquee al crear un ndice en particular:
1 > db.puntuaciones.ensureIndex({ campo_que_desees_indexar : 1 }, { background : true })
Ten en cuenta que el proceso de creacin de ndices en el fondo es ms lento que
la creacin normal. Inclusive si los ndices utilizan ms espacio que la memoria
disponible, el proceso se torna considerablemente ms lento.
Reconstruccin de ndices
Estar jugando con los ndices a medida que vamos introduciendo data nueva
causar que algunos registros no estn indexados, por lo que necesitaremos
reconstruirlos, con MongoBD este proceso es muy sencillo, tan solo debemos
ejecutar el mtodo de reindexacin sobre la coleccin deseada:
1 > db.puntuaciones.reIndex()

Medicin de uso
Durante este par de entradas quizs te habrs estado preguntando: Cmo saber
si en realidad las bsquedas estn utilizando los ndices en lugar del procedimiento
normal?.
Para eso tenemos un par mtodos de gran utilidad.
explain()
Este mtodo pretende explicarnos brevemente su plan de ejecucin para una
operacin en particular, hagamos la prueba con una bsqueda sencilla para ver de
qu se trata:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
> db.puntuaciones.find().explain()
{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"n" : 4,
"nscannedObjects" : 4,
"nscanned" : 4,
"nscannedObjectsAllPlans" : 4,
"nscannedAllPlans" : 4,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {

},
"server" : "Mordor.local:27017"
}
Aqu podemos notar un plan de ejecucin comn donde podemos apreciar varias
estadsticas sobre lo que la operacin har, entre ellas la cantidad de objetos que
sern escaneados, la duracin del proceso, y varios detalles sobre la utilizacin de
ndices, en este caso ningn ndice es utilizado, probemos con la bsqueda del
primer ndice que creamos en la entrada pasada:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
> db.puntuaciones.find({dinero : { $gt : 50 }}).explain()

"cursor" : "BtreeCursor dinero_1",
"isMultiKey" : false,
"n" : 1,
"nscannedObjects" : 1,
"nscanned" : 1,
"nscannedObjectsAllPlans" : 1,
"nscannedAllPlans" : 1,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"dinero" : [
[
50,
1.7976931348623157e+308
]
]
},
"server" : "Mordor.local:27017"
}
Notaremos que en este caso las estadsticas son algo diferentes, el cursor es
diferente debido a que ahora posee una estructura de tipo BTREE para recorrer los
nodos de ndices. El nmero de objetos escaneadas en menor y adems nos
especifica el campo del ndice y sus lmites.
hint()
Este mtodo nos permite indicarle a una bsqueda qu ndice debe utilizar.
Probemos con una bsqueda sencilla que obliguemos a utilizar el ndice de dinero:
1
2
3
4
5
> db.puntuaciones.find().hint({ ganadas : 1 })

{ "_id" : ObjectId("525a31a74582c960d118b1dd"), "nombre" : "Ricardo", "ganadas" : 3, "perdidas" : 3, "retiradas"
: 9, "dinero" : 15, "mejores" : [ "1 tro", "2 pares" ], "comentarios" : [ { "hora" : "22:10:10", "texto" : "Hoy
ser mi da de suerte" }, { "hora" : "23:50:32", "texto" : "Quizs otro da" } ] }
{ "_id" : ObjectId("525a31ae4582c960d118b1df"), "nombre" : "Oscar", "ganadas" : 4, "perdidas" : 6, "retiradas" :
5, "dinero" : 30, "mejores" : [ "Full house", "1 tro" ], "comentarios" : [ { "hora" : "22:09:12", "texto" : "En
realidad vamos a jugar nada mas para mostrar un ejemplo de Mongo?" }, { "hora" : "23:59:59", "texto" :
"ZZZZZZzzzzzzz" } ] }
{ "_id" : ObjectId("525a31aa4582c960d118b1de"), "nombre" : "Ramses", "ganadas" : 8, "perdidas" : 4, "retiradas"
: 3, "dinero" : 120, "mejores" : [ "Escalera real", "Full house" ], "comentarios" : [ { "hora" : "22:12:56", "texto"
: "Los humillar a todos" }, { "hora" : "23:55:59", "texto" : "Gracias por darme su dinero" } ] }
Inclusive podemos utilizar este mtodo en conjunto con anterior para ver su plan
de ejecucin:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> db.puntuaciones.find().hint({ ganadas : 1 }).explain()

{
"cursor" : "BtreeCursor ganadas_1",
"isMultiKey" : false,
"n" : 3,
"nscannedObjects" : 3,
"nscanned" : 3,
"nscannedObjectsAllPlans" : 3,
"nscannedAllPlans" : 3,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {
"ganadas" : [
18
19
20
21
22
23
24
25
26
27
28
29
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
},
"server" : "Mordor.local:27017"
}
Tambin es posible forzar el desuso de ndices indicando el operador $natural:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> db.puntuaciones.find({dinero : { $gt : 50 }}).hint({ $natural : 1 }).explain()

{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"n" : 1,
"nscannedObjects" : 4,
"nscanned" : 4,
"nscannedObjectsAllPlans" : 4,
"nscannedAllPlans" : 4,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 0,
"indexBounds" : {

},
19
20
"server" : "Mordor.local:27017"
}

Eliminacin de ndices
Si consideramos que un ndice ya no es necesario podemos eliminarlo
manualmente de la siguiente manera:
1 > db.puntuaciones.dropIndex({ campo_que_deseo_eliminar_el_indice : 1 })
Incluso podemos eliminar todos los ndices de la coleccin a excepcin del _id por
defecto de la siguiente manera:
1 > db.puntuaciones.dropIndexes()

Conclusin
El uso de ndices ayudan mucho en el rendimiento de una base de datos si son
utilizados de la manera correcta, ahora con estos conocimientos puedes llevar un
poco ms all tus estrategias de manipulacin de datos para hacer tus
aplicaciones dependientes de bases de datos MongoDB ms rpidas y mejor
organizadas.


MongoDB desde Cero: Auto-
incremento y Bsquedas Avanzadas

La esencia de una base de datos es la capacidad de almacenar datos; sin
embargo su propsito principal es obtener informacin especfica basada en los
parmetros que necesarios para un momento determinado, esto con la finalidad de
no tener que recorrer manualmente todos los datos que poseemos para obtener lo
que deseamos. Para ello, esta semana hablaremos de las bsquedas avanzadas y
las secuencias auto-incrementadas.

Secuencias Auto-incrementadas
Una de las necesidades con la cual nos hemos encontrado en algn punto al tener
nuestro esquema de base de datos es la de poseer aquella estructura que permite
asignar automticamente el siguiente valor de la secuencia de un campo particular
al insertar un nuevo registro. A esta funcionalidad se le conoce como secuencias
auto-incrementadas, algunas bases de datos permiten establecer un campo con
esta propiedad con tan solo definir una restriccin o constraint; sin embargo el
esquema de datos de MongoDB no adopta nativamente dicho aspecto pero
permite su implementacin siguiendo el patrn abajo descrito.
Para entender el comportamiento supongamos el caso que tenemos una coleccin
de autores y deseamos establecer el campo _id como auto-incrementado.
Este patrn se basa en el uso de una coleccin y funcin auxiliar que permita llevar
y obtener los valores siguientes de la secuencia incremental. Para esto primero
crearemos una coleccin de contadores de la siguiente manera:
1
2
3
4
5
6
> var usuariosAutoincrement = {
_id: 'autoresid',
secuencia: 0
}

> db.contadores.insert(usuariosAutoincrement)
Al especificar el campo secuencia como 0 indicar que la misma
comenzar con este nmero.
Ahora crearemos una funcin Javascript la cual se encargar de buscar el
prximo nmero en la secuencia, veamos de que se trata:
1
2
3
4
5
6
7
8
9
> function proximoEnSecuencia(nombre){
var resultado = db.contadores.findAndModify({
query: { _id: nombre },
update: { $inc: { secuencia: 1 } },
new: true
});

return resultado.secuencia;
}
Bien, veamos en detalle que hace nuestra funcin:
Sobre la coleccin contadores hacemos una bsqueda y actualizacin al
mismo tiempo (findAndModify).
El parmetro query nos especifica qu documento de la
coleccin contadores debemos buscar, es decir, aquel con el _id que se
especifique como parmetro de la funcin.
El parmetro update indica que luego de haber encontrado el documento en
cuestin se debe incrementar ($inc) el campo secuencia en 1.
El parmetro new le indica al mtodo findAndModify que debe arrojar como
resultado el nuevo documento en lugar del original, es decir, aquel que ya ha
sido actualizado con el incremento.
El resultado de este mtodo findAndModify es asignado a una variable y
debido a que el resultado es un documento, podemos finalmente retornar el
campo secuencia de dicho documento, el cual ser el prximo nmero en la
secuencia.
Ahora cuando queramos hacer uso de dicha funcin para que se encargue de
asignar automticamente el siguiente _id para nuestra coleccin de autores lo
haremos de la siguiente manera:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> var oscar = {
_id: proximoEnSecuencia('autoresid'),
nombre: 'Oscar',
edad: 25
};

> var alberto = {
_id: proximoEnSecuencia('autoresid'),
nombre: 'Alberto',
edad: 'veintiseis'
};

> var jonathan = {
_id: proximoEnSecuencia('autoresid'),
nombre: 'Jonathan',
apellido: 'Wiesel'
};

> db.autoresAutoIncrement.insert(oscar);
> db.autoresAutoIncrement.insert(alberto);
> db.autoresAutoIncrement.insert(jonathan);
Probemos que en efecto nuestra solucin ha hecho su trabajo:
1
2
3
4
5
6
> db.autoresAutoIncrement.find()
{ "_id" : 1, "nombre" : "Oscar", "edad" : 25 }
{ "_id" : 2, "nombre" : "Alberto", "edad" : "veintiseis" }
{ "_id" : 3, "nombre" : "Jonathan", "apellido": "Wiesel" }

> db.contadores.find()
7 { "_id" : "autoresid", "secuencia" : 3 }
Notemos que nuestro autores han tomado su respectivo valor de la secuencia
mientras que el documento de la secuencia como tal permanece actualizado con el
ltimo valor utilizado.
Selectores de bsqueda
Como mencionamos anteriormente el poder que ofrece una base de datos reside
en la capacidad que esta tiene para poder ofrecer los datos que necesitamos en un
momento especifico segn las necesidades que se nos presenten en dicha
situacin. Ciertamente vimos como filtrar las bsquedas en nuestra segunda
entrada del curso; sin embargo en esta entrada veremos algo un poco ms
avanzado.
Veamos algunas de las diferentes maneras de filtrar nuestras bsquedas
haciendo uso de diferentes tipos de operadores o selectores de bsquedas.
Adicionalmente notaremos que se enfoca a lo mismo que conocemos en SQL.
Comparativos
$gt mayor a X valor.
$gte mayor o igual a X valor.
$lt menor a X valor.
$lte menor o igual a X valor.
$ne distinto a X valor.
$in entre los siguientes [ X, Y, ... ]
$nin no est entre los siguientes [ X, Y, ... ]
Los primeros 4 evidentemente estn enfocados a valores numricos y pueden ser
utilizados de las siguiente manera:
1
2
3
> db.autoresAutoIncrement.find({ _id : { $gt : 1 } })
{ "_id" : 2, "nombre" : "Alberto", "edad" : "veintiseis" }
{ "_id" : 3, "nombre" : "Jonathan", "apellido": "Wiesel" }
En SQL sera algo como SELECT * FROM autoresAutoIncrement WHERE _id >
1
El operador $ne (distino de) como podrs adivinar puede utilizarse para campos
numricos y no numricos. Mientras que los ltimos 2 operadores se enfocan en la
comparacin con arreglos de valores:
1
2
> db.autoresAutoIncrement.find({ nombre : { $in : ['Alberto', 'Ricardo', 'Oscar'] } })
{ "_id" : 1, "nombre" : "Oscar", "edad" : 25 }
3 { "_id" : 2, "nombre" : "Alberto", "edad" : "veintiseis" }
En SQL sera algo como SELECT * FROM autoresAutoIncrement WHERE
nombre in ('Alberto', 'Ricardo', 'Oscar')
Lgicos
$or
$and
$nor
$not
Estos operadores lgicos nos permiten juntar mltiples condiciones y dependiendo
del cumplimiento de alguna de ellas ($or), todas ellas ($and) o ninguna de ellas
($nor) obtendremos lo que deseamos, inclusive si lo que deseamos es
completamente lo opuesto ($not) a lo que especificamos como condicin de
bsqueda.

1
2
3
> db.autoresAutoIncrement.find({ $or : [{_id: 1}, {nombre: 'Jonathan'}] })
{ "_id" : 1, "nombre" : "Oscar", "edad" : 25 }
{ "_id" : 3, "nombre" : "Jonathan", "apellido": "Wiesel" }
En SQL sera algo como SELECT * FROM autoresAutoIncrement WHERE _id =
1 OR nombre = 'Jonathan'
En el caso del operador $and, si has prestado atencin a lo largo de la serie te
dars cuenta que MongoDB maneja implcitamente este tipo de operador, si no lo
recuerdas puedes visitar el curso de operaciones bsicas para refrescar la
memoria.
Para el operador $nor seguiramos la misma notacin que el ejemplo anterior con
la diferencia que obtendramos como resultado aquellos registros que ni tengan
el _id = 1 ni el nombre = Jonathan. Por lo que obtendramos a Alberto
nicamente.
Finalmente el operador $not acta sobre el operador que le siga y como podrs
imaginar, devolver el resultado contrario.
1
2
3
> db.autoresAutoIncrement.find({ _id : { $not: { $gt: 2 }} })
{ "_id" : 1, "nombre" : "Oscar", "edad" : 25 }
{ "_id" : 2, "nombre" : "Alberto", "edad" : "veintiseis" }
Al principio pensars:
Por qu usar este operador si pude haber utilizado el $lte?
Una de las ventajas que quizs pasaste por alto es que suponiendo el caso donde
dicho filtro se hace sobre otro campo distinto al de _id el cual no es obligatorio,
usar el operador $lte obtendr aquellos documentos con el campo mayor o igual
al valor indicado; sin embargo al utilizar el operador $not tambin obtendremos
aquellos documentos que ni siquiera poseen el campo.
Elementales
$exists
$type
Este tipo de operadores elementales permiten hacer comparaciones referentes a
las propiedades del campo como tal.
En el caso de $exist, es un operador booleano que permita filtrar la bsqueda
tomando en cuenta la existencia de un campo en particular:
1
2
> db.autoresAutoIncrement.find({ apellido: { $exists: true }})
{ "_id" : 3, "nombre" : "Jonathan", "apellido" : "Wiesel" }
Notaremos que hemos filtrado la bsqueda para que arroje nicamente los
documentos que poseen el campo apellido.
Para el caso de $type podemos filtrar por la propiedad de tipo de campo y como
valor especificaremos el ordinal correspondiente a su tipo de dato BSON basado
en lo siguiente:
1 Double
2 String
3 Objeto
4 Arreglo
5 Data binaria
6 Indefinido (deprecado)
7 Id de objeto
8 Booleano
9 Fecha
10 Nulo
11 Expresin regular
13 Javascript
14 Smbolo
15 Javascript con alcance definido
16 Entero de 32bit
17 Estampilla de tiempo
18 Entero de 64bit
127 Llave mxima
255 Llave mnima
1 > db.autoresAutoIncrement.find({ edad: { $type: 1 }})
2
3
4
5
{ "_id" : 1, "nombre" : "Oscar", "edad" : 25 }

> db.autoresAutoIncrement.find({ edad: { $type: 2 }})
{ "_id" : 2, "nombre" : "Alberto", "edad" : "veintiseis" }
Notemos que para el primer caso indicamos el tipo de campo Double en
lugar de uno entero, esto se debe a que el nico tipo de dato numrico
nativo existente en Javascript es de tipoDouble y al ser insertado por la
consola de MongoDB se torna en este tipo de dato.

Conclusin
Hemos dado las herramientas para que puedas hacer los filtros que necesites para
tu aplicacin y finalmente lograr obtener la informacin especfica necesaria para
manejarla correctamente. Ten en cuenta que algunos ORM (modeladores de
relaciones y objetos) utilizan una sintaxis parecida a la que vimos en esta entrada
por lo que recordarla te podr ayudar en el futuro para otras cosas. Hasta la
prxima!


MongoDB desde Cero: Seguridad

Hemos progresado mucho desde que empezamos esta serie; sin embargo, antes
de pensar en implementar este tipo de base de datos en un ambiente de
produccin debemos saber como protegerla para que no pueda ser violentada y la
informacin que esta contiene no sea robada o alterada, para ello esta semana
hablaremos sobre las consideraciones de seguridad a tomar en cuenta al usar
MongoDB.

Autenticacin
El primer mtodo de proteccin de la base de datos es quizs el ms comn.
Evitar que cualquiera pueda acceder a la instancia mediante unas credenciales. El
procedimiento es bastante intuitivo, debemos ingresar a la instancia, crear un
usuario administrador y reiniciar dicha instancia. Veamos de que se trata:
Primero ingresemos a la instancia y nos cambiaremos a la base de datos
administradora que reside dentro de ella:
1
2
3
4
5
$ mongo
MongoDB shell version: 2.4.7
...
> use admin
switched to db admin
Ahora creemos nuestro nuevo usuario administrador:
1 > db.addUser('jonathan','c0d3h3r0')
En la versin 2.6 el mtodo addUser ha quedado desaprobado
o deprecado, se debe usarcreateUser en su lugar
(Fuente: Documentacin de MongoDB).
Bien, ahora en el archivo de configuracin de la instancia habilitaremos la
autenticacin:
1 auth = true
Finalmente reiniciemos la instancia, volvamos a acceder a ella y tratemos de listar
las bases de datos:
1
2
3
4
5
$ mongo
MongoDB shell version: 2.4.7
...
> show dbs
Sun Nov 10 17:14:05.505 listDatabases failed:{ "ok" : 0, "errmsg" : "unauthorized" } at
src/mongo/shell/mongo.js:46
Si no recuerdas donde est el archivo de configuracin para tu tipo de
sistema o cmo reiniciar los servicios, vuelve a nuestra primera entrada
de la serie.
Notaremos que hemos recibido un error de unauthorized (no autorizado).
Suministremos nuestras credenciales para poder trabajar con normalidad:
1
2
3
4
5
6
7
8
9
> use admin
switched to db admin
> db.auth('jonathan','c0d3h3r0')
1
> show dbs
admin 0.203125GB
codehero 0.203125GB
local 0.078125GB
test 0.203125GB
Como nuestras credenciales residen en la base de datos administrador, notemos
que debimos cambiar a ella para autorizarnos.
Si quisiramos crear un usuario para una base de datos en particular, luego de
estar autorizado como el usuario administrador nos cambiamos a la base de datos
que queramos y creamos un usuario de la misma manera que antes. Este usuario
solo podr realizar operaciones sobre esta base de datos especificada:
1
2
3
4
5
6
7
8
9
10
...
> use codehero
switched to db codehero
> db.addUser('blogAdmin','1234')
{
"user" : "blogAdmin",
"readOnly" : false,
"pwd" : "49e2220035d3d25cf3010bb9ff9f8ad9",
"_id" : ObjectId("5280083e4c857f20fa16e052")
}

Autorizacin
Cuando estamos creando los usuarios sobre una base de datos es comn que
queramos definir ciertas restricciones para cada uno de ellos solo pueda realizar
ciertas operaciones sobre la base de datos. A este conjunto de restricciones o
privilegios se les conoce como Roles, veamos los diferentes tipos de roles que
hay:
read permite realizar operaciones de lectura sobre las colecciones que
componen una BD.
readWrite operaciones de lectura y escritura sobre las colecciones de una
BD.
dbAdmin permite realizar diversas tareas administrativas de una BD.
userAdmin ofrece acceso de lectura y escrituro a la coleccin de usuarios
de una BD.
Mltiples bases de datos
Estos roles permiten realizar las operaciones que se indicaron anteriormente pero
para cualquier base de datos, por lo tanto deben ser definidas en la base de
datos admin.
readAnyDatabase
readWriteAnyDatabase
userAdminAnyDatabase
dbAdminAnyDatabase
Administrativo
clusterAdmin permite el acceso a diferentes operaciones referentes al
sistema como tal, utilizado especialmente en estrategias de replicacin y
fragmentacin.
Para definir los roles que un usuario puede tener, cuando estemos agregando el
usuario lo haremos mediante un documento como este:
1
2
3
4
5
6
db.addUser({
user: 'nombre_de_usuario',
pwd: 'contrasea',
roles: ['rol1','rol2', ... ]
})


Exposicin
Otra manera de controlar el acceso a nuestra base de datos es manipulando los
niveles a los que esta se encuentra expuesta la instancia, veamos algunas
estrategias de como lograrlo.
Pgina de estatus
Si accedes por medio del explorador de internet a la direccin donde se encuentra
tu instancia de MongoDB especificando el puerto 28017 (por defecto), podremos
notar una pgina como esta:

Evidentemente es una pgina que no queremos quede al descubierto en nuestro
ambiente de produccin ya que revela muchsima informacin sensible.
Por esto, en nuestro archivo de configuracin colocaremos lo siguiente:
1 nohttpinterface = true
Si deseas mantenerla disponible solo para aquellas personas en las que confas,
es recomendable ajustar tus opciones en elfirewall para que informacin tan
sensible no quede a la vista de todos.
Interface REST
Si al entrar en la pgina anterior intentaste acceder a alguno de los vnculos de
comandos o estatus, te habrs dado cuenta que ha ocurrido un error de que no
tienes REST activado. Esta interface interactiva permite realizar algunas tareas
administrativas que pueden ser de utilidad.
Para activarlo, solo debemos colocar en nuestra archivo de configuracin:
1 rest = true
Esta interface no implementa ningn tipo de autenticacin por lo que se
recomienda tener cuidado al ser expuesta.
Ligado de IPs
Otra manera muy efectiva de limitar el acceso es usar el parmetro bind_ip, esto
definir qu direcciones IP pueden tener acceso a la instancia.
Para activarlo basta con especificarlo en nuestro archivo de configuracin:
1
2
3
4
5
bind_ip = 127.0.0.1 # suponiendo que solo quieremos que acceda algo en el mismo equipo

# o si queremos especificar varios, lo podemos hacer separndolos por coma:

bind_ip = 127.0.0.1, 192.168.0.105, 192.168.0.111
Esto no es una garanta absoluta ya que un especialista en seguridad
podra hacerse pasar por una IP en la que tu instancia confa mediante lo
que se conoce como IP Spoofing.
El puerto
Quizs uno de los detalles ms pasados por alto. Usar el puerto principal por
defecto (27017) puede llevar a que cualquiera que sepa que ests utilizando
MongoDB sabr el puerto que debe atacar para hacerse con la base de datos. Es
recomendable cambiar este puerto en un ambiente de produccin, basta tan solo
especificarlo en nuestro archivo de configuracin:
1 port = 22622
En caso de cambiar el puerto de la instancia, la pgina de estatus se
encontrar en el puerto especificado + 1000, en este caso se encontrara
en el 23622.
Firewall
Siendo el mtodo ms confiado, establecer polticas de firewall permite que
nicamente aquellos que le sea permitido el acceso al servidor puedan acceder a
la instancia de base de datos.
En caso de instalar en un VPS, es probable que tengas alguna interfaz grfica en
el rea de administracin que te permita manipular las opciones del firewall.
De lo contrario siempre puedes utilizar soluciones personalizadas
de firewall como iptables en sistemas Linux, ipfw en sistemas Mac OS X /
FreeBSD y netsh en sistemas Windows.

Conclusin
Proteger los datos que residen en las bases de datos las cuales alimentan las
aplicaciones que ofrecemos es un derecho y nuestro deber como desarrolladores
ya que a nadie le gustara que la informacin que le confiaste a una aplicacin en
particular ande rondando por ah a la vista de todos. Ahora que sabes como
hacerlo, ests un paso ms cerca de ser un hroe en MongoDB.


MongoDB desde Cero: Colecciones
Limitadas y Expiraciones

Una base de datos puede crecer rpidamente para algunos casos de uso en los
cuales se desea almacenar informacin histrica como eventos, bitcoras
personalizadas, informacin de sesiones y otros. Ms importante aun es que
muchas veces dicha informacin solo nos es til por un margen de tiempo
determinado, luego terminan siendo desechos y a medida que pasa el tiempo
pueden afectar el rendimiento. Por esta razn esta semana hablaremos de como
mantener solo la informacin que necesitamos y aquella que es vieja, desecharla y
que deje de ser parte de la base de datos.

Expiraciones o Tiempos de Vida
(TTL)
Esta funcionalidad nos permite establecer un tiempo de vigencia sobre los
documentos de una coleccin. MongoDB maneja estos tiempos de vida mediante
el uso de un ndice especial con el mismo nombre (TTL index), uno de los hilos del
proceso demongod se encarga de realizar las bsquedas necesarias usando estos
ndices con el fin de determinar aquellos documentos que cumplan con la
condicin de vida preestablecida y los elimina automticamente. Adems veremos
las 2 maneras de establecer el vencimiento de un documento.
El proceso de bsqueda y eliminacin es ejecutado cada 60 segundo
aproximadamente, por lo que es posible que un documento contine
siendo parte de la coleccin luego de su tiempo de vida
preestablecido, adicionalmente se puede producir un retraso
dependiendo de la carga de trabajo que tenga la instancia en dicho
momento.
Existen algunas consideraciones respecto a los ndices TTL que debemos tomar
en cuenta cuando vayamos a implementar este tipo de solucin.
Restricciones
Los ndices TTL deben hacerse sobre campos que no posean otro ndice.
El campo a indexar debe ser de tipo fecha o un arreglo de fechas.
En caso de tener un arreglo de fechas, el documento expirar cuando la
menor fecha sea alcanzada.
El ndice TTL no puede ser compuesto.
Expiracin en cuenta regresiva
Esta estrategia se basa en definir la duracin en segundos que tendrn de vigencia
los documentos de una coleccin especifica.
Para ello crearemos una coleccin con un par de documentos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> var fantasma1 = {
fecha: new Date(),
mensaje: 'buuuu'
}

> var fantasma2 = {
fecha: new Date(),
mensaje: 'no durare mucho tiempo'
}

> db.fantasmas.insert(fantasma1)
> db.fantasmas.insert(fantasma2)

> db.fantasmas.find()
{ "_id" : ObjectId("52891f4d9f5ebcdb2a850063"), "fecha" : ISODate("2013-11-17T19:55:46.097Z"), "mensaje" :
"buuuu" }
{ "_id" : ObjectId("52891f4f9f5ebcdb2a850064"), "fecha" : ISODate("2013-11-17T19:55:50.721Z"), "mensaje" :
"no durare mucho tiempo" }
El comando new Date() crear un objeto tipo fecha con la fecha y hora de
este momento.
Bien, ahora crearemos el ndice TTL y especificaremos la duracin de vigencia que
deben tener los documentos.
1 > db.fantasmas.ensureIndex({ fecha : 1 }, { expireAfterSeconds : 300 })
En este caso especificamos que aquellos documentos que tengan en su
campo fecha un valor mayor a 300 segundos (5 minutos) de antigedad deben ser
eliminados.
Luego de que hayan pasado 5 minutos si volvemos a buscar los documentos de la
coleccin estos ya no existirn. Ten en cuenta que si un documento no posee el
campo fecha o si el campo no es de tipo date este simplemente no se vencer.
Expiracin en hora especifica
Esta segunda estrategia se basa en la definicin especifica en cada documento de
cuando este debe vencer, esto nos permitir establecer un comportamiento
dinmico para cada documento y que cada uno pueda tener ms o menos vigencia
que aquellos que comparten la misma coleccin.
Para demostrarlo crearemos un par de documentos con fechas diferentes, una con
una fecha bastante prxima, digamos 5 minutos y otro para el ao que viene, lo
cual nos permitir apreciar el comportamiento:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
> var fantasma3 = {
fecha: new Date('Nov 17, 2013. 16:00:00'),
mensaje: 'solo un susto y me voy'
}

> var fantasma4 = {
fecha: new Date('Nov 17, 2014. 16:00:00'),
mensaje: 'estare aqui un largo tiempo'
}

> db.otrosFantasmas.insert(fantasma3)
> db.otrosFantasmas.insert(fantasma4)

> db.otrosFantasmas.find()
{ "_id" : ObjectId("528927c79f5ebcdb2a85006a"), "fecha" : ISODate("2013-11-17T20:30:00Z"), "mensaje" :
"solo un susto y me voy" }
{ "_id" : ObjectId("5289281f9f5ebcdb2a85006b"), "fecha" : ISODate("2014-11-17T20:30:00Z"), "mensaje" :
"estare aqui un largo tiempo" }
Podemos notar que la hora que fue insertada no es la misma que la que
indicamos, esto se debe a que la hora almacenada se encuentra en el
horario GMT 0, como en Venezuela estamos en GMT-4:30 por ello se
almacena la hora como a las 8:30pm en lugar de las 4:00pm
Por ltimo colocaremos el ndice TTL pero en este caso colocaremos la vigencia
como cero, esto le indicar a MongoDB que debe vencer los documentos segn la
fecha indicada en el campo fecha de cada uno:
1 > db.otrosFantasmas.ensureIndex({ fecha : 1 }, { expireAfterSeconds : 0 })
Ahora si esperamos a que se cumple la hora que estipulamos y buscamos los
documentos de nuestra coleccin veremos que el que se venca prximamente ya
no existe, y el del ao que viene todava lo sigue ah:
1
2
> db.otrosFantasmas.find()
{ "_id" : ObjectId("5289281f9f5ebcdb2a85006b"), "fecha" : ISODate("2014-11-17T20:30:00Z"), "mensaje" :
"estare aqui un largo tiempo" }

Colecciones Limitadas
Este tipo de colecciones cumple el propsito de almacenamiento circular de
documentos, es decir, la coleccin al alcanzar un tamao determinado se empieza
a sobreescribir desde el inicio, muy similar a lo que sera un buffer circular.
Otra ventaja de las colecciones limitadas es su alto rendimiento de insercin ya
que su informacin es almacenada (y devuelta al consultar) en el orden natural que
fueron insertados.
Sin embargo estas caractersticas no vienen sin sacrificio alguno, hay ciertos
aspectos a considerar.
Restricciones
No se pueden eliminar documentos particulares.
Al actualizar documentos ya existentes, estos no pueden crecer su tamao
en disco.
TTL no es compatible con estas colecciones.
Esto viene dado por el hecho de que al declarar una coleccin limitada, se reserva
un espacio en disco para ella y los documentos que contendr, a su vez, como los
documentos se almacenarn de manera continua en disco, esto limita que no se
puedan eliminar documentos ni incrementar su tamao para que la estructura del
espacio alocado no sea alterado.
Creacin
Para crear una coleccin limitada lo haremos de una manera un poco diferente a
como estamos acostumbrados, ya que debemos especificar la capacidad que
tendr:
1 > db.createCollection( 'eventos', { capped: true, size: 1000000, max: 10000 } )
En este caso estamos creando una coleccin llamada eventos la cual
especificamos que tiene que ser limitada (capped), tambin delimitamos el espacio
en disco (en bytes) que deber ser alocado, en este caso un poco menos de
1MB,opcionalmente podemos adems especificar la cantidad mxima de
documentos que podr contener esta coleccin, para este caso 10000
documentos.
Conversin
Es posible convertir una coleccin normal a limitada sin necesidad de crearla
desde cero, para esto debemos ejecutar el comando de conversin y especificar
los lmites:
1
2
3
4
5
6
7
8
> db.otrosFantasmas.isCapped()
false

> db.runCommand({"convertToCapped": "otrosFantasmas", size: 1000000});
{ "ok" : 1 }

> db.otrosFantasmas.isCapped()
true
Hemos convertido una coleccin con ndices TTL a limitada, por lo tanto
si ejecutamos el comando db.otrosFantasmas.getIndexes() notaremos que
ya el ndice no existe.

Conclusin
Hemos visto como evitar que nuestra base de datos se llene con informacin que
luego de un tiempo deja de ser relevante, esto es parte vital que impacta sobre el
rendimiento de la misma ya que mientras ms informacin existe, ms tardarn las
bsquedas en realizarse. Hasta la prxima semana.


MongoDB desde Cero: Respaldos y
Restauracin

Como bien sabemos, parte importante del mantenimiento de una base de datos es
la prevencin al desastre por medio del respaldo de la informacin que esta
contiene, de igual manera debemos estar preparados para saber como restaurar
dicha informacin nuevamente. Veamos como llevar a cabo estas tareas.

Volcado (dump)
MongoDB posee una herramienta muy til que nos permite hacer un volcado de la
informacin de la base de datos a un archivo de respaldo. Esta herramienta se
llama mongodump, y se utiliza por medio de la consola o terminal de comandos.
Un uso muy bsico sera simplemente ejecutar:
1 $ mongodump
Esto se conecta a la instancia de Mongo que se encuentra ejecutandose en el
mismo equipo, en el puerto por defecto 27017 y crea un archivo de respaldo de
todas las bases de datos de la instancia (menos local) y lo almacena en un
directoriodump/ de la ruta de donde se ejecut el comando.
Ciertamente podemos agregarle algunos parmetros a este comando para
adaptarlo a nuestras necesidades:
--out se especifica un directorio distinto al por defecto dump/ para que se
almacene el respaldo.
--port se especifica un puerto, en caso que no se utilice el por
defecto 27017.
--host se especifica la direccin donde reside la instancia, en caso que no
se utilice el por defecto localhost.
--db se especifica una base de datos particular en lugar de tomar todas.
--collection usado en conjunto con --db se especifica una coleccin
particular que se quiera extraer de dicha base de datos.
--dbpath se especifica el directorio que contiene los archivos de las bases
de datos. Esto es sumamente til en caso de que el proceso de mongod no
est ejecutandose ya que podemos acceder directamente a sus archivos.
--username y --password permite especificar credenciales de usuario en
caso de que estas sean requeridas.
Para nuestro ejemplo volquemos la informacin de la base de datos codehero:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ mongodump --db codehero

connected to: 127.0.0.1
Fri Nov 29 23:17:51.202 DATABASE: codehero to dump/codehero
Fri Nov 29 23:17:51.203 codehero.system.indexes to dump/codehero/system.indexes.bson
Fri Nov 29 23:17:51.204 6 objects
Fri Nov 29 23:17:51.204 codehero.autores to dump/codehero/autores.bson
Fri Nov 29 23:17:51.228 5 objects
Fri Nov 29 23:17:51.228 Metadata for codehero.autores to dump/codehero/autores.metadata.json
Fri Nov 29 23:17:51.229 codehero.system.users to dump/codehero/system.users.bson
Fri Nov 29 23:17:51.230 0 objects
Fri Nov 29 23:17:51.230 Metadata for codehero.system.users to dump/codehero/system.users.metadata.json
Fri Nov 29 23:17:51.230 codehero.fantasmas to dump/codehero/fantasmas.bson
Fri Nov 29 23:17:51.237 0 objects
Fri Nov 29 23:17:51.237 Metadata for codehero.fantasmas to dump/codehero/fantasmas.metadata.json
Fri Nov 29 23:17:51.245 codehero.otrosFantasmas to dump/codehero/otrosFantasmas.bson
Fri Nov 29 23:17:51.259 1 objects
Fri Nov 29 23:17:51.259 Metadata for codehero.otrosFantasmas to
dump/codehero/otrosFantasmas.metadata.json

$ cd dump/codehero
~/dump/codehero $ ls
22
23
24
25

autores.bson fantasmas.metadata.json system.indexes.bson
autores.metadata.json otrosFantasmas.bson system.users.bson
fantasmas.bson otrosFantasmas.metadata.json system.users.metadata.json
Dicha base de datos debera estar presente y con algunas colecciones si
le has seguido el paso a la serie.

Restauracin
El proceso de restauracin es bastante similar al de volcado, el comando para
dicha accin es mongorestore.
Primero borremos nuestra base de datos codehero de la instancia local:
1
2
3
4
5
6
7
8
9
10
11
$ mongo
> use codehero
switched to db codehero
> db.dropDatabase()
{ "dropped" : "codehero", "ok" : 1 }
> show dbs
admin 0.203125GB
codehero (empty)
local 0.078125GB
test 0.203125GB
> exit
Notaremos que la base de datos se encuentra totalmente vaca.
Ahora probaremos restaurando el respaldo que hicimos anteriormente:
1
2
3
4
5
$ mongorestore --db codehero dump/codehero

connected to: 127.0.0.1
Fri Nov 29 23:33:07.464 dump/codehero/autores.bson
Fri Nov 29 23:33:07.464 going into namespace [codehero.autores]
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
5 objects found
Fri Nov 29 23:33:07.465 Creating index: { key: { _id: 1 }, ns: "codehero.autores", name: "_id_" }
Fri Nov 29 23:33:07.919 dump/codehero/fantasmas.bson
Fri Nov 29 23:33:07.919 going into namespace [codehero.fantasmas]
Fri Nov 29 23:33:07.999 Created collection codehero.fantasmas with options: { "create" : "fantasmas", "flags" :
1 }
Fri Nov 29 23:33:07.999 file dump/codehero/fantasmas.bson empty, skipping
Fri Nov 29 23:33:07.999 Creating index: { key: { _id: 1 }, ns: "codehero.fantasmas", name: "_id_" }
Fri Nov 29 23:33:07.999 Creating index: { key: { fecha: 1 }, ns: "codehero.fantasmas", name: "fecha_1",
expireAfterSeconds: 300 }
Fri Nov 29 23:33:08.002 dump/codehero/otrosFantasmas.bson
Fri Nov 29 23:33:08.002 going into namespace [codehero.otrosFantasmas]
Fri Nov 29 23:33:08.005 Created collection codehero.otrosFantasmas with options: { "create" :
"otrosFantasmas", "capped" : true, "size" : 1000000 }
1 objects found
Fri Nov 29 23:33:08.007 Creating index: { key: { _id: 1 }, ns: "codehero.otrosFantasmas", name: "_id_" }
Fri Nov 29 23:33:08.008 dump/codehero/system.users.bson
Fri Nov 29 23:33:08.009 going into namespace [codehero.system.users]
Fri Nov 29 23:33:08.051 file dump/codehero/system.users.bson empty, skipping
Fri Nov 29 23:33:08.051 Creating index: { key: { _id: 1 }, ns: "codehero.system.users", name: "_id_" }
Fri Nov 29 23:33:08.053 Creating index: { key: { user: 1, userSource: 1 }, unique: true, ns:
"codehero.system.users", name: "user_1_userSource_1" }
Ahora entremos nuevamente a la instancia para verificar la informacin:
1
2
3
4
5
6
7
8
9
10
$ mongo
> show dbs
admin 0.203125GB
codehero 0.203125GB
local 0.078125GB
test 0.203125GB
> use codehero
switched to db codehero
> show collections
autores
11
12
13
14
15
16
17
18
19
fantasmas
otrosFantasmas
system.indexes
system.users
> db.autores.find()
{ "_id" : ObjectId("5232344a2ad290346881464a"), "nombre" : "Jonathan", "apellido" : "Wiesel", "secciones" :
[ "Como lo hago", "MongoDB" ] }
{ "_id" : ObjectId("523236022ad290346881464b"), "nombre" : "Oscar", "apellido" : "Gonzalez", "secciones" :
[ "iOS", "Objective C", "NodeJS" ], "socialAdmin" : true }
{ "_id" : ObjectId("5232383a2ad290346881464c"), "nombre" : "Alberto", "apellido" : "Grespan", "secciones" :
"Git", "genero" : "M" }
{ "_id" : ObjectId("5246049e7bc1a417cc91ec8c"), "nombre" : "Ramses", "apellido" : "Velazquez", "secciones" :
[ "Laravel", "PHP" ] }
Notaremos que la informacin efectivamente ha sido restaurada con xito.

Migracin
Es posible que nos encontremos en la situacin donde debamos migrar una base
de datos de una instancia a otra, afortunadamente MongoDB hace este proceso
sumamente sencillo.
Como instancia destino usar una mquina virtual de Vagrant, pero puedes
probarlo con cualquier otro equipo con MongoDB que tengas a la mano.
Puedes volver a la primera entrada de la serie si quieres instalar
MongoDB en tu equipo destino.
Primero debemos acceder a la instancia de Mongo del equipo destino y tan solo
haremos lo siguiente:
1
2
3
4
5
6
7
> db.copyDatabase('codehero','codeheroRemoto','192.168.0.100')
{ "ok" : 1 }

> show dbs
codeheroRemoto 0.0625GB
local 0.03125GB
> use codeheroRemoto
8
9
10
11
12
13
14
15
16
17
18
19
switched to db codeheroRemoto
> show collections
autores
fantasmas
otrosFantasmas
system.indexes
system.users
> db.autores.find()
{ "_id" : ObjectId("5232344a2ad290346881464a"), "nombre" : "Jonathan", "apellido" : "Wiesel", "secciones" :
[ "Como lo hago", "MongoDB" ] }
{ "_id" : ObjectId("523236022ad290346881464b"), "nombre" : "Oscar", "apellido" : "Gonzalez", "secciones" :
[ "iOS", "Objective C", "NodeJS" ], "socialAdmin" : true }
{ "_id" : ObjectId("5232383a2ad290346881464c"), "nombre" : "Alberto", "apellido" : "Grespan", "secciones" :
"Git", "genero" : "M" }
{ "_id" : ObjectId("5246049e7bc1a417cc91ec8c"), "nombre" : "Ramses", "apellido" : "Velazquez", "secciones" :
[ "Laravel", "PHP" ] }
Recuerda que si tienes la opcin bind_ip asignada a una IP particular en
tu archivo de configuracin de la instancia de
origen (generalmente localhost), solo esa IP podr acceder a ella y
efectivamente bloquear las conexiones para copiar una base de datos a
otra instancia.
Notemos que el comando copyDatabase recibi como opciones:
Base de datos origen. codehero
Base de datos destino. codeheroRemoto
Direccin de instancia de origen 192.168.0.100
Podriamos concatenarle el puerto de ser necesario
192.168.0.100:27017
En caso que la base de datos tuviese restringido el acceso por autorizacin de
usuario podramos pasarle un par de opciones ms con el nombre de
usuario y clave.

Conlusin
Hemos aprendido como manipular los respaldos de una base de datos de
MongoDB y ya estamos en capacidad de migrar su informacin de manera
sencilla. Debemos resaltar que estas son solo algunas a de las medidas de
prevencin a tomar, ms adelante veremos algunas ms avanzadas y como
responder a una situacin catastrfica.


MongoDB desde Cero: Replicacin
Parte I
Una de las tcnicas utilizadas ampliamente en sistemas de produccin es la
replicacin de los datos a travs de distintas instancias y computadores para
asegurar que la informacin est siempre disponible y reducir los riesgos de
prdida o corrupcin de la misma. Esta semana veremos como encargarnos de
esto en MongoDB.

Funcionamiento conceptual
El principal propsito de la implementacin de estrategias de replicacin de datos
es incrementar la redundancia de los mismos, esto nos permite tener una base de
datos de alta disponibilidad e integridad. Una base de datos al tener varias copias
exactas en diferentes infraestructuras separadas asegura que si una de las
instancias falla, ya sea una falla catastrfica a nivel de hardware o situaciones
diversas que pudiesen corromper y/o evitar el acceso a la data, el sistema que
dicha base de datos alimente no se vea afectado ya que existen otras instancias
espejo que tomarn el lugar de la original mediante un proceso transparente para
los usuarios finales.

Arquitectura
En MongoDB, al grupo de instancias que poseern la misma informacin se les
denomina replica set o grupo de replicacin.
Un replica set en MongoDB est compuesto por 2 tipos principales de miembros,
instancias primarias y secundarias, teniendo una nica instancia primaria la cual
aceptar todas las operaciones de escritura provenientes de los sistemas cliente.
Las operaciones de lectura son dirigidas por defecto a la instancia primaria; sin
embargo es posible configurar la lectura a instancias secundarias.

Estas operaciones que alteran los datos son escritas en un archivo
llamado oplog o bitcora de operaciones, los miembros secundarios replican este
archivo del miembro primario y ejecutan las mismas operaciones sobre su conjunto
de datos, esto permite tener la misma informacin en las diferentes instancias.

Debemos tomar en cuenta que debido a que el procedimiento de
replicacin se realiza de manera asncrona, es posible que clientes que
consulten directamente a miembros secundarios no obtengan la
informacin ms reciente.
Tipos de miembros secundarios
Existen 3 configuraciones especiales para los miembros secundarios, esto nos
permitir delimitar su funcionamiento para un fin especfico.
Miembro de prioridad 0
Se le denomina a un miembro secundario configurado con prioridad 0, esto evitar
que dicho miembro pueda ser elegido a convertirse en miembro primario en caso
de que aquel falle.
Miembro escondido
Son miembros secundarios de tipo prioridad 0 pero que adems se les niega la
posibilidad de ser accesibles para lectura por parte de los sistemas cliente.
Miembro retrasado
Tambin son miembros de prioridad 0 y poseen la cualidad particular de mantener
un estado retrasado de la base de datos, suele utilizarse como instancias de
respaldo ya que no han sido afectadas por las ltimas operaciones que pudiesen
estar alterando de manera no deseada la informacin. Debido al estado retrasado
de este miembro se recomienda que tambin se defina como un miembro
escondido.

Elecciones
La arquitectura de los replica set dicta que los miembros deben enviar latidos
o pings entre ellos cada 2 segundos, si en un perodo de 10 segundos el latido no
es devuelto, se marca al miembro en cuestin como inaccesible.

Llamar a eleccin
Una eleccin se lleva a cabo cuando no existe un miembro primario, este deja de
responder o este es obligado a darse de baja, veamos las diferentes condiciones:
La iniciacin de un nuevo replica set.
Un miembro secundario pierde contacto con el primario.
El miembro primario es obligado a convertirse en secundario.
Si un secundario es elegible para elecciones y posee un mayor indice de
prioridad.
Prioridad
La eleccin de un nuevo miembro primario se basa inicial y principalmente en la
comparacin de prioridades de aquellos miembros elegibles, esta prioridad es por
defecto 1, esto para darles a todos los miembros la posibilidad de ser elegidos.
Si un miembro de mayor prioridad alcanza a tener al da su informacin con al
menos 10 segundos con respecto al oplog, se declara una eleccin para darle a
dicho nodo de mayor prioridad la oportunidad de convertirse en primario.
Recuerda que los miembros con prioridad 0 no pueden ser elegidos
como primarios.
Optime
Se toma en cuenta la estampilla de tiempo ms reciente de la ltima operacin que
el miembro aplic del oplog, por ello se llama optime o tiempo de operacin.
Conectividad
Otro aspecto a considerar es la capacidad que tiene el candidato para conectarse
con la mayora de de los miembros en el grupo.
En este caso particular se le llama miembros a aquellos que poseen la
capacidad para votar. Al configurar un miembro del replica set se puede
indicar si a este se le permite votar o no y cuantos votos posee cada uno.
Negaciones en elecciones
Cualquier miembro del replica set puede vetar o negar una eleccin (incluyendo
aquellos miembros que no votan) si ocurre alguna las siguientes situaciones:
Si el miembro que busca la eleccin no es parte del grupo de votantes.
Si el miembro que busca la eleccin no est al da con las operaciones ms
recientes en el oplog que son accesibles por el replica set.
Si el miembro que busca la eleccin posee una prioridad menor a algn otro
miembro que tambin sea elegible.
Si el miembro primario para el momento posee un optime mayor o igual que
aquel que busca la eleccin (desde el punto de vista del miembro votante).

Consideraciones
Existen varios aspectos a considerar cuando se desea implementar una estrategia
de replicacin, varias de ellas aplican a sistemas de bases de datos comunes.
Miembros impares
Procura que la cantidad de miembros en tu replica set sea impar, esto facilitar
los procesos de elecciones y solicitar menos recursos y menos tiempo.
Para estos casos es posible configurar un miembro rbitro. Este tipo de
miembros no poseen una copia del conjunto de datos y por ende no pueden
volverse primarios. Pueden ser de gran utilidad cuando no posees la
infraestructura para soportar un miembro ms pero deseas seguir el estndar de
miembros impares ya que la cantidad de recursos que necesita es mucho menor.
Nmero de miembros
Un replica set puede tener un mximo de 12 miembros y solo 7 de ellos con la
capacidad de votar. En caso de que tu solucin necesite un mayor nmero de
miembros es posible implementar la estrategia precedente a los replica
set conocida comoreplicacin maestro-esclavo. Se debe tomar en cuenta que esta
estrategia ofrece menos redundancia y no soporta el proceso automtico de
recuperacin a fallas como lo hacen los replica set con los latidos y elecciones.
Distribucin geogrfica
Procura subdividir los miembros de tu replica set en distintos centros de datos,
esto asegurar que si algo le sucede al centro de datos, tu base de datos se
mantendr activa. De igual manera trata de mantener una mayora en uno de los
centros para garantizar que los miembros puedan elegir a un primario en caso de
que la comunicacin entre otros miembros localizados en otro centro se vea
dificultado.
Inclusive puedes tener un miembro tipo prioridad 0 en un centro de datos aparte
como respaldo.
Balanceo de carga
Como mencionamos inicialmente, es posible configurar el replica set para
soportar la lectura de miembros secundarios, esto permitir balancear la carga en
ambientes que puedan encontrarse bajos altos niveles de estrs y concurrencia.

Conclusin
Esta semana hemos visto mucha teora de lo que amerita implementar una
estrategia de replicacin para nuestra base de datos en MongoDB, ciertamente es
mucho que digerir pero no te preocupes que la semana que viene llevaremos esto
a la prctica para que lo puedas ver con mayor facilidad. Sin embargo nos vemos
en la necesidad de tocar todos estos aspectos debido a que es parte clave de lo
ser tu ambiente de produccin final.


MongoDB desde Cero: Replicacin
Parte II

La semana pasada aprendimos la teora de replicacin en MongoDB mejor
conocido como replica set. Ahora estamos listos para tomar esos conocimientos y
llevarlos a la prctica.

Convertir a Replica Set
Uno de los casos de uso comunes al implementar estrategias de replicacin es la
de primero trabajar con una instancia independiente y luego convertirla a replica
set. Veamos lo que debemos hacer para llevar a cabo este sencillo proceso:
Primero debemos detener la instancia de mongod.
Luego debemos especificar el nombre del replica set que ser formada, para ello
podemos especificarlo en el archivo de configuracin como:
1 replSet = nombre_del_replica_set
O si lo prefieres puedes pasarlo como argumento al comando de ejecucin de la
instancia cuando no se ejecuta como un servicio:
1 mongod --replSet nombre_del_replica_set
Posteriormente al levantar la instancia de mongod, entraremos al la consola
de mongo:
1 $ mongo
2
3
4
5
6
7
8
9
10
11
...
> rs.status()
{
"startupStatus" : 3,
"info" : "run rs.initiate(...) if not yet done for the set",
"ok" : 0,
"errmsg" : "can't get local.system.replset config from self or any seed (EMPTYCONFIG)"
}
> rs.conf()
null
El comando rs.status() nos indica el estado actual del replica set, en este caso
podemos observar que en efecto a la instancia le ha sido indicada que debe
trabajar como un replica set; sin embargo esta no ha sido iniciada y por eso no
tiene asignada ninguna configuracin (accesible con el comando rs.config()).
Iniciemos el replica set ejecutando el comando rs.initiate():
1
2
3
4
5
6
7
> rs.initiate()
{
"info2" : "no configuration explicitly specified -- making one",
"me" : "Mordor.local:27017",
"info" : "Config now saved locally. Should come online in about a minute.",
"ok" : 1
}
Para este caso mongod crear una configuracin sencilla base para el replica set.
Vemos lo que esta iniciacin ha logrado:
1
2
3
4
5
6
7
8
miRS:PRIMARY> rs.status()
{
"set" : "miRS",
"date" : ISODate("2013-12-15T15:43:27Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
"name" : "Mordor.local:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 401,
"optime" : Timestamp(1387122082, 1),
"optimeDate" : ISODate("2013-12-15T15:41:22Z"),
"self" : true
}
],
"ok" : 1
}
miRS:PRIMARY> rs.conf()
{
"_id" : "miRS",
"version" : 1,
"members" : [
{
"_id" : 0,
"host" : "Mordor.local:27017"
}
]
}
Ahora la consola de mongo nos indica el miembro especifico sobre el cual estamos
ejecutando los comandos, en este caso sobre el primario (PRIMARY) del replica
set de nombre miRS.
Podemos notar tambin mucha informacin con respecto a los miembros
del replica set como la estampilla de tiempo de la ltima operacin realizada
(optime), su estado funcional, su funcin dentro del replica set, entre otros.

Agregar miembros
Ya hemos convertido una instancia independiente en un replica set; sin embargo
no nos sirve de nada una solucin de este tipo con un solo miembro, para
demostrar esto utilizar una instancia de Ubuntu con Vagrant, pero si lo deseas
tambin puedes utilizar otros equipos que tengas a la mano o incluso levantar
otras instancias de mongod en tu mismo equipo utilizando archivos de configuracin
diferentes o puertos distintos.
Lo primero que debemos hacer es obviamente tener instalado MongoDB en
nuestro equipo secundario.
Luego debemos asegurarnos de que el directorio de data de dicha instancia est
vaca ya que este miembro copiar toda la informacin del miembro primario.
Tambin puedes copiar manualmente la informacin del miembro
primario, esto reducir el tiempo de preparacin de este miembro
secundario.
Ahora en nuestro equipo secundario debemos indicarle el nombre del replica
set de la misma manera que lo hicimos con la primaria, indicandolo en su archivo
de configuracin o al levantar manualmente la instancia. (en este caso utilizamos
el nombre miRS)
1
2
3
4
5
6
vagrant@precise32:~$ sudo nano /etc/mongodb.conf

# mongodb.conf
...
# in replica set configuration, specify the name of the replica set
replSet = miRS
Reiniciamos la instancia:
1
2
3
4
5
6
7
8
9
vagrant@precise32:~$ sudo service mongodb restart
* Restarting database mongodb [ OK ]
vagrant@precise32:~$ mongo
MongoDB shell version: 2.4.8
...
> rs.status()
{
"startupStatus" : 3,
"info" : "run rs.initiate(...) if not yet done for the set",
10
11
12
"ok" : 0,
"errmsg" : "can't get local.system.replset config from self or any seed (EMPTYCONFIG)"
}
En este caso no ejecutaremos rs.inititate() ya que el replica set se encuentra
iniciado por otro lado y este miembro ser uno que agregaremos a este ya
existente.
Tomemos nota del host donde se encuentra esta instancia de mongod para poder
agregarla al replica set:
1
2
3
4
vagrant@precise32:~$ ifconfig
eth1 Link encap:Ethernet HWaddr **:**:**:**:**:**
inet addr:192.168.33.10 Bcast:192.168.33.255 Mask:255.255.255.0
...
Ahora volveremos a nuestra instancia primaria para agregar el nuevo miembro
al replica set:
1
2
miRS:PRIMARY> rs.add('192.168.33.10:27017')
{ "ok" : 1 }
Y si esperamos un poco a que MongoDB haga su magia podremos notar algo
como esto:
1
2
3
4
5
6
7
8
9
10
11
12
13
miRS:PRIMARY> rs.status()
{
"set" : "miRS",
"date" : ISODate("2013-12-15T17:40:32Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "Mordor.local:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 2010,
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
"optime" : Timestamp(1387129181, 1),
"optimeDate" : ISODate("2013-12-15T17:39:41Z"),
"self" : true
},
{
"_id" : 1,
"name" : "192.168.33.10:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 51,
"optime" : Timestamp(1387129181, 1),
"optimeDate" : ISODate("2013-12-15T17:39:41Z"),
"lastHeartbeat" : ISODate("2013-12-15T17:40:31Z"),
"lastHeartbeatRecv" : ISODate("2013-12-15T17:40:30Z"),
"pingMs" : 1,
"lastHeartbeatMessage" : "syncing to: Mordor.local:27017",
"syncingTo" : "Mordor.local:27017"
}
],
"ok" : 1
}
miRS:PRIMARY> rs.conf()
{
"_id" : "miRS",
"version" : 2,
"members" : [
{
"_id" : 0,
"host" : "Mordor.local:27017"
},
45
46
47
48
49
50
{
"_id" : 1,
"host" : "192.168.33.10:27017"
}
]
}
En mi caso tuve que colocar en el archivo /etc/hosts de mi equipo
secundario la asociacin del host Mordor.local a la IP de mi equipo
principal 192.168.0.100, ya que de lo contrario el nuevo miembro no
podra resolver ese nombre a nivel de DNS para lograr conectarse con el
miembro principal.
Agregar rbitro
Si quisiramos agregar un rbitro ejecutaramos en lugar de rs.add(..), el
comando rs.addArb(...). Recuerda que el directorio especificado para este
miembro donde se almacenara la data ser nicamente utilizado para almacenar
configuracin, NO el conjunto de datos, ya que los rbitros no poseen una copia
del conjunto de datos.

Configuracin de miembros
Como vimos la semana pasada existen varios tipos de miembros secundarios
adems de algunas consideraciones especiales que se pueden especificar para
los miembros del replica set, si recordamos bien, delimitar estas funcionalidades
se basan en una sencilla configuracin del miembro para el fin especifico.
Estas configuraciones deben realizarse desde el miembro primario.
Configurarlo es muy sencillo, hagamos uso de nuestros conocimientos de
Javascript para esto. Veamos el ltimo comando que ejecutamos:
1
2
3
4
5
miRS:PRIMARY> rs.conf()
{
"_id" : "miRS",
"version" : 2,
"members" : [
6
7
8
9
10
11
12
13
14
15
{
"_id" : 0,
"host" : "Mordor.local:27017"
},
{
"_id" : 1,
"host" : "192.168.33.10:27017"
}
]
}
Es aqu donde debemos definir la configuracin para cada miembro. Ser tan fcil
como asignarle dicho comando a una variable y empezaremos a manipular el
objeto como lo haramos normalmente en Javascript:
1 config = rs.conf()
Pongamos como ejemplo la configuracin de un miembro retrasado el cual
sabemos ahora que es un miembro de prioridad 0, que debe ser adems
un miembro escondido y posee un tiempo de retraso determinado, hagamos esto
con nuestro miembro secundario:
1
2
3
config.members[1].priority = 0
config.members[1].hidden = true
config.members[1].slaveDelay = 3600
Si quisieramos podramos definir tambin la cantidad de votos que puede
tener este miembro para determinar su influencia en elecciones con el
atributo votes.
Y ahora solo reconfiguramos el replica set de la siguiente manera :
1
2
3
4
5
6
7
rs.reconfig(config)
...
miRS:PRIMARY> rs.conf()
{
"_id" : "miRS",
"version" : 5,
"members" : [
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"_id" : 0,
"host" : "Mordor.local:27017"
},
{
"_id" : 1,
"host" : "192.168.33.10:27017",
"priority" : 0,
"slaveDelay" : 3600,
"hidden" : true
}
]
}
Muy bien ahora ya tenemos configurado un miembro retrasado en nuestro replica
set.
Tambin puedes configurar directo el miembro cuando lo ests agregando
al replica set especificando los parmetros directamente de la siguiente manera:
1 rs.add( { _id: 1, host: '192.168.33.10:27017', priority: 0, hidden: true, slaveDelay: 3600 } )

Eliminacin de miembros
Supongamos el caso que deseamos eliminar uno de los miembros del replica set.
Si dicho miembro es el primario debemos primero relevarlo de su cargo y dejar que
un nuevo primario sea elegido.
Para esto ejecutaramos el comando rs.stepDown(<cantidad_segundos>) en el
miembro primario, esto lo forzar a ceder su papel como primario y evitar ser
elegido en la siguiente eleccin durante la cantidad de segundos indicada.
Posteriormente podremos eliminar un miembro desde el primario de la siguiente
manera:
1 rs.remove('192.168.33.10:27017')
Si revisamos el estado y configuracin del replica set luego de esto, podremos ver
que en efecto esa instancia ya no forma parte de la misma.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
miRS:PRIMARY> rs.status()
{
"set" : "miRS",
"date" : ISODate("2013-12-15T19:57:23Z"),
"myState" : 1,
"members" : [
{
"_id" : 0,
"name" : "Mordor.local:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 10221,
"optime" : Timestamp(1387137431, 1),
"optimeDate" : ISODate("2013-12-15T19:57:11Z"),
"self" : true
}
],
"ok" : 1
}

miRS:PRIMARY> rs.conf()
{
"_id" : "miRS",
"version" : 6,
"members" : [
{
"_id" : 0,
"host" : "Mordor.local:27017"
}
]
32 }
De igual manera si accedemos a nuestro antiguo miembro podremos ver que se
encuentra con estado REMOVED:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
miRS:REMOVED> rs.status()
{
"set" : "miRS",
"date" : ISODate("2013-12-15T20:07:29Z"),
"myState" : 10,
"members" : [
{
"_id" : 1,
"name" : "192.168.33.10:27017",
"health" : 1,
"state" : 10,
"stateStr" : "REMOVED",
"uptime" : 8811,
"optime" : Timestamp(1387130876, 1),
"optimeDate" : ISODate("2013-12-15T18:07:56Z"),
"self" : true
}
],
"ok" : 1
}

Convertir miembro en independiente
Para utilizar este antiguo miembro secundario como una instancia aislada
nuevamente podemos volver a ejecutar el comando de inicio de la instacia sin el
parmetro --replSet o eliminarlo del archivo de configuracin (dependiendo de
cmo hayas decidido iniciar la instancia de mongod).
Luego reiniciemos la instancia y borraremos los rastros del replica set al borrar la
base de datos local donde se almacena la informacin de la misma:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vagrant@precise32:~$ sudo nano /etc/mongodb.conf
...
# mongodb.conf
...
# in replica set configuration, specify the name of the replica set
# replSet = miRS #eliminamos o comentamos esta linea
...

vagrant@precise32:~$ sudo service mongodb restart
* Restarting database mongodb [ OK ]
vagrant@precise32:~$ mongo
...
> use local
switched to db local
> db.dropDatabase()
{ "dropped" : "local", "ok" : 1 }

Conclusin
Ya sabemos como tener un cluster de replicacin en MongoDB, esto nos permitir
tener una alta disponibilidad de los datos y aseguraremos la durabilidad de los
mismos por el incremento de la redundancia. De igual manera estaremos
protegidos en caso que sucedan situaciones catastrficas inesperadas. Incluso
podras configurar en el driver de MongoDB de tu aplicacin cliente para que lea
de los miembros secundarios en caso de que sea necesario. Ms adelante
llevaremos el concepto de clusterizacin mucho ms lejos cuando hablemos
de fragmentacin. Hasta entonces.


MongoDB desde Cero: Fragmentacin
Parte I
En las entradas pasadas hemos iniciado a hablar de temas de clusterizacin, es
decir, poseer varias instancias para escalar nuestra solucin de base de datos. Sin
embargo podemos llevar nuestro concepto de cluster ms alla de lo que hemos
visto con los replica sets al repartir informacin entre diferentes instancias, por
ello esta semana hablaremos de la fragmentacin de datos en MongoDB.

Propsito
Si estas desarrollando un servicio que se va haciendo popular o los niveles de
acceso a base de datos son cada vez ms altos, empezars a notar que tu base
de datos est siendo martillada por el exceso de trfico y tu servidor est sufriendo
por los altos niveles de procesamiento continuo y te podras ver en la necesidad de
actualizar tu infraestructura para soportar la demanda.
Entra en juego la fragmentacin de datos, esta permite separar las colecciones por
conjuntos de documentos en diferentes instancias o fragmentos. Esta estrategia
te permite escalar tu base de datos horizontalmente al agregar ms equipos para
repartir la informacin en lugar de obligar a mejorar el que tienes.
La mayora de las veces resulta ms costoso tener un nico computador
de altas capacidades que varios de gama inferior.
Por lo tanto si tenemos una coleccin muy grande, digamos de 1TB por ejemplo,
resultara prudente particionarla en diferentes fragmentos, digamos 5, para que la
informacin de dicha coleccin pueda ser distribuida en 200GB entre cada uno de
ellos, esto a su vez distribuye la carga a nivel de procesamiento.
En MongoDB la unidad de base de datos que se fragmenta son las colecciones.
Por lo tanto una coleccin que sea declarada como fragmentada podra poseer
distintos documentos en los fragmentos del cluster.
Un nico documento nunca estar repartido entre fragmentos. Un
documento puede tener un tamao mximo de 16MB, en caso de
necesitar mayor tamao para un documento se necesitara implementar
la solucin de GridFS el cual separa el documento en varios trozos
ochunks.

Arquitectura
Un cluster de fragmentacin suele poseer una arquitectura como esta:

Como puedes notar existen 4 componentes claves de la arquitectura. Hablemos un
poco sobre cada uno de ellos:
Aplicacin y Driver
Las aplicaciones cuando necesitan comunicarse con la base de datos de
MongoDB lo hacen a traves de un driver, estos tienen implementados los mtodos
y protocolos necesarios para comunicarse correctamente con la base de datos
encapsulando la complejidad del proceso al desarrollador.
Fragmento
Un fragmento o shard es aquel que posee los datos fragmentados de las
colecciones que componen la base de datos como tal, este suele estar compuesto
por un replica set preferiblemente; sin embargo en ambientes de desarrollo podra
ser una nica instancia por fragmento.
Router
Debido a que las aplicaciones ven la base de datos como un todo, el router es el
encargado de recibir las peticiones y dirigir las operaciones necesarias al
fragmento o fragmentos correspondiente(s).
En ambientes de produccin es comn tener varios routers para
balancear la carga de peticiones de los clientes.
Servidores de configuracin
Este tipo de instancias se encargan de almacenar la metadata del cluster de
fragmentacin, es decir, qu rangos definen un trozo de una coleccin y qu
trozos se encuentran en qu fragmento. Esta informacin es almacenada en
cach por el router para lograr un ptimo tiempo de procesamiento.
En ambientes de produccin se deben tener 3 servidores de
configuracin ya que si solo se posee uno y este falla, el cluster puede
quedar inaccesible.

Escoger llave de fragmentacin
MongoDB separa una coleccin en los trozos correspondientes para repartir a los
diferentes fragmentos por medio de una llave de fragmentacin. Esta llave viene
siendo uno de los campos perteneciente a los documentos el cual debe poseer las
siguientes caractersticas:
Cardinalidad y Divisibilidad
Una llave de fragmentacin debe tener una alta cardinalidad para asegurar que los
documentos puedan ser efectivamente divididos en los distintos fragmentos, es
decir, suponiendo que escogemos una llave de fragmentacin que posee solo 3
valores posibles y tenemos 10 fragmentos, no podramos separar los documentos
en los 10 fragmentos al solo tener 3 valores posibles para separar. Mientras ms
valores posibles pueda tener la llave de fragmentacin ser ms fcil y eficiente la
separacin de los trozos en los fragmentos.
Incluso si solo tienes 3 fragmentos puedes correr el riesgo al no cumplir
la caracterstica que veremos a continuacin.
Aleatoriedad
Adicionalmente es muy importante que la llave de fragmentacin posea un alto
nivel de aleatoriedad, esto se debe a que si utilizamos una llave que siga un patrn
incremental como una fecha o un ID, traer como consecuencia que cuando
estemos insertando documentos, el mismo fragmento estar siendo utilizando
constantemente durante el rango de valores definido para l, esto sin duda
mantendr los datos separados ptimamente pero pondr siempre bajo estrs a un
fragmento en lapsos de tiempo mientras que los otros posiblemente queden con
muy poca actividad (a este comportamiento se le conoce como hotspotting).
Para casos donde los campos de tus documentos se ven limitados para
cumplir con estas condiciones, es posible tener una llave de
fragmentacin compuesta. Incluso es posible escoger un campo que siga
patrones incrementales y utilizarlo como llave de
fragmentacinhasheada, lo cual crear un hash del valor del campo y
esto lograr que tenga un alto nivel de aleatoriedad.
Adicionalmente debemos recalcar que una llave de fragmentacin siempre deber
poseer un ndice, de lo contrario el rendimiento del sistema no sera muy bueno y
se estara sacrificando a costas de poder escalar nuestro sistema. Normalmente si
dicho campo no posee un ndice, la tratar de agregar el fragmento
al cluster MongoDB te obligar a crearlo o ste lo crear por ti.

Conclusin
Al igual que cuando hablamos de replicacin, esto es un tema con mucha teora y
aspectos a considerar por lo que dejaremos la parte prctica para la semana
siguiente. Como estars notando esta estrategia de clusterizacin tiene mucho que
ofrecer y vers no es tan difcil de implementar aunque se deben tomar en
consideracin muchos aspectos y la arquitectura es un poco ms compleja de lo
que estamos acostumbrados, hasta la semana que viene.


MongoDB desde Cero: Fragmentacin
Parte II
La semana pasada comenzamos a hablar sobre la fragmentacin de datos en
MongoDB, vimos cmo nos ayuda a escalar nuestra solucin de almacenamiento y
sus diferentes ventajas. Adems conocimos gran parte de la materia terica que
esto implica. De seguro ests ansioso por poner todo ello en prctica, por eso est
semana nos ponemos en accin para aplicar lo aprendido y armaremos nuestro
propio cluster de fragmentacin.

Creando un cluster de
fragmentacin
Es hora de ponernos a trabajar para crear nuestro primer cluster de fragmentacin,
por razones de facilidad educativa crearemos todo el cluster en el mismo equipo,
para ello solo deberemos crear cada instancia en un puerto distinto.
Servidores de configuracin
Comencemos creando nuestros servidores de configuracin que segn vimos
deben ser 3:
1
2
3
4
$ mkdir configServer1
$ mkdir configServer2
$ mkdir configServer3

5
6
7
$ mongod --configsvr --dbpath configServer1 --port 27019 --fork
$ mongod --configsvr --dbpath configServer2 --port 27020 --fork
$ mongod --configsvr --dbpath configServer3 --port 27021 --fork
La opcin --fork ejecutar en el fondo a la instancia para que el
comando regrese al terminal en lugar de quedarse escuchando al
servidor.
Para crear nuestro router debemos pasarle como parmetro los hostnames de
cada servidor de configuracin, para ello entraremos a cualquiera de los que
acabamos de crear y tomaremos nota de l:
1
2
3
4
$ mongo --port 27019
...
configsvr> hostname()
Mordor.local
Ya que todas las instancias se encuentran en el mismo equipo,
los hostnames son todos iguales y lo nico que cambia son los puertos.
Routers
Bien, ahora crearemos nuestro enrutador. Estos a diferencia de todos los otros
tipos de componentes, son instanciasmongos en lugar de mongod. Le debemos
pasar una cadena de caracteres con las direcciones de los servidores de
configuracin:
1
$ mongos --configdb Mordor.local:27019,Mordor.local:27020,Mordor.local:27021 --port 27030 --fork --logpath
routerLog
En ambientes de produccin se recomienda que se tengan mltiples instancias
enrutadoras, esto evitar que se forme un cuello de botella a nivel de acceso de
las aplicaciones. Un buen nmero para tomar como referencia es uno por
fragmento, y distribuidos de manera acorde.
Fragmentos
Ahora debemos crear nuestras instancias fragmentos, en ambientes productivos
se recomienda ampliamente que cada fragmento sea un replica set pero para no
hacer esta entrada tan larga y posiblemente confusa utilizaremos una nica
instancia por fragmento:
1
2
3
$ mkdir shard1
$ mkdir shard2
$ mkdir shard3
4
5
6
7

$ mongod --shardsvr --dbpath shard1 --port 27040 --fork
$ mongod --shardsvr --dbpath shard2 --port 27041 --fork
$ mongod --shardsvr --dbpath shard3 --port 27042 --fork
Deberamos a estas alturas tener corriendo 7 procesos de MongoDB, siendo 3
servidores de configuracin, 1 router y 3 instancias fragmentos:
1
2
3
4
5
6
7
8
$ ps -ax | grep mongo | grep -v grep
1844 ?? 0:00.65 /usr/local/Cellar/mongodb/2.4.8/mongod --configsvr --dbpath configServer1 --port 27019 --
fork --config /usr/local/etc/mongod.conf
1887 ?? 0:00.62 /usr/local/Cellar/mongodb/2.4.8/mongod --configsvr --dbpath configServer2 --port 27020 --
fork --config /usr/local/etc/mongod.conf
1928 ?? 0:00.59 /usr/local/Cellar/mongodb/2.4.8/mongod --configsvr --dbpath configServer3 --port 27021 --
fork --config /usr/local/etc/mongod.conf
1944 ?? 0:00.14 mongos --configdb Mordor.local:27019,Mordor.local:27020,Mordor.local:27021 --port
27030 --fork --logpath routerLog
2002 ?? 0:00.14 /usr/local/Cellar/mongodb/2.4.8/mongod --shardsvr --dbpath shard1 --port 27040 --fork --
config /usr/local/etc/mongod.conf
2043 ?? 0:00.12 /usr/local/Cellar/mongodb/2.4.8/mongod --shardsvr --dbpath shard2 --port 27041 --fork --
config /usr/local/etc/mongod.conf
2084 ?? 0:00.12 /usr/local/Cellar/mongodb/2.4.8/mongod --shardsvr --dbpath shard3 --port 27042 --fork --
config /usr/local/etc/mongod.conf
Bien, ahora agreguemos los fragmentos al cluster, para ello debemos ingresar a la
instancia router y agregarlos de la siguiente manera:
1
2
3
4
5
6
7
8
9
10
11
$ mongo --port 27030
...
mongos> sh.addShard("Mordor.local:27040")
{ "shardAdded" : "shard0000", "ok" : 1 }
mongos> sh.addShard("Mordor.local:27041")
{ "shardAdded" : "shard0001", "ok" : 1 }
mongos> sh.addShard("Mordor.local:27042")
{ "shardAdded" : "shard0002", "ok" : 1 }
mongos> sh.status()
--- Sharding Status ---
sharding version: {
12
13
14
15
16
17
18
19
20
21
22
23
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("52dc95a944281854002ed8e7")
}
shards:
{ "_id" : "shard0000", "host" : "Mordor.local:27040" }
{ "_id" : "shard0001", "host" : "Mordor.local:27041" }
{ "_id" : "shard0002", "host" : "Mordor.local:27042" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
Habilitar fragmentacin
Perfecto tenemos nuestro cluster armado, solo nos falta activar la fragmentacin,
para ello en la misma instancia router la habilitaremos para la base de
datos codehero y fragmentaremos la coleccin pruebaFragmentacion por su
campo _idde manera hasheada lo cual nos permitir cumplir con las reglas de
eleccin de llaves de fragmentacin como vimos en la entrada pasada:
1
2
3
4
5
6
7
mongos> use codehero
switched to db codehero
mongos> sh.enableSharding("codehero")
{ "ok" : 1 }
mongos> db.pruebaFragmentacion.ensureIndex({ _id : "hashed" })
mongos> sh.shardCollection("codehero.pruebaFragmentacion", { "_id": "hashed" } )
{ "collectionsharded" : "codehero.pruebaFragmentacion", "ok" : 1 }
Demo
Muy bien ya tenemos nuestra coleccin fragmentada, ahora crearemos un montn
de documentos para ver como se distribuyen entre los fragmentos:
1 mongos> for(var i=0; i &lt; 100001; i++) db.pruebaFragmentacion.insert({})
Finalmente veamos como se encuentran distribuidos estos documentos en las
colecciones:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
mongos> db.pruebaFragmentacion.getShardDistribution()

Shard shard0000 at Mordor.local:27040
data : 687KiB docs : 29350 chunks : 1
estimated data per chunk : 687KiB
estimated docs per chunk : 29350

Shard shard0001 at Mordor.local:27041
data : 980KiB docs : 41839 chunks : 1
estimated data per chunk : 980KiB
estimated docs per chunk : 41839

Shard shard0002 at Mordor.local:27042
data : 675KiB docs : 28812 chunks : 1
estimated data per chunk : 675KiB
estimated docs per chunk : 28812

Totals
data : 2.28MiB docs : 100001 chunks : 3
Shard shard0000 contains 29.34% data, 29.34% docs in cluster, avg obj size on shard : 24B
Shard shard0001 contains 41.83% data, 41.83% docs in cluster, avg obj size on shard : 24B
Shard shard0002 contains 28.81% data, 28.81% docs in cluster, avg obj size on shard : 24B
Veremos que la informacin se ha distribuido bastante bien entre los distintos
fragmentos y que los datos no han sido todos asignados a uno solo, lo cual nos
indica que hemos escogido correctamente nuestra llave de fragmentacin y hemos
logrado obtener el escalamiento de base de datos horizontal que estamos
buscando.
Tambin podemos ver varios aspectos del cluster al ejecutar el
comando sh.status():
1
2
3
mongos> sh.status()
--- Sharding Status ---
sharding version: {
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("52d2f649c3d6590b6ddbb99b")
}
shards:
{ "_id" : "shard0000", "host" : "Mordor.local:27040" }
{ "_id" : "shard0001", "host" : "Mordor.local:27041" }
{ "_id" : "shard0002", "host" : "Mordor.local:27042" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
{ "_id" : "codehero", "partitioned" : true, "primary" : "shard0000" }
codehero.pruebaFragmentacion
shard key: { "_id" : "hashed" }
chunks:
shard0001 1
shard0002 1
shard0000 1
{ "_id" : { "$minKey" : 1 } } -->> { "_id" : NumberLong("-1492793005875893056") } on : shard0001
Timestamp(2, 0)
{ "_id" : NumberLong("-1492793005875893056") } -->> { "_id" :
NumberLong("3847987569150422320") } on : shard0002 Timestamp(3, 0)
{ "_id" : NumberLong("3847987569150422320") } -->> { "_id" : { "$maxKey" : 1 } } on : shard0000
Timestamp(3, 1)
Notars en la parte inferior los rangos que ha tomado cada fragmento sobre la
llave de fragmentacin _id hasheada para distribuir los documentos.
De igual manera podremos notar que exiten 3 chunks o trozos, de los cuales existe
uno en cada fragmento. Un chunk est delimitado por un rango definido por
MongoDB sobre la llave de fragmentacin, en este caso cada fragmento posee un
nicochunk, si este llegara a pasar los 64MB (o lo que se haya especificado en
configuraciones avanzadas) se realizar automaticamente una operacin
de separacin o splitting la cual dividir el trozo en 2 para lograr mantener un alto
nivel de rendimiento. Es posible tambin que si un fragmento comienza a tener
varios chunks en comparacin con sus hermanos, se ejecute una operacin
de migracin de chunks, este mover chunks en los extremos de su rango a otro
fragmento.
Es posible que si pruebas con menor cantidad de documentos no notes
que la informacin se separe en los diferentes fragmentos ni chunks, esto
se debe a que la informacin es todava muy pequea para que
MongoDB considere separarla, ya que como puedes ver hemos insertado
100.000 documentos y estos solo ocupan un tamao de 2.28MB debido a
la ausencia de complejidad en su estructura.

Conclusin
Hemos recorrido un largo camino, hemos llegado a uno de los temas ms
avanzados de MongoDB, cuando nos encontramos hablando de este tipo de temas
en porque nos interesa que una gran infraestructura de base de datos sea lo ms
escalable y mantenible posible, ciertamente es un tema enfocado ms a los DBAs
que a los desarrolladores pero es importante para ambos conocer las
implicaciones de estas situaciones ya que la cooperacin de ambos ayudar a
determinar un rendimiento ptimo, especialmente al determinar aspectos crticos
como la llave de fragmentacin.


MongoDB desde Cero: Agregacin
Parte I

Estamos llegando al final de la serie, hemos pasado por una gama amplia de
tpicos, desde lo ms bsico a los ms avanzado, tocando temas de desarrollo y
de administracin de la base de datos. Una funcionalidad muy interesante en
MongoDB es aquella que nos permitir transformar la data antes de sacarla de la
base de datos, a esto se le llama el Aggregation Framework o
simplemente Agregacin.

Propsito
El proceso de agregacin se define como una serie de operaciones a las cuales
se somete una coleccin para obtener un conjunto de resultados calculados,
formateados y/o filtrados de manera diferente a como se encuentran en los
documentos, en general con el objetivo de agrupar y/o calcular datos que residen
en los documentos de acuerdo a una necesidad particular.
Para aquellos que les suene familiar el concepto probablemente hayan trabajado
con el modelo de MapReduce en otras bases de datos, en efecto MongoDB
tambin soporta esta modalidad; sin embargo haremos nfasis en la
implementacin particular de MongoDB.

Tubera de agregacin
Este concepto es la modalidad de implementacin de agregacin que es parte del
ncleo de MongoDB a partir de la versin 2.2. Este se basa en someter a una
coleccin a un conjunto de operaciones o etapas las cuales irn convirtiendo un
conjunto de documentos pertenecientes a una coleccin hasta obtener un conjunto
de documentos con el resultado computado deseado.
Se le llama tubera ya que cada etapa ir modificando, moldeando y calculando la
estructura de los documentos para pasarlo a la etapa que le sigue. Ciertamente
podemos repetir las etapas segn sea necesario, no existe limitacin al respecto;
sin embargo si debemos tomar en cuenta las ventajas a nivel de rendimiento que
puede ofrecer el orden de las etapas ya que los procesos de agregacin son
tareas que pueden llegar a consumir altos niveles de recursos si no sabemos bien
lo que hacemos.

Etapas
Ciertamente suena un poco ambiguo en teora pero veamos en prctica como
podemos manipular los documentos de una coleccin usando la modalidad de
tubera de agregacin.
1
2
3
4
5
6
7
8
9
10
11
db.ordenes.aggregate([
{
$etapa1: {
...
}
},{
$etapa2: {
...
}
},...
])
Si prestamos atencin a la sintaxis notaremos que le estamos indicando a la
coleccin ordenes que debe someterse a un un proceso de agregacin
(aggregate) el cual consiste de varias (notemos los corchetes) etapas, cada una
de ellas est definida por un conjunto de opciones, campos y/o argumentos que
veremos a continuacin para cada tipo de etapa.
Para que nos sea ms fcil entender como se comporta cada una de estas etapas,
tomaremos como premisa que la coleccinordenes luce algo as:
1
2
3
4
5
6
7
{
_id: 1,
id_cliente: 10,
monto: 200,
modo_de_pago: 'efectivo',
articulos: ['harina', 'aceite', 'papel de bao']
}
Si deseas probar en vivo la funcionalidad puedes descargar el archivo
de ordenes.json del repositorio de esta entrada e importarlo a tu base de
datos para que puedas seguir en prctica lo que aqu veremos.
Ciertamente una coleccin que almacene datos como rdenes debera
tener ms campos y estar mejor estructurada pero para nuestra ejemplo
ser suficiente para lograr demostrar como funciona la agregacin.
Filtrar ($match)
La opcin de filtrar es bastante anloga a lo que ya hemos visto con los filtros
convencionales en las bsquedas, simplemente filtrar los documentos segn los
valores que indiquemos.
Si quisiramos obtener las rdenes que realiz el cliente de ID = 2 lo haramos
parecido a como estamos acostumbrados pero con la sintaxis de la tubera de
agregacin:
1
2
3
4
5
6
7
8
9
> db.ordenes.aggregate([
{
$match: {
id_cliente: 2
}
}
])

{
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
"result" : [
{
"_id" : 3,
"id_cliente" : 2,
"monto" : 220,
"modo_de_pago" : "efectivo",
"articulos" : [
"pasta",
"ketchup",
"papel de bao"
]
},
{
"_id" : 8,
"id_cliente" : 2,
"monto" : 89,
"modo_de_pago" : "efectivo",
"articulos" : [
"harina",
"aceite",
"papel de bao"
]
}
],
"ok" : 1
}
Agrupar ($group)
La agrupacin es quizs la etapa ms utilizada en el proceso de agregacin ya que
es la que permite agrupar y realizar clculos sobre los documentos. Esta suele ser
la etapa ms complicada de entender, as que trataremos de explicarla paso a
paso.
Ya que esta etapa tomar los documentos originales de la coleccin y los
convertir en una serie de nuevos documentos, debemos especificar como estar
compuesto este nuevo, es decir, los campos que contendr.
Digamos que queremos agrupar las ordenes por modo_de_pago y queremos que
los documentos finales tengan la cantidad de ordenes para cada modo de pago y
la suma de sus correspondientes montos. Por lo tanto dichos documentos
resultantes tendrn una estructura como esta:
1
2
3
4
5
{
_id: 'tarjeta',
cantidad_de_ordenes : ...,
monto_total: ...
}
Es obligatorio especificar un campo _id para estos nuevos documentos
ya que estos sern los valores a agrupar. Es posible tener agrupaciones
mltiples como por ejemplo agrupar pormodo_de_pago y por id_cliente lo
cual permitira sacar clculos para cada tipo de pago para cada cliente ya
que por cada cliente se obtendran una cantidad de documentos igual a
la cantidad de tipos de pago que este utiliz.
Bien, ahora te estarn surgiendo algunas preguntas.
Cmo puedo hacer para tomar los valores de los campos?
Ciertamente si analizamos los valores del campo modo_de_pago de la coleccin
notaremos que existen 2 valores posibles,tarjeta y efectivo, pero debemos
indicarle al proceso de agregacin que tome dichos valores de los campos. Los
valores de los campos se toman colocndole al mismo el smbolo prefijo $. Por lo
tanto para agrupar por modo_de_pago como dijimos anteriormente usaramos algo
as:
1
2
3
4
{
_id: "$modo_de_pago"
...
}
Y cmo hago para hacer los clculos?
Existen varios operadores para hacer clculos en esta etapa:
$addToSet
$push
$first
$last
$min
$max
$avg
$sum
Si has seguido la serie vers que los nombres te parecen conocidos y su
funcionamiento en el proceso de agregacin es bastante similar.
Los 2 primeros, $addToSet y $push permitirn crear un arreglo de los valores
correspondientes a los campos cuando estemos agrupando.
Con $first y $last, podrs tomar de dicho campo el primer o ltimo valor
encontrado. (Estas son utilizadas despus de la etapa de ordenamiento ya que de
lo contrario su resultado es impredecible).
Posteriormente $min y $max, podrs tomar el mnimo y mximo valor de dicho
campo.
Y por ltimo, los operadores $avg y $sum te permitirn sacar un promedio de los
valores de dicho campo y sumar su cantidad o su ocurrencia para cada
agrupacin.
Ahora volviendo a nuestro ejemplo y retomando los pasos que hemos visto,
queremos agrupar por modo_de_pago y obtener para cada uno el monto total y la
cantidad de ordenes. Para ello haremos los siguiente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> db.ordenes.aggregate([
{
$group: {
_id: "$modo_de_pago",
cantidad_de_ordenes: { $sum : 1 },
monto_total: { $sum : "$monto" }
}
}
])

{
"result" : [
{
"_id" : "tarjeta",
"cantidad_de_ordenes" : 11,
"monto_total" : 5154
},
{
19
20
21
22
23
24
25
"_id" : "efectivo",
"cantidad_de_ordenes" : 9,
"monto_total" : 2100
}
],
"ok" : 1
}

Conclusin
Hemos empezado a ver uno de los temas ms avanzados en MongoDB, este te
permite manipular los documentos para realizar clculos que son de gran utilidad
bajo circunstancias particulares. Aun faltan algunas etapas y mostraremos tambin
un ejemplo completo de todas las etapas trabajando juntas, no te lo pierdas la
semana que viene.


MongoDB desde Cero: Agregacin
Parte II

La semana pasada comenzamos a hablar del Aggregation Framework, iniciamos
viendo en qu consiste, las ventajas que ofrece y algunas de las etapas que
componen la tubera de agregacin, esta semana seguiremos viendo el resto de
las etapas y veremos como utilizarlas juntas en un ejercicio.

Etapas (continuacin)
Recuerda que puedes acceder al repositorio de esta entrada para obtener algunos
datos de prueba en el archivoordenes.json que te ayudarn a practicar y probar
en vivo lo que haremos aqu.
Proyectar ($project)
La etapa de proyeccin nos permite especificar qu campos estarn en el
documento resultante de esta etapa, a su vez tambin podemos renombrar el
campo de ser necesario:
1
2
3
4
5
> db.ordenes.aggregate([
{
$project : {
monto: 1,
cliente: "$id_cliente"
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
}
}
])

{
"result" : [
{
"_id" : 1,
"monto" : 200,
"cliente" : 10
},
{
"_id" : 2,
"monto" : 180,
"cliente" : 10
},...
],
"ok" : 1
}
Al asignarle al nombre de un campo el valor booleano 1 estaremos indicandole al
proceso de agregacin que queremos incluir este campo en el documento
resultante. En cuanto al campo id_cliente podremos ver que lo que hicimos fue
renombrarlo a cliente, esto puede ser muy util para trabajar de manera ms facil
los documentos en etapas siguientes de la tubera.
Probablemente te estars preguntando: Por qu el campo _id est presente si no
especifiqu que lo deseaba como resultado?.
El campo _id por defecto es incluido a menos que se especifique lo contrario
mediante una exclusin explcita _id : 0
Desenvolver ($unwind)
La etapa de desenvolvimiento permite tomar un campo de los documentos que sea
de tipo arreglo y generar un documento para cada valor del mismo. Esta etapa
suele combinarse con la de agrupacin cuando la finalidad es realizar algn
calculo que involucre a los valores de un campo tipo arreglo.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
> db.ordenes.aggregate([
{
$unwind : "$articulos"
}
])

{
"result" : [
{
"_id" : 1,
"id_cliente" : 10,
"monto" : 200,
"modo_de_pago" : "efectivo",
"articulos" : "harina"
},
{
"_id" : 1,
"id_cliente" : 10,
"monto" : 200,
"modo_de_pago" : "efectivo",
"articulos" : "arroz"
},
{
"_id" : 1,
"id_cliente" : 10,
"monto" : 200,
"modo_de_pago" : "efectivo",
"articulos" : "ketchup"
},...
],
"ok" : 1
32 }
Como mencionamos, el arreglo resultante de documentos contiene un documeto
para cada valor del arreglo. Veamos cmo es inicialmente ese primer documento
como referencia:
1
2
3
4
5
6
7
8
9
10
11
12
> db.ordenes.find({ _id : 1 }).pretty()
{
"_id" : 1,
"id_cliente" : 10,
"monto" : 200,
"modo_de_pago" : "efectivo",
"articulos" : [
"harina",
"arroz",
"ketchup"
]
}
Ordenar, limitar y saltar ($sort, $limit, $skip)
Estas etapas son quizs las ms intuitivas debido a la facilidad de uso y la similitud
de su funcionalidad a lo que hemos aprendido desde el inicio.
1
2
3
4
5
> db.ordenes.aggregate([
{
$sort: { monto: -1, _id: 1 }
}
])
La etapa de ordenamiento recibir como parmetro un documento indicando con
qu campos se debe ordenar y en que sentido, siendo 1 ascendente y -1
descendente. En este caso se ordenar primero descendentemente por monto y de
haber 2 montos iguales se ordenarn los involucrados de manera ascendente por
su campo _id. Veamos su comportamiento en la seccin inferior de resultados:
1
2
...{
"_id" : 10,
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
"id_cliente" : 1,
"monto" : 202,
"modo_de_pago" : "efectivo",
"articulos" : [
"harina",
"aceite",
"papel de bao"
]
},
{
"_id" : 1,
"id_cliente" : 10,
"monto" : 200,
"modo_de_pago" : "efectivo",
"articulos" : [
"harina",
"arroz",
"ketchup"
]
},
{
"_id" : 4,
"id_cliente" : 10,
"monto" : 200,
"modo_de_pago" : "efectivo",
"articulos" : [
"carne",
"aceite",
"galletas"
]
},
34
35
36
37
38
39
40
41
42
43
44
{
"_id" : 15,
"id_cliente" : 7,
"monto" : 183,
"modo_de_pago" : "efectivo",
"articulos" : [
"pasta",
"aceite",
"papel de bao"
]
},...
De igual manera podemos lograr algo como lo que conocemos desde antes con la
limitacin y salto de registros; sin embargo debemos tomar en cuenta que
saltar y/o limitar una serie de documentos que no hemos ordenamos primero
tendr resultados impredecibles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> db.ordenes.aggregate([
{
$sort: { monto: -1, _id: 1 }
},
{
$skip: 7
},
{
$limit: 2
}
])

{
"result" : [
{
"_id" : 9,
"id_cliente" : 10,
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
"monto" : 311,
"modo_de_pago" : "tarjeta",
"articulos" : [
"harina",
"ketchup",
"pollo"
]
},
{
"_id" : 6,
"id_cliente" : 3,
"monto" : 302,
"modo_de_pago" : "tarjeta",
"articulos" : [
"harina",
"pasta",
"papel de bao"
]
}
],
"ok" : 1
}

Ejercicio completo
Bien, ahora que conocemos como funciona cada una de las etapas podemos
proceder a construir nuestra tubera de agregacin con todas las etapas a ver si
entendimos correctamente de qu se trata. Veamos primero el comando y luego
explicaremos paso a paso lo que sucede.
1 > db.ordenes.aggregate([
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
$match: {
monto : { $gt: 200 }
}
},
{
$unwind : "$articulos"
},
{
$group: {
_id: "$articulos",
monto_promedio: { $avg: "$monto" },
cantidad_ordenes: { $sum: 1 },
compradores: { $addToSet: "$id_cliente" }
}
},
{
$sort: { monto_promedio: -1, cantidad_ordenes: -1}
},
{
$skip: 3
},
{
$limit: 2
}
])
Antes de adelantarte a la respuesta tratemos de analizar lo que hemos hecho:
Filtrar
En la primera etapa de la tubera obtuvimos las ordenes que tuviesen un monto
mayor a 200.
Desenvolver
Luego desenvolvimos el arreglo de articulos para poder hacer clculos con ellos.
Agrupar
Posteriormente agrupamos los documentos por articulo Sacamos un promedio de
su monto. Contamos cuantas ordenes existan para dicho artculo. Y qu clientes
habin comprado dichos articulos.
Ordenar
Luego de agrupar procedimos a ordenar nuestro conjunto de documentos por
monto promedio y por cantidad de ordenes de manera descendente.
Saltar y Limitar
Finalmente saltamos los 3 primeros documentos y limitamos el resto del resultado
a solo 2 documentos.
Desgloce
Ciertamente no fue necesario el uso de la etapa de proyeccin, esto es comn
especialmente cuando utilizamos agrupaciones ya que esta ltima se suele
encargar de realizar las tareas que se podran realizar al proyectar.
Ahora que conocemos lo que hicimos paso a paso podemos llegar a la conclusin
de cual podra haber sido el enunciado de un ejercicio como este:
Encuentre el 4to y 5to artculo de mayor monto (tomando en cuenta que
el monto vara segun el momento de la compra), indicando los
compradores involucrados y cantidad de ordenes realizadas.
1
2
3
4
5
6
7
8
9
10
{
"result" : [
{
"_id" : "carne",
"monto_promedio" : 555.6666666666666,
"cantidad_ordenes" : 3,
"compradores" : [
3,
10
]
11
12
13
14
15
16
17
18
19
20
21
22
},
{
"_id" : "galletas",
"monto_promedio" : 542,
"cantidad_ordenes" : 1,
"compradores" : [
3
]
}
],
"ok" : 1
}

Conclusin
Con lo que hemos visto en los ltimos captulos de la serie podemos realizar
tareas avanzadas de clculos de datos del lado de la base de datos, esto evitar
que tu aplicacin tenga que realizar varias bsquedas e implementar la lgica para
calculo mediante mltiples ciclos y validaciones. Recuerda que las operaciones de
agregacin pueden realizarse de manera ms rpida si haces uso de los ndices,
no dudes en comentarnos tus dificultades en este tema ya que suele tornarse un
tanto complejo.


MongoDB desde Cero: Produccin

Luego de pasar por un espectro de temas de esta solucin de base de datos
NoSQL hemos llegado al final de la serie. Pasamos desde lo ms sencillo
aprendiendo qu es MongoDB, de qu esta compuesto y como se instala a las
tareas ms avanzadas de manipulacin de datos. Para culminar la serie es vital
mencionar varios aspectos que se deben considerar al usar MongoDB en un
ambiente de produccin.

Seguridad
Debemos siempre proteger nuestras instancias de base de datos y la informacin
que estas contienen, por ello es altamente recomendado establecer los usuarios
con sus respectivos niveles de acceso a las instancias, esto evitar que cualquier
individuo con o sin acceso a las mismas pueda realizar operaciones que no
debera estar haciendo.
No utilices el puerto estndar de las instancias (27017 para mongod por ejemplo), el
conocimiento por agentes externos de donde se alojan tus servicios es el primer
paso que puede desatar un ataque.
Protege los accesos, dentro de lo posible trata de limitar por medio de reglas
de firewall el acceso para que solo las aplicaciones que deben comunicarse con la
base de datos sean las autorizadas a establecer una conexin con el servidor.
Para ms detalles puede volver a echar un vistazo a la entrada de Seguridad.

Infraestructura
Siempre utiliza sistemas operativos de 64bit. Los paquetes de 32bit de MongoDB
solo pueden almacenar 2GB de datos, estos son ideales para ambientes de
prueba y aprender pero no para el despliegue de la base de datos final.
Si ests buscando maximizar el rendimiento de entrada y salida de la base de
datos se recomienda invertir en memoria RAM y discos de estado slido (SSD),
incrementar el poder de procesamiento al agregar ms ncleos de CPU o
actualizar a uno ms potente puede ayudar pero los cambios no son tan
significativos.
Siempre habilita memoria swap en sistemas Linux, esto evitar errores de escasez
de memoria que pueda matar algn proceso de Mongo.
Trate de utilizar almacenamiento local en lugar de sistemas de archivos remotos,
esto aplica en general para varios sistemas de base de datos, no solo MongoDB.
En caso de utilizarlos, opta por servicios de protocolo iSCSI y no NFS, ya que este
ltimo puede causar mltiples escenarios de errores, incompatibilidades y
degradacin en el rendimiento. Algunos ejemplos de esto seran el EBS de
Amazon y unidades locales montadas como sistemas de archivos para mquinas
virtuales.
En ambientes Linux que posean NUMA (Acceso de memoria no uniforme) se debe
desactivar este comportamiento para MongoDB para evitar mltiples escenarios de
problemas y degradacin en el rendimiento. Esto tambin aplica para otras bases
de datos como MySQL.

Disponibilidad y Rendimiento
Se recomienda ampliamente utilizar replica sets, esto ayudar a mantener la base
de datos siempre disponible sin importar si alguno de sus nodos falla.
Siempre debes tener un nmero impar de miembros, preferiblemente repartidos
en datacenters separados ya que cuando un proveedor de servicios falla suele
fallar por datacenter completo.
Recuerda que puedes utilizar los tipos especiales de miembros secundarios para
tareas especiales como reportes y respaldos, de esta manera no se estar
generando una carga adicional sobre los miembros principales.
Si tu aplicacin que se comunica con la base de datos tiene un nivel muy elevado
de lecturas puedes habilitar la lectura a miembros secundarios, esto permitir
balancear la carga para que el primario no se someta a tanto estrs.
Para manejo de volmenes de datos muy grandes considera
utilizar fragmentacin esto te permitir escalar tu infraestructura para soportar ms
datos manteniendo un alto nivel de rendimiento.
Nunca te olvides de construir los ndices necesarios para los tipos de bsquedas
ms frecuentes para incrementar la velocidad de las operaciones.

Prevencin
Siempre respalda con frecuencia la informacin de tu base de datos. Esto es
imperativo para cualquier solucin de base de datos y debe ser tomada muy en
serio ya que nadie quiere perder informacin valiosa que pueda comprometer la
aplicacin que esta soporta.
Mantn monitoreado tu sistema, de esta manera puedes detectar degradacin en
el rendimiento y estar al tanto de fallas que puedan ocurrir.
Para ambos casos los compaeros de 10gen (compaa detrs del desarrollo de
MongoDB) nos ofrecen una herramienta tipo cloud llamada MMS (Servicio de
administracin de MongoDB), esta monitorear de manera gratuita todas tus
instancias de base de datos y te avisar si alguna falla, y por una cuota mensual
tambin va respaldando los datos de tu base de datos de manera frecuente.
Para monitorear tambin puede utilizar Munin con el plugin de MongoDB.

Alternativas
Si prefieres que alguien ms se encargue de la carga pesada administrativa de la
base de datos puedes utilizar servicioscloud como MongoHQ o MongoLab, de esta
manera se tercerizan las tareas ms pesadas como la fragmentacin, replicacin,
respaldos y monitoreo. Incluso tienen planes gratis para que puedas ir
desarrollando sobre ellos y determines si es lo que deseas.

Conclusin
Bueno, han sido unos meses muy interesantes y productivos desde que
empezamos con MongoDB aqu en CODEHERO, espero que lo hayan
aprovechado y disfrutado, ciertamente yo lo hice y aprend junto con ustedes. Hay
varios detalles que no tocamos pero creo que hemos cubierto lo suficiente como
para enfrentarnos a la mayora de las situaciones que se nos pueden presentar.
Fue un placer guiarlos a travs de este camino del NoSQL, y siempre pueden
hacernos saber sus dudas, inquietudes y comentarios.

Вам также может понравиться