Vous êtes sur la page 1sur 32

MongoDB en Castellano

josebyte
Este libro está a la venta en http://leanpub.com/mongodbcastellano

Esta versión se publicó en 2016-01-31

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.

This work is licensed under a Creative Commons Attribution-NonCommercial 3.0 Unported


License
Índice general

Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Introducción a MongoDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Un poco de historia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
¿Qué es MongoDB? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Documentos JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
BSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

Mongo Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

CRUD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Insert . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
update . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
save . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
find y findOne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
remove . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
findAndModify . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

Operadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Operadores especiales: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

Modelado de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

Índices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Consideraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Ver indices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Funcionamiento de los índices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Crear indices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
mongotop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
mongostat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

Agregation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

ReplicaSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
ÍNDICE GENERAL

Sharding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

DBA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Store engine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27

Bibliografía . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Agradecimientos
Me llamo Joseba Madrigal¹, soy graduado en informática además de una persona curiosa y analítica,
me encanta descubrir nuevas tecnologías y ayudar a las personas de mi entorno, por ello he dedicado
un par de meses a escribir este libro que espero ayude a la gente a profesionalizarse en MongoDB.
El libro está escrito para programadores con interés en utilizar MongoDB a un nivel profesional.
El motivo de escribir este libro es que cuando intente realizar el salto del mundo SQL, en el cual
me encontraba muy cómodo, no encontré ninguna documentación en Español y aunque todo buen
desarrollador debe tener buen nivel de inglés, siempre es más cómodo leer en la lengua materna.
Había pasado muchos años trabajando con Oracle PL-SQL y más de los que me gustaría trabajando
con MYSQL hasta que entre en “Tapquo S.L.” y conocí más de cerca el mundo no-sql. Fue entonces
cuando me apunté a los cursos de MongoDB-University y comencé a apreciar las virtudes de este
mundo no-sql tan desconocido para mí. En apenas 3 meses había logrado la certificación oficial
de MongoDb tanto de sus cursos como en su examen, por lo que me di cuenta de que la curva de
aprendizaje es realmente rápida y buena, por consiguiente quería hacerla aún mejor para todos los
hispanohablantes que quieran profesionalizarse en MongoDB.
Escribí este libro en mis ratos libres, es difícil encontrar un rato libre cuando tienes una vida con
un trabajo a jornada completa, impartes clases después de la jornada de trabajo, estudias, tienes
familia, novia y amigos. Estas líneas no hubieran sido posibles sin el apoyo incondicional de todo mi
entorno, desde aquí me gustaría agradecer a todos su comprensión y sus ánimos. A toda mi familia,
pero en especial a mi hermano Aitor, por ayudarme a mejorar mi inglés, apoyarme, animarme y
además hacer la revisión final del libro. A mi novia Katy agredezco su paciencia, aguantarme en los
buenos y los malos momentos y por permitirme ponerme en el ordenador para escribir pese a que
no le guste verme delante del PC. A mis amigos les agradezco que me ayuden a desconectar, lo cual
es imprescindible de vez en cuando. A todos ellos les estaré eternamente agradecido por apoyarme
siempre en todas mis decisiones.
Y como no, gracias a ti por descargar este libro, espero que sea de tu agrado.
¹http://www.josebamadrigal.com

1
Introducción a MongoDB
Un poco de historia
Actualmente se escucha hablar de BigData, pero antes de comenzar hablando de grandes cantidades
de datos es mejor empezar por el origen, ¿cuál es el motivo de la existencia de bases de datos para
Big Data?
Desde el origen de la informática ha existido la necesidad de almacenar información de forma
permanente. Con el término permanente se hace alusión a datos almacenados en un medio físico
como un disco duro ,(la memoria RAM no es almacenamiento permanente), la cual puede ser leída
posteriormente incluso despues de apagar y encender el equipo. Inicialmente se guardaban los datos
en ficheros de texto, pero pronto surgieron limitaciones, es un método poco eficaz, ¿qué ocurre si
cambiamos la estructura de los datos? ¿y si cambiamos algunos datos? ¿inconsistencias?… Por ello
surgieron las bases de datos históricas como las jerárquicas, en red o transaccionales. Estas bases
de datos se siguen estudiando actualmente en las universidades pero ya no son utilizadas debido a
sus limitaciones: Complejidad, duplicidad de registros, Integridad referencial, (No existe garantía de
que un registro hijo esté relacionado con un registro padre válido), Desnormalización…
Debido a esas limitaciones surgieron las bases de datos que se utilizan actualmente. Existen tres tipos
de bases de datos: - Bases de datos relacionales: Hasta la actualidad son las más usadas, utilizan
SQL. Por ejemplo: Oracle, MS SQL Server, MySQL o MariaDB son bases de datos relacionales. -
Bases de datos orientadas a objetos: Se han diseñado para trabajar bien en conjunción con lenguajes
de programación orientados a objetos como Java, C#, Visual Basic.NET y C++. (No están muy
extendidas en la actualidad.) - Bases de datos no-relacionales: No utilizan lenguaje SQL y están
orientadas a la escalabilidad, por ejemplo MongoDB.

¿Qué es MongoDB?
MongoDB es una base de datos no-relacional de documentos JSON y de código abierto escrito en
C++, que proporciona alto rendimiento, alta disponibilidad y escalabilidad automática.
El concepto de escalabilidad está totalmente ligado con “Big data” debido a que cuando se trabaja
con grandes cantidades de datos, en otros tipos de bases de datos es complejo y costoso de gestionar.
Las bases de datos como MongoDb proporcionan escalabilidad automática, lo cual hace que esta
base de datos sea idonea para grandes cantidades crecientes de información.
El desarrollo de MongoDB comenzó en el año 2007 por la empresa 10gen3, publicando una versión
final en el 2009 y actualmente se encuentra en la versión 3.0.
Por lo tanto hemos mencionado que MongoDB es una base de datos escalable, no-relacional y orien-
tada a documentos JSON(BSON), pero ¿qué significa no-relacional? y ¿documentos JSON(BSON)?

2
Introducción a MongoDB 3

No-relacional
No-relacional es un término nuevo que se aplica a todas las bases de datos que no son relacionales.
Este tipo de base de datos también son conocidas como “no-SQL” o “Big Data”. MongoDB tiene las
siguientes características no-relacionales: - No-SQL. - No tiene JOINs. - No tiene esquema. - Escala
horizontalmente. - No tiene transacciones atómicas para multiples documentos.

No-SQL
MongoDB no usa SQL como lenguaje de consultas, en su lugar dispone de una serie de métodos en
JavaScript que permiten realizar consultas y operaciones.

No tiene JOINs
En MongoDB no se puede hacer una relación entre dos colecciones, no existen las “join” del mundo
SQL. La razón es que MongoDB está orientado a ser escalable y las join de las bases de datos
relacionales escalan muy mal.

Sin esquema
Una misma colección (en sql: “colección”==”tabla”) de documentos puede contener documentos con
distinto formato, a esto se le llama “schemeless” o “sin esquema”. Es decir podremos tener ,por
ejemplo, una colección de usuarios en donde guardemos datos diferentes para cada usuario. A esa
capacidad le llamamos polimorfismo.

Escalabilidad horizontal
Uno de los motivos más importantes por el que se han creado las bases de datos no-relacionales
es por la escalabilidad. Actualmente cada vez más nos encontramos con gran cantidad de datos
en diferentes servidores. Las bases de datos relacionales son lentas y poco escalables en esos
casos, no están pensadas para que la información almacenada sea muy grande y se encuentre
distribuida, por ello ha surgido MongoDB. Este sistema de base de datos tiene la capacidad de escalar
horizontalmente correcta y fácilmente sin penalización. Un sistema escala horizontalmente bien si
al agregar más nodos al mismo, el rendimiento de éste mejora. Quizá ahora entiendas un poco mejor
el término que comentamos anteriormente que se usa para estas bases de datos, están diseñadas para
grandes cantidades de información, por ese motivo son conocidas también como bases de datos de
“Big Data”.
Introducción a MongoDB 4

Sin transacciones multi-documento


MongoDB solo garantiza atomicidad en el propio documento. ¿Que es la atomicidad? Según
Wikipedia: “La atomicidad es la propiedad que asegura que una operación se ha realizado o no,
y por lo tanto ante un fallo del sistema no puede quedar a medias”.
Imaginemos que estamos desarrollando la aplicación de un banco, dispondremos de diferentes
cuentas para cada usuario y existirán transferencias de dinero de una cuenta a otra. Las bases de datos
relacionales aseguran que si solicitamos restar de una cuenta y añadir a otra ésta sea una operación
atómica que no puede quedar a medias, o se hacen las dos operaciones(resta de una cuenta y suma
a la otra) o no se hace ninguna.
En la realidad, las bases de datos no se encuentran en un mismo sistema por lo que en las bases de
datos relacionales también es necesario controlar la atomicidad si llevamos este caso a la vida real.
En MongoDB solo las operaciones sobre un documento son atómicas, por lo que hay que tomar
medidas en el código por ejemplo mediante la creación de un “semáforo” para resolver este tipo de
problemas.

Documentos JSON
MongoDB está orientada a documentos en “JavaScript Object Notation” (JSON), por lo que
almacenamos documentos en lugar de “registros” o “filas”.
Un ejemplo de un documento JSON puede ser:

1 {"name": "Joseba", "surname": "Madrigal"}

Un documento JSON es un documento clave-valor. En el ejemplo anterior la clave “name” contiene


el valor “Joseba” y la clave “surname” contiene el valor “Madrigal”.
Un ejemplo más complejo de un JSON:

1 {
2 "name" : "Joseba",
3 "surname" : "Madrigal",
4 "age" : 28,
5 "skills" : ["javascript", "MongoDB", "jquery", "angularjs", "php"],
6 "address" : {
7 "street_address": "23 Elm Drive" ,
8 "city": "Palo Alto",
9 "state": "California",
10 "zipcode": "94305"
11 }
12 }
Introducción a MongoDB 5

Más adelante comentaremos los tipos de datos de JSON y veremos que internamente se almacenan
BSONs. Existe un límite de 16MB por documento en MongoDB. En 16MB cabe mucha información
y si tuviésemos que introducir más podríamos partir esa información en varias colecciones. Para
usar documentos más grandes MongoDB proporciona GridFS.
JSON es un estándar y puedes saber más en JSON.org²

BSON
Pese a que nosotros a priori en MongoDB usemos documentos en formato JSON, MongoDB trabaja
internamente con BSON (JSON Binario). Un BSON no es más que la representación binaria de un
documento JSON.
Puedes ver más información en BSONspec³. BSON extiende del modelo JSON para proporcionar
“tipos de datos” para una correcta codificacion y decodificación en los diferentes lenguajes. Por lo
tanto BSON proporciona a MongoDB dos capacidades: 1. Rápida escaneabilidad 2. Tipos de datos
JSON proporciona únicamente 6 tipos de dato: - String - Number - booleans - null - arrays - objetos
/ documentos
Los tipos de datos que maneja internamente BSON son los siguientes: - Double - String - Object -
Array - Binary data - Undefined (Deprecated) - Object id - Boolean - Date - Null - Regular Expression
- JavaScript Symbol JavaScript (with scope) - 32-bit integer - Timestamp - 64-bit integer - Min key
- Max key
Podemos ver BSON como un formato serializado de JSON.

Esquema BSON

²http://json.org
³http://bsonspec.org
Introducción a MongoDB 6

La figura superior muestra el proceso de conversión JSON-BSON, en MongoDB los datos se


almacenan en BSON y se envían en dicho formato, el encargado de realizar la conversión BSON<-
>JSON es el driver de Mongo de la plataforma cliente correspondiente.
En el proceso de consulta de un documento el servidor de MongoDB nos sirve un BSON y nuestra
APP a través del driver de Mongodb transforma el BSON a JSON. Cuando enviamos información de
nuestra APP al servidor enviamos un JSON y el driver es el encargado de convertirlo a BSON para
su envío. Todo este proceso se realiza de forma transparente.
Los nombres de los campos tienen las siguientes limitaciones: - El nombre de campo _id está
reservado para la primary key; debe ser único e inmutable, y puede ser de cualquier tipo excepto
array. - Los nombres no pueden empezar por el signo dólar ($). - Los nombres no pueden contener
punto (.). - Los nombres no pueden contener el caracter null.
Mongo Shell
En la carpeta donde tenemos instalado MongoDB hay una carpeta “bin”, en ella encontraremos todos
los ejecutables de MongoDB. Los más usados son: - mongod: es el demonio de base de datos, debemos
ejecutarlo y dejarlo en ejecución para poder lanzar consultas y trabajar. - mongo: es el shell desde
el cual lanzaremos las consultas. - mongos: es un servicio de routing de MongoDB Shard.
Por lo que si no tienes ya iniciado mongod lo primero es iniciarlo con: mongod
Ahora vamos a empezar a lanzar consultas contra nuestro mongod, para ello accedemos a nuestro
shell escribiendo: mongo
Por defecto intentaremos conectarnos al mongod de localhost en el puerto 27017. Si deseamos
conectarnos a otro demonio de MongoDB debemos especificar el puerto y el host mediante: –port y
–host.
Mongo shell es parte de MongoDB y proporciona un entorno completo de Javascript además de una
interfaz para MongoDB.
Desde el shell estos son los comandos más básicos y utilizados, no hace falta que los probeis, de
momento esta lista es para que os vayan sonando ya que los iremos utilizando a lo largo del libro:
Mostrar la base de datos actual: db
Muestra la lista de bases de datos: show dbs
Seleccionaremos la base de datos “midb”: use midb
Si realizamos una búsqueda en una colección de la base de datos elegida: db.usuarios.find()
El shell de MongoDB devolverá un cursor con los primeros 20 resultados, si queremos mostrar más
usaremos la palabra “it” para iterar el cursor.
Un cursor se cerrará pasados 10 minutos o cuando iteramos todos sus resultados. Podemos modificar
el tiempo que permanecerá abierto el cursor. Podemos establecer el tiempo máximo del cursor con
el siguiente comando: var micursor = db.usuarios.find() micursor.maxTimeMS(1200000)
El comando anterior ampliará el tiempo máximo de “micursor” a 20 mininutos (el parametro que
recibe es milisegundos, 1200000==20min), aunque si es iterado completamente se cerrará antes de
ese tiempo máximo.
Para imprimir un documento JSON de forma más “bonita” tenemos las siguientes opciones: - .pretty()
Al final de una consulta - printjson() == print(tojson()) Para imprimir variables.
Si queremos almacenar un array en una variable: var users = db.usuarios.find() Para ob-
tener el indice 4 o la posición 4 del cursor almacenado en la variable users creada en la linea
anterior: printjson( users [ 4 ] ) Mongo devolverá el usuario en esa posición: { "_id" :
ObjectId("51a7dc7b2cacf40b79990bea"), "nombre" : "Joseba", "surname" : "Madrigal" }

7
Mongo Shell 8

Cuando accedes a un cursor por un índice, internamente mongo llama a “cursor.toArray()” para
convertir el cursor en un array donde poder manejar índices y almacena TODOS los documentos
que devuelve el cursor en la memoria RAM. Para resultados muy grandes de documentos es posible
que mongo devuelva: “out of available memory” ya que no cabe todo en la RAM.
CRUD
Las operaciones CRUD (Create, Read, Update, Delete) existen en Mongodb en forma de funciones.
No se puede usar SQL, se utilizan las funciones o métodos que nos proporciona MongoDB. - C(create)
-> insert, save, update(upsert) - R(read) -> find, findOne - U(update) -> update, save - D(delete) ->
remove

Insert
1 db.collection.insert(
2 <document or array of documents>,
3 {
4 writeConcern: <document>,
5 ordered: <boolean>
6 }
7 )

Para insertar un documento en MongoDb utilizaremos la función insert sobre la colección de una
base de datos. Si la colección no existe, la creará automáticamente. Por ejemplo:

1 var doc = {"name": "Joseba", "surname": "Madrigal", "website": "http://josebamad\


2 rigal.com"};
3 db.people.insert( doc );

• Primero generamos una variable doc e introducimos un objeto en formato JSON.


• db.people representa la colección de datos donde insertaremos el documento. Puede no existir,
en caso de que no exista se creará automáticamente.
• No hemos añadido parámetros writeConcern ni ordered en esta ocasión, los comentaremos
más adelante ya que esta es nuestra primera inserción en MongoDB.

¡Felicidades, ya has insertado tu primer documento en la base de datos!


Si hacemos una búsqueda de todos los documentos que hay en la colección people:

1 db.people.find();

9
CRUD 10

Observaremos que además de nuestras claves y valores MongoDB ha insertado automáticamente


una clave “_id” con un Object ID con una estructura similar a: ObjectID(“507f191e810c19729de860ea”).
Eso ocurre porque automáticamente MongoDB añadirá a nuestros documentos una clave primaria
única e inmutable (no se puede cambiar una vez insertada) llamada _id. El _id por lo tanto será único
dentro de cada colección de documentos, además será inmutable por lo que podríamos localizar
inequívocamente un documento dentro de una colección por su _id. La única forma de “cambiar”
un _id a un documento es borrándolo y volviéndolo a insertar, el _id generado automáticamente
será diferente.
Si deseamos crear nuestra propia clave única podemos insertar nosotros un _id por ejemplo:

1 var doc = {"\_id": "mjoseba@gmail.com", "name": "Joseba", "surname": "Madrigal"};


2 db.people.insert( doc );

El _id de este documento será “mjoseba@gmail.com”. He elegido una cuenta de correo ya que es
una clave primaria única e inmutable para la mayoría de aplicaciones ,(es muy raro que se cambie
el email), normalmente se suele utilizar como nombre de usuario para el login. Podríamos utilizar
el dni perfectamente ya que identifica este tipo de documento, pero debes ser cuidadoso al elegir la
clave, si no crees que puedas hacerlo deja que MongoDb genere automáticamente el _id por ti.

update
1 db.collection.update(
2 {<query>},
3 {<update>},
4 {
5 upsert: <boolean>,
6 multi: <boolean>,
7 writeConcern: <document>
8 }
9 )

• upsert: true -> Hace que si el documento no existe se cree y si existe lo actualiza. Si lo
establecemos a false ,si existe lo actualiza, en caso contrario no hace nada.
• multi: true -> Especificamos que queremos actualizar múltiples documentos, por defecto es
false de modo que solo se actualiza un único documento, esto es importante ya que en SQL
un update actualiza todas las coincidencias, en MongoDB por defecto multi es false por lo que
solo actualizará la primera coincidencia.
• writeConcer -> Hablaremos de este concepto en el tema de “ReplicaSet”, se trata de una opción
para definir en cuantas réplicas de servidores mongo debe estar escrito el cambio para devolver
un “OK”.
CRUD 11

Existen dos formas de update en Mongodb. - Update de “reemplazo de documento completo” -


Update “parcial”
Podemos actualizar un documento completo, para ello especificaremos en el primer parámetro la
query con el documento que queremos actualizar, en el segundo la actualización que queremos
aplicar y en el tercer parámetro las opciones. Por ejemplo:

1 db.people.update( { name: "Joseba" }, { name: "Jose", active: 1 }, { upsert: tru\


2 e } )

Con el update anterior vamos a editar el documento completo con “name” “Joseba” y vamos a
introducir el nombre “Jose” y active con valor 1.
Realizando esta update perderemos los campos que no hayamos especificado, quedara un documento
tal y como hemos especificado:
Para editar uno o varios campos y preservar el documento o lo que es lo mismo realizar un “update
parcial” debemos utilizar el operador $set, por ejemplo:

1 db.people.update( { name: "Joseba" }, { $set: {name: "Jose", fullstack: 1 } }, {\


2 upsert: true } )

De esta forma realizaremos un update parcial y añadiremos fullstack: 1 , cambiaremos el nombre


por “Jose” y mantendremos intactos los campos que existiesen previamente.
Si no agregamos la opción multi:true solo se modificará 1 documento, si lo añadimos se modificarán
todos los usuarios que tengan el nombre “Joseba”.

save
.save realiza la misma acción que update con “upsert” por defecto. Es decir, si no exist el _id insertará
el nuevo documento y si existe lo actualizará.

1 db.collection.save(
2 <document>,
3 {
4 writeConcern: <document>
5 }
6 )

Por ejemplo:
CRUD 12

1 db.people.save( { _id: ObjectId("50691737d386d8fadbd6b01d"), nombre: "Patxi", su\


2 rname: "Etxeberria" } )

Si el id existe actualizará el documento, si no existe lo creará.

find y findOne
find({WHERE},{PROJECTION})
Para buscar los documentos que insertemos en la base de datos podemos utilizar find() o findOne().
La diferencia es que find retorna un cursor con todos los documentos coincidentes y findOne solo un
documento, si existiese más de una coincidencia será uno al azar. Si lanzamos find() sin argumentos:

1 db.people.find()

Obtendremos todos los documentos de la colección. Por el contrario si ejecutamos sin argumentos
findOne() :

1 db.people.findOne()

Obtendremos un documento al azar.


En la sección anterior “MongoDB Shell” ya hemos comentado algo sobre los cursores, si no estás
familiarizado aún con las consultas y los cursores ahora si que te recomiendo volver atrás y practicar
algunas consultas para familiarizarte con MongoDB.
Podemos utilizar argumentos en find() o findOne() para buscar por una clave concreta, por ejemplo:

1 db.people.findOne({"\_id": "mjoseba@gmail.com"})

En este caso retornará nuestro registro y estamos seguros que no hay más ya que hemos buscado
sobre la clave primaria y única.

1 {"\_id": "mjoseba@gmail.com", "name": "Joseba", "surname": "Madrigal"}

Si queremos buscar todos los usuarios que se llaman “Joseba” dentro de la colección: db.people.find({“name”:
“Joseba”})
Retorna todos los usuarios llamados “Joseba”.
Si especificamos un segundo parámetro definiremos los campos que queremos recibir: db.people.find({“name”:
“Joseba”}, {“surname”: true, _id:false})
Devuelve los apellidos de las personas que se llaman Joseba: {“surname”: “Madrigal”}

remove
CRUD 13

1 db.collection.remove(query, {justOne: boolean, writeConcern: document})

Es un borrado de múltiples documentos por defecto a no ser que se especifique justOne a true.

findAndModify
Modifica y retorna un único documento.

1 findAndModify: <collection-name>,
2 query: <document>,
3 sort: <document>,
4 remove: <boolean>,
5 update: <document>,
6 new: <boolean>,
7 fields: <document>,
8 upsert: <boolean>
Operadores
Podemos utilizar los siguientes operadores para evaluar en nuestras consultas.

• $eq -> (Equal) Evalúa si un campo es igual al valor que se le pasa.


• $gt -> (Greater than) Evalúa si un campo es mayor que el valor pasado.
• $gte -> (Greater than or equal) Evalúa si un campo es mayor o igual que el valor pasado.
• $lt -> (Lesser than or equal) Evalúa si un campo es menor que el valor pasado.
• $lte -> (Lesser than or equal) Evalúa si un campo es menor o igual que el valor pasado.
• $ne -> (Not equal) Evalúa si un campo no es igual al valor.
• $or -> (O)
• $and -> (Y)
• $exists -> {exists: true} evalúa si existe(no es nulo).
• $in -> Evalúa si el valor se encuentra dentro del array.
• $nin -> (Not in) Evalúa si el valor no se encuentra dentro del array.
• $all -> Evalúa si todos los valores se encuentran dentro del array.
• $mod -> (Modulo) Aplica la operación de módulo y lo compara con el valor pasado.
• $regex -> Selecciona los documentos que casan con el valor de la expresión regular.
• $text -> Realiza una busqueda de texto.
• $where -> Casa con los documentos que satisfagan una expresión en JavaScript.

Por ejemplo si queremos buscar los usuarios de 18 años: db.people.find({ age : { $eq : 18 }
} )

Por ejemplo si queremos buscar los usuarios mayores de 18 años y menores de 50 o de 50 en nuestra
colección “people”:

1 db.people.find({ age : { $gt : 18 , $lte : 50 } } )

Si queremos buscar los documentos con una cantidad menor que 20 o un precio de 10: db.inventory.find(
{ $or: [ { quantity: { $lt: 20 } }, { price: 10 } ] } )

14
Operadores 15

Operadores especiales:

Geoespaciales
• $geoWithin: Selecciona geometrías dentro de una geometría GeoJSON. Los índices 2dsphere
y 2d soportan $geoWithin.
• $geoIntersects: Seleccionan las geometrías con intersección en un GeoJSON. Los índices
2dsphere y 2d lo soportan.
• $geoIntersects.
• $near: Devuelve un objeto geoespacial en la proximidad de un punto. Require un índice
geoespacial. Los índices 2dsphere y 2d soportan $near.
• $nearSphere: Devuelve un objeto geoespacial en la proximidad de un punto de la esfera.
Require un índice geoespacial. Los índices 2dsphere y 2d soportan $nearSphere.

Array
Para los campos que contienen Arrays, MongoDB proporciona los siguientes operadores: - $elem-
Match - $slice - $. - $size
Por ejemplo, the inventory collection contains the following document: { "\_id" : 1, "name" :
"Joseba", "languages" : [ "JS", "PHP", "HTML" ] }

Si quisiéramos obtener solo los dos primeros elementos del array languages lo haríamos de la
siguiente manera: db.inventory.find( { \_id: 1 }, { languages: { $slice: 2 } } )
Modelado de datos
Si vienes del mundo sql estarás acostumbrado a crear entidades, aplicar las formas normales… En
MongoDb se crean los esquemas de la base de datos orientados al uso que se le da a los datos en la
aplicación. Este tipo de diseño de esquema se llama “Application driven schema”. Analizaremos los
datos y los patrones de acceso que se usarán en el programa para crear un esquema a esos datos.
Muchas veces nos encontraremos con la duda de si embeber un documento o no. Por ejemplo en un
blog, imaginemos una colección post:

1 {
2 "author" : "Joseba",
3 "title" : "Mongodb introduction",
4 "text" : "MongoDB is a no-sql document oriented data base ...",
5 "comments" : [
6 {"author": "Patxi", "comment": "Thanks for this post!" },
7 {"author": "Juan", "comment": "Nice post!" }
8 ],
9 "tags" : ["javascript", "mongodb", "jquery", "angularjs"],
10 }

Normalmente si no vamos a acceder de forma independiente al contenido de un comentario y


sabemos que no seran muchos los comentarios o seran limitados, lo embeberemos. Lo normal es
que al mostrar el post mostremos abajo los comentarios, es difícil querer leer un comentario sin
ver el post del que se trata, MongoDB está orientado a la programación. Cuando tengas duda entre
embeber o no, decántate por la forma en la que vas a acceder a los datos.
Reglas: - Relaciones 1-1: Por ejemplo un usuario tiene un único dni y ese dni no pertenece a
nadie más que a ese usuario. Ese tipo de datos deben estar siempre embebidos en un docu-
mento dentro del usuario: { nick: "josebyte", pass: "1234", type: "admin", dni:{ codi-
go: "78964914f", fechaEmision: "", fechaCaducidad: "", padre: "", madre: "" } } - Re-
laciones 1-n: Por ejemplo un usuario tiene de 0 a n coches y cada coche pertenece únicamente a un
usuario. Ese tipo de datos se deben embeber en un array de documentos:

16
Modelado de datos 17

1 {
2 nick: "josebyte",
3 pass: "1234",
4 type: "admin",
5 cars:[
6 {marca: "Ford", año: "2009", matricula:"1234-GGG"},
7 {marca: "Opel", año: "2001", matricula:"4321-JJJ"}
8 ]
9 }

• Relaciones n-m: Si pensamos en el ejemplo anterior y que un coche puede tener varios usuarios
el ejemplo anterior sería n-m y en lugar de embeber los datos en el documento deberiamos
crear otra coleccion:

1 Colección usuarios:
2 {
3 nick: "josebyte",
4 pass: "1234",
5 type: "admin",
6 cars:[
7 11154,
8 11155
9 ]
10 }
11
12 Colección coches:
13 {"_id": 11154, "marca": "Ford", "año": "2009", "matricula":"1234-GGG"}
14 {"_id": 11155, "marca": "Opel", "año": "2001", "matricula":"4321-JJJ"}

Nota: Se podra embeber en usuarios el array de coche o a la inversa, un array conductores dentro
de los documentos coches que contendrá el _id de cada usuario de ese coche. Dependerá de cómo
se va a acceder a los datos en el programa.
Embeber documentos 1-1 y 1-n proporcionará mayor rapidez de lectura. Es importante que se
analicen las lecturas y escrituras que realizará el programa sobre la base de datos para orientar
el modelo de base de datos y mejorar la eficiencia.
Índices
Introducción
Los índices en MongoDB funcionan igual que en cualquier base de datos. Si realizamos una consulta
en una base de datos sin usar índices la consulta será lenta debido a que se recorrerán todos los
documentos de la colección para comprobar si se cumple la consulta. Sin embargo si disponemos
de un índice, los elementos están ordenados y como sabréis no es lo mismo buscar palabras en un
diccionario que en una sopa de letras… el diccionario lleva un índice alfabético en las palabras.

Consideraciones
Cuando creas índices debes tener en cuanta lo siguiente: - Por defecto los _id de todas las colecciones
tienen un índice. - Cada índice requiere 8KB de espacio. - Añadir un índice tiene un impacto
negativo para las operaciones de escritura. Para colecciones con alto ratio escritura>lectura, los
índices son caros ya que cada inserción también debe actualizar los índices. - Las colecciones con
alto ratio lectura>escritura a menudo se benefician de índices adicionales. Los índices no afectan al
rendimiento de las lecturas que no usen índices. - Cada índice activo consume espacio en disco y
memoria. Este uso puede ser importante y debe ser analizado para la planificación de la capacidad.

Ver indices
Puedes ver todos los índices de la base de datos con la siguiente consulta:

1 db.system.indexes.find()

Si quieres ver los indices de una colección concreta: db.micoleccion.getIndexes()


Si quisiéramos ver el tamaño de los índices de una colección: db.micoleccion.totalIndexSize()

Funcionamiento de los índices


Si creamos un índice para “nombre”: db.createIndex({nombre:1}) - Las consultas sobre nombre
usaran el índice.
Si creamos un índice sobre “nombre” y “apellido”. db.createIndex({nombre:1, apellido:1}) -
Las consultas sobre “nombre” usarán el índice. - Las consultas sobre “nombre” y “apellido” usarán el

18
Índices 19

índice. - Las consultas sobre “apellido” NO usarán el índice. - Las consultas sobre “nombre” y “edad”
usarán el índice pero solo la parte de “nombre”.
Puedes forzar usar un índice determinado indicando a la consulta .hint({campo:1}) .hint({$natural:1})
//para no usar indice
Por defecto en el log encontraremos las consultas que tardan mas de 100ms
Podemos usar profiler para establecer el nivel de capturas que deseamos realizar: - 0 desactivado - 1
consultas lentas - 2 todas las consultas

1 db.setProfilingLevel(2)

1 db.system.profile.find()

Crear indices
Para crear un índice: db.micoleccion.createIndex({micampo:1}) db.micoleccion.ensureIndex({micampo:1})
//Para versiones previas a 3.0
Podemos crear índices compuestos de varios campos db.micoleccion.createIndex({{micampo:1
, orden:1})

Índice unico
1 db.micoleccion.createIndex({{micampo:1},{unique:true})

Eliminar duplucados
Si en la creación indicamos la opción dropDups:1 eliminaremos los valores duplicados.

Borrar indece:
1 db.micoleccion.dropIndex({micampo:1});

Multikey index
Si creamos un índice de un campo que es un array será un índice multikey. No puedes crear un
índice compuesto sobre dos campos multikey, en ese caso debes crear dos índices separados.
Si existe un documento con valores y creas un índice ya se considera multikey pese a que el resto de
valores sean numéricos o string.
Si hacemos un .explain() de una consulta y dice isMultiKey significa que usa es un índice multikey.
Índices 20

Sparse index
Los índices creados con la opción sparse:1 generará índices solo para los campos con valor disntito de
null. Es usado principalmente en conjunto con unique ya que si no disponen ese campo se considera
que tienen null y hay valores duplicados.

Índices en background
Por defecto se lanzan en foreground y son mas rápidas pero bloquean las escrituras en la misma
base de datos. En background no bloqueará las escrituras pero será bastante más lento.
Una instancia de mongod solo puede construir un índice en background por base de datos. Una
creación de índices en background permite escrituras pero bloquea el shell.

Indices geoespaciales
Hay dos tipos de indices geoespaciales: - 2D [x,y] - 2D Sphere [long, lat]
2d createIndex({location: "2d", type: 1}) find({ location:{$near:[x,y]} }).limit(30)

spherical createIndex({location: "2dsphere", type: 1}) find({ location:{ $near: { $geo-


metry:{ type: "Point", coordinates: [lat, long] } $maxDistance: 2000 } } }).limit(30)

Full text search


1 createIndex({mitexto: "text"})
2 find({$text:{$search:{"dog"}}})
3 find({$text:{$search:{"dog CAT"}}})

Hace un OR con las palabras.


Podemos proyectar y ordenar por $meta:”textScore” para obtener los mejores resultados para nuestra
busqueda ordenados

mongotop
Muestra tiempos de lectura, escritura y totales por cada colección. Podemos lanzarlo con: mongotop
5 Refrescará cada 5milisegundos

mongostat
Muestra update, query delete por query. Nos dice si el índice está en memoria o no.
Agregation
MongoDB dispone de herramientas para hacer consultas sobre agrupaciones de documentos. Pode-
mos agrupar datos de varios documentos y realizar operaciones sobre esa agrupación. Utilizaremos
agregaciones por ejemplo para mostrar estadísticas sobre nuestros documentos. Funciona de forma
similar al comando GROUP BY y HAVING de SQL.
Si quisiésemos agrupar y contar cuantos usuarios hay por país:

1 db.usuarios.aggregate([
2 {
3 $group: {
4 "_id": "$pais",
5 "num_usuarios": {$sum: 1}
6 }
7 }
8 ])

Si quisiésemos agrupar por usuarios de cada país y provincia: db.usuarios.aggregate([ {


$group: { "_id": { "pais":"$pais", "provincia":"$provincia" }, "num_usuarios": {$sum:
1} } } ]) - El primer parámetro de cada id entrecomillado que no tiene $ es el nombre que nosotros
queramos mostrar. - El valor con $ después de “pais” y “provincia” debe coincidir con el campo de
la base de datos. - $sum es una expresión que suma 1 por cada documento.
La agregación usa un pipeline, y cada una de estas fases pueden aparecer más de una vez en el
pipeline: - $project -> remodela el documento seleccionando los campos deseados o trayéndolos de
lo más profundo a lo más alto. 1-1 - $match -> filtra. n-1 - $group -> puedes sumar contar y agrupar
por determinado campo. n-1. - $sort -> ordenación. 1-1 - $skip -> saltar documentos. n-1 - $limit ->
limitar los resultados. - $unwind -> normaliza el array, por ejemplo: tags:[“rojo”, “azul”] se convertirá
en: tags: “rojo”, tags: “azul” - $out -> redirigir la salida a otra coleccion en lugar de a un cursor (por
defecto) 1-1
Expresiones: - $sum - $avg - $min - $max - $push - $push - $addToSet

• $first -> requiere que ordenes con sort los documentos


• $last -> requiere que ordenes con sort los documentos

Si quisiéramos sumar los puntos de cada pais que han obtenido los usuarios: db.usuarios.aggregate([
{ $group: { "_id": "$pais", "puntosPorPais": {$sum: "$puntos"} } } ])

21
Agregation 22

Si quisiéramos calcular la media de puntos de cada país: db.usuarios.aggregate([ { $group: {


"_id": "$pais", "mediaDePuntos": {$avg: "$puntos"} } } ])

Si quisiéramos mostrar las provincias que tienen usuarios: db.usuarios.aggregate([ { $group:


{ "_id": "$pais", "provincias": {$addToSet: "$provincia"} } } ])

Si quisiéramos obtener el mejor usuario de cada país: db.usuarios.aggregate([ { $group: { "_-


id": "$pais", "maxPuntos": {$max: "$puntos"} } } ])
ReplicaSet
La forma de lograr alta disponibilidad y tolerancia a fallos es con las replicaSet. Con sharding lo que
se hace es partir la información en varios fragmentos por lo que lo que logramos es escalabilidad.
Para crear un sistema con tolerancia a fallos y alta disponibilidad debemos crear un ReplicaSet.
Vamos a plantear un esquema comun de 3 nodos de mongod (el mínimo de nodos es 3 para un
ReplicaSet) funcionando con los mismos datos y trabajando en equipo. Hay tres tipos de nodo en los
ReplicaSet: - Normal (Primario y Secundarios) - Delayed o “con retraso” - Arbitro - Oculto
Los datos se escriben sobre el primario y luego se replican asincronamente en los secundarios. Los
árbitros nunca pueden convertirse en primarios y unicamente entran en accion cuando el nodo
primario se cae y es necesario votar para elegir un nuevo primario. Para tener un nuevo primario
debes tener una mayoría estricta del numero original de nodos.
Cuando un nodo se recupera vuelve como secundario.
Las elecciones de nodo primario se realizan de forma transparente para el usuario.
Veamos como hacer un replicaSet para lograr alta disponibilidad y tolerancia a fallo. Antes de nada
debo decir que lo lógico y natural es que para lograr lo anterior debemos disponer de 3 servidores
distintos ya que si los montamos sobre la misma maquina no estamos solucionando nada en el caso
de que se caiga o se rompa. Aclarado eso y solo con fines educativos crearemos el replicaset sobre
la misma maquina en diferentes puertos (ya sabemos que esta mal pero es solo para aprender).
Lo primero vamos a especificar un nombre para nuestro replicaSet mediante:

1 mongod --replSet "rs0"

Este comando creará un replicaSet con el nombre “rs0”.


Ahora accedemos al shell de mongo: mongo
E iniciamos el replicaSet: rs.initiate()
Mediante el siguiente comando podremos ver la configuración del replicaset: rs.conf()
Podemos guardarlo y editar esa información añadiendo dos miembros más tal y como pue-
de apreciarse aquí: { "_id" : "rs0", "version" : 1, "members" : [ { "_id" : 1, "host" :
"localhost:27017" }, { "_id" : 2, "host" : "localhost:27018" }, { "_id" : 3, "host" :
"localhost:27019" }, ] }
O también podemos añadirlos por comando: rs.add("mongodb1.example.net") rs.add("mongodb2.example.net

Finalmente podemos comprobar el estado de nuestra replicaSet de esta forma: rs.status()

23
Sharding
La forma de lograr alta disponibilidad y tolerancia a fallos es con las replicaSet. Con sharding lo que
haces es partir la información en varios fragmentos por lo que lo que logramos es escalabilidad.
Las particiones se realizan basadas en rangos sobre la shard-key.
The config server processes are mongod instances that store the cluster’s metadata. You designate a
mongod as a config server using the –configsvr option. Each config server stores a complete copy
of the cluster’s metadata.
In production deployments, you must deploy exactly three config server instances, each running on
different servers to assure good uptime and data safety. In test environments, you can run all three
instances on a single server.
IMPORTANT All members of a sharded cluster must be able to connect to all other members of a
sharded cluster, including all shards and all config servers. Ensure that the network and security
systems including all interfaces and firewalls, allow these connections. Create data directories for
each of the three config server instances. By default, a config server stores its data files in the
/data/configdb directory. You can choose a different location. To create a data directory, issue a
command similar to the following: mkdir /data/configdb Start the three config server instances.
Start each by issuing a command using the following syntax: mongod –configsvr –dbpath <path>
–port <port> The default port for config servers is 27019. You can specify a different port. The
following example starts a config server using the default port and default data directory: mongod
–configsvr –dbpath /data/configdb –port 27019 For additional command options, see mongod or
Configuration File Options. NOTE All config servers must be running and available when you first
initiate a sharded cluster. Start the mongos Instances
The mongos instances are lightweight and do not require data directories. You can run a mongos
instance on a system that runs other cluster components, such as on an application server or a server
running a mongod process. By default, a mongos instance runs on port 27017.
When you start the mongos instance, specify the hostnames of the three config servers, either in the
configuration file or as command line parameters.
TIP To avoid downtime, give each config server a logical DNS name (unrelated to the server’s
physical or virtual hostname). Without logical DNS names, moving or renaming a config server
requires shutting down every mongod and mongos instance in the sharded cluster. To start a mongos
instance, issue a command using the following syntax:
mongos –configdb <config server hostnames> For example, to start a mongos that connects to config
server instance running on the following hosts and on the default ports:
cfg0.example.net cfg1.example.net cfg2.example.net You would issue the following command:

24
Sharding 25

mongos –configdb cfg0.example.net:27019,cfg1.example.net:27019,cfg2.example.net:27019 Each mon-


gos in a sharded cluster must use the same configDB string, with identical host names listed in
identical order.
If you start a mongos instance with a string that does not exactly match the string used by the other
mongos instances in the cluster, the mongos return a Config Database String Error error and refuse
to start.
Add Shards to the Cluster
A shard can be a standalone mongod or a replica set. In a production environment, each shard should
be a replica set. Use the procedure in Deploy a Replica Set to deploy replica sets for each shard.
From a mongo shell, connect to the mongos instance. Issue a command using the following
syntax: mongo –host <hostname of machine running mongos> –port <port mongos listens on>
For example, if a mongos is accessible at mongos0.example.net on port 27017, issue the following
command: mongo –host mongos0.example.net –port 27017 Add each shard to the cluster using the
sh.addShard() method, as shown in the examples below. Issue sh.addShard() separately for each
shard. If the shard is a replica set, specify the name of the replica set and specify a member of the
set. In production deployments, all shards should be replica sets. OPTIONAL You can instead use the
addShard database command, which lets you specify a name and maximum size for the shard. If you
do not specify these, MongoDB automatically assigns a name and maximum size. To use the database
command, see addShard. The following are examples of adding a shard with sh.addShard(): To add
a shard for a replica set named rs1 with a member running on port 27017 on mongodb0.example.net,
issue the following command: sh.addShard( “rs1/mongodb0.example.net:27017” ) Changed in ver-
sion 2.0.3.
For MongoDB versions prior to 2.0.3, you must specify all members of the replica set. For example:
sh.addShard( “rs1/mongodb0.example.net:27017,mongodb1.example.net:27017,mongodb2.example.net:27017”
) To add a shard for a standalone mongod on port 27017 of mongodb0.example.net, issue the
following command: sh.addShard( “mongodb0.example.net:27017” ) NOTE It might take some time
for chunks to migrate to the new shard. Enable Sharding for a Database
Before you can shard a collection, you must enable sharding for the collection’s database. Enabling
sharding for a database does not redistribute data but make it possible to shard the collections in
that database.
Once you enable sharding for a database, MongoDB assigns a primary shard for that database where
MongoDB stores all data before sharding begins.
From a mongo shell, connect to the mongos instance. Issue a command using the following syntax:
mongo –host <hostname of machine running mongos> –port <port mongos listens on> Issue the
sh.enableSharding() method, specifying the name of the database for which to enable sharding. Use
the following syntax: sh.enableSharding(“<database>”) Optionally, you can enable sharding for a
database using the enableSharding command, which uses the following syntax:
db.runCommand( { enableSharding: <database> } ) Enable Sharding for a Collection
You enable sharding on a per-collection basis.
Sharding 26

Determine what you will use for the shard key. Your selection of the shard key affects the efficiency
of sharding. See the selection considerations listed in the Considerations for Selecting Shard Key. If
the collection already contains data you must create an index on the shard key using createIndex(). If
the collection is empty then MongoDB will create the index as part of the sh.shardCollection() step.
Enable sharding for a collection by issuing the sh.shardCollection() method in the mongo shell. The
method uses the following syntax: sh.shardCollection(“<database>.<collection>”, shard-key-pattern)
Replace the <database>.<collection> string with the full namespace of your database, which consists
of the name of your database, a dot (e.g. .), and the full name of the collection. The shard-key-pattern
represents your shard key, which you specify in the same form as you would an index key pattern.
EXAMPLE The following sequence of commands shards four collections:
sh.shardCollection(“records.people”, { “zipcode”: 1, “name”: 1 } ) sh.shardCollection(“people.addresses”,
{ “state”: 1, “_id”: 1 } ) sh.shardCollection(“assets.chairs”, { “type”: 1, “_id”: 1 } ) sh.shardCollection(“events.alerts”,
{ “_id”: “hashed” } ) In order, these operations shard: The people collection in the records database
using the shard key { “zipcode”: 1, “name”: 1 }. This shard key distributes documents by the value of
the zipcode field. If a number of documents have the same value for this field, then that chunk will
be splittable by the values of the name field. The addresses collection in the people database using
the shard key { “state”: 1, “_id”: 1 }. This shard key distributes documents by the value of the state
field. If a number of documents have the same value for this field, then that chunk will be splittable
by the values of the _id field. The chairs collection in the assets database using the shard key {
“type”: 1, “_id”: 1 }. This shard key distributes documents by the value of the type field. If a number
of documents have the same value for this field, then that chunk will be splittable by the values of
the _id field. The alerts collection in the events database using the shard key { “_id”: “hashed” }. New
in version 2.4.
This shard key distributes documents by a hash of the value of the _id field. MongoDB computes the
hash of the _id field for the hashed index, which should provide an even distribution of documents
across a cluster.
DBA
Store engine
El store engine es una capa entre la Base de datos y el hardware. El store engine define: -Como se
guardan los datos en disco. -Como se borra la información de disco. -Como se lee la información de
disco. -Las estructuras que se usan para almecenar los daots.
Existe dos storage engines actualmente: - MMAP v1 - WiredTiger
El store engine determina dos aspectos fundamentales: -Formato de los indices -El formato de datos
y archivos.

27
Bibliografía
El material que me ha ayudado a escribir este libro y considero de tu interés es el siguiente:
Documentación oficial de MongoDB: https://docs.mongodb.org/manual/
Cursos gratuitos de Mongo University: M101

28

Vous aimerez peut-être aussi