Gestión avanzada de datos con MongoDB

Mejora del rendimiento con índices, diseño de estructuras de datos y operaciones de agregación
4.4 (11 ratings) Instead of using a simple lifetime average, Udemy calculates a
course's star rating by considering a number of different factors
such as the number of ratings, the age of ratings, and the
likelihood of fraudulent ratings.
222 students enrolled
$45
Take This Course
  • Lectures 93
  • Contents Video: 10.5 hours
  • Skill Level All Levels
  • Languages Spanish
  • Includes Lifetime access
    30 day money back guarantee!
    Available on iOS and Android
    Certificate of Completion
Wishlisted Wishlist

How taking a course works

Discover

Find online courses made by experts from around the world.

Learn

Take your courses with you and learn anywhere, anytime.

Master

Learn and practice real-world skills and achieve your goals.

About This Course

Published 5/2014 Spanish

Course Description

El curso, que es el segundo de una serie de cursos sobre MongoDB, nos permite profundizar en las tareas de gestión de datos. Vamos a ver:

  • qué tipos índices soporta MongoDB
  • cómo se usan los índices
  • cómo se evalúa el uso de los índices
  • qué criterios seguir a la hora de definir la estructura de datos de nuestra aplicación
  • qué otras posibilidades de almacenamiento nos ofrece MongoDB (colecciones limitadas, GridFS,...)
  • cómo realizamos operaciones de agregación en MongoDB
  • cómo utilizamos MapReduce en MongoDB

MongoDB cuenta con varios tipos de índices para mejorar el rendimiento de las operaciones de lectura. Los índices geoespaciales nos permiten trabajar con datos de geolocalización, por ejemplo filtrando la información por la distancia a la que se encuentra de un punto sobre la superficie de la Tierra. Los índices de texto, definitivamente incluidos en MongoDB a partir de la versión 2.6, nos permiten realizar búsquedas sobre textos.

También veremos cómo diseñar la estructura de datos en la que almacenaremos la información de nuestra aplicación. Aunque MongoDB no es una base de datos relacional, y no impone una estructura rígida a los datos de nuestra aplicación, es conveniente tener en cuenta las implicaciones de rendimiento que las decisiones sobre esta estructura pudieran ocasionar.

MongoDB cuenta con varias alternativas para llevar a cabo operaciones de agregación de los datos (medias, máximas, sumas,...). En este curso nos ocuparemos principalmente del sistema de agregación de MongoDB, y de la implementación MapReduce realizada por MongoDB.

El curso consta de una serie de lecciones en formato vídeo, que constan tanto de contenidos teóricos cómo prácticos. La longitud de los vídeos suele ser inferior a los diez minutos, aunque en ocasiones alguno de ellos se acerca a los quince minutos.

Veremos cómo replicar en vuestros equipos los ejemplos incluidos en los vídeos, para que hagáis con vuestras propias manos las operaciones que os muestro. Cada cierto número de lecciones se incluye un grupo de preguntas para comprobar el grado de compresión de los temas tratados. En ocasiones, se incluye algún ejercicio práctico entre las preguntas, que requiere la descarga de algún archivo y el manejo de un servidor MongoDB. También se incluyen diversos ejemplos de código NodeJS, para ver cómo se trabaja con MongoDB desde una aplicación.

El curso puede ser completado en unas seis semanas, dedicando unas 8 - 12 horas a la semana. En todo caso, elige el ritmo que más se adapte a tu disponibilidad.

What are the requirements?

  • Para realizar los ejemplos del curso hay que instalar MongoDB
  • Conocimientos básicos de Javascript y NodeJS
  • Conocimientos básicos de MongoDB

What am I going to get from this course?

  • Acelerar las operaciones de lectura con los índices
  • Realizar operaciones con datos de geolocalización
  • Diseñar eficientemente nuestra estructura de datos
  • Extraer información agregada de nuestros datos (medias, máximos,...)

What is the target audience?

  • Administradores de bases de datos
  • Programadores web
  • Emprendedores
  • Aficionados a la informática

What you get with this course?

Not for you? No problem.
30 day money back guarantee.

Forever yours.
Lifetime access.

Learn on the go.
Desktop, iOS and Android.

Get rewarded.
Certificate of completion.

Curriculum

Section 1: Introducción al curso
Introducción
03:18
Section 2: Índices
03:44
Sin índices, para filtrar una colección hay que recorrerla completamente (escanear), y comparar cada documento de la colección con las condiciones de la consulta.
En la medida de lo posible hay que evitar que nuestras consultas se resuelvan mediante escaneos de las colecciones, ya que en general son muy lentos.
Con índices, para filtrar una colección se buscan en uno de ellos los valores que cumplen las condiciones de la consulta.
Esos valores tendrán asociados unos identificadores que apuntan directamente a los documentos que cumplen las condiciones de la consulta.
05:50
En MongoDB todos los índices se guardan en estructuras jerárquicas denominadas árboles-B (B-tree).
En los árboles-B cada nivel del árbol define la distribución de los valores del índice en el siguiente nivel, y los valores del índice se almacenan de manera ordenada.
Cada valor distinto del campo o de los campos incluidos en el índice, tiene asociado uno o varios valores que apuntan a la localización dentro de la colección de los documentos que contienen ese valor o valores.
04:04
El índice es menor en tamaño que la colección, y además recorrerlo es más rápido por el hecho de tener una estructura en árbol-B.
El caso más óptimo de utilización de un índice es cuando los campos que se han de devolver están incluidos en el índice. En ese caso ni siquiera se accede a la colección.
Cuando se utiliza un índice para ordenar, lo que se hace es recorrer el árbol-B del índice en el orden especificado, recuperando para cada valor del índice la localización de cada documento.
Introducción a los índices
5 questions
04:30
En MongoDB los índices se crean con db.colección.ensureIndex( patrón de clave ), si no existe previamente. El patrón de clave o key pattern especifica qué campo o campos se incluyen en el índice.
Un ejemplo sería { edad : 1 , pais : -1 }, que crea un índice sobre los campos edad y pais de una colección. El orden se especifica con 1 (ascendente) ó -1 (descendente).
Cada colección cuenta con un índice único automáticamente creado sobre el campo _id. El resto de índices se han de crear de manera explícita con db.colección.ensureIndex. Los índices se usan de manera automática en las consultas, no hay que hacer nada para forzar su uso.
08:02
Para averiguar cuántos índices tenemos en una colección concreta haremos db.colección.getIndexes(). Podemos conocer cuál es el tamaño del índice creado con db.colección.stats().
Para ver como afectan los índices vamos a trabajar con una colección de datos de puntuaciones que ya vimos en otro tema. Si queréis utilizar la colección en vuestro entorno de trabajo podéis descargar el archivo 2.PrimerosPasosIndices.zip y descomprimirlo.
Ejecutáis mongo localhost:vuestroPuerto --shell ruta/cargaPuntuaciones2.js. Prestad atención a utilizar el puerto en el que escucha vuestra instancia de MongoDB y la ruta en la que habéis descargado el archivo anterior.
Si le echáis un vistazo veréis que cambia de base de datos, vacía la colección puntuaciones y luego la alimenta con 500000 documentos (con puntuaciones aleatorias).
Creación de índices
4 questions
07:11
El índice que se crea por defecto puede incluir claves repetidas. Para crear un índice único lo que haremos será db.colección.ensureIndex( patrón de clave , { unique : true } ).
Si intentamos crear un índice único sobre una colección que ya tiene documentos con claves repetidas dará error.
Si tenemos una colección con un campo que sólo aparece en algunos documentos, para crear un índice sobre él sería útil indicar que es un índice disperso, para que el índice no incluya información de los documentos que no lo contienen.
Esto se indica con db.colección.ensureIndex( patrón de clave, { sparse : true } ). Hay que tener cuidado con estos índices, puesto que bajo determinadas circunstancias pueden producir resultados curiosos.
10:26
Podemos utilizar un índice TTL (Time To Live), para que los documentos se eliminen tras un determinado tiempo, o en un momento determinado.
Si tenemos que crear un índice sobre una colección grande, y no queremos bloquear el acceso a la base de datos, podemos crearla en segundo plano ( { background : true } ). Tardará más, y el índice creado ocupará algo más.
Si queremos crear un índice único sobre un campo que ya cuenta con claves repetidas podemos especificar { dropDups : true } para eliminar los documentos que sean necesarios. Pero los documentos eliminados no son los que queramos, si no que MongoDB los elimina de forma arbitraria. A partir de la versión 2.6 de MongoDB se desaconseja su uso.
Opciones para la creación de índices
4 questions
11:57
Los índices que debemos crear están absolutamente definidos por la forma en la que se accede a los datos. En ocasiones puede pasar que un mismo campo participe en varias condiciones de filtrado o de ordenación.
En esas circunstancias hay que decidir si crear un índice con sólo ese campo, o incluirlo en uno compuesto con otros. Para decidir esto hay que tener en cuenta cuándo un índice puede utilizarse.
Para comprobar la diferencia entre usar índices o no, vamos a trabajar con una colección de puntuaciones con un modelo de documento que ya hemos visto.
Si queréis utilizar la colección en vuestro entorno de trabajo podéis descargar el archivo 2.ComoSeUsanIndices.zip y lo descomprimis. Luego ejecutáis mongo localhost:vuestroPuerto --shell ruta/cargaPuntuacionesMasiva.js.
Prestad atención a utilizar el puerto en el que escucha vuestra instancia de MongoDB y la ruta en la que habéis descargado el archivo anterior. Si le echáis un vistazo veréis que cambia de base de datos, vacía la colección puntuaciones y luego la alimenta con un millón de documentos (puntuaciones aleatorias de 0 a 99.99).
La forma de averiguar si un índice se utiliza en una consulta es añadir explain() al final de ésta. Por ejemplo db.colección.find().explain(). Esto ejecuta la consulta, pero no devuelve los documentos que cumplen las condiciones de la consulta, sino información acerca de cómo se lleva a cabo.
11:10
Para poder usar un índice compuesto en una operación, las condiciones de filtrado deben afectar a un subconjunto cabecero de los campos del índice.
También se utilizará si el criterio de ordenación se impone sobre un subconjunto cabecero de los campos del índice, a no ser que el primero o los primeros estén incluidos en las condiciones de filtrado. En ese caso tiene que ser un subconjunto cabecero de los campos no utilizados en las condiciones de filtrado.
Para los ejemplos vamos a trabajar con una colección de puntuaciones con un modelo de documento que ya hemos visto. Si queréis utilizar la colección en vuestro entorno de trabajo podéis descargar el archivo 2.CuandoSeUsanIndices.zip y lo descomprimis.
Luego ejecutáis mongo localhost:vuestroPuerto --shell ruta/cargaPuntuaciones.js. Prestad atención a utilizar el puerto en el que escucha vuestra instancia de MongoDB y la ruta en la que habéis descargado el archivo anterior.
09:16
Cada cierto tiempo MongoDB evalúa el coste de utilizar todos los índices posibles para una consulta, y almacena los resultados. Hasta que no vuelve a realizar esta evaluación, utiliza esos resultados para decidir qué índices utilizar para cada operación.
En algunos casos puede que la consulta utilice uno de los índices que no nos interesan. Para asegurarnos de que una consulta utiliza un índice concreto podemos hacer db.colección.find( criterio , proyeccion ).hint( criterio de ordenación ).
En el archivo 2.QueIndiceSeUsa.zip tenéis el código de un ejemplo NodeJS que fuerza el uso de un determinado índice con hint.
Uso de índices
4 questions
09:26
En general los índices aceleran las lecturas de datos y ralentizan las escrituras, si bien las actualizaciones y eliminaciones de datos pueden acelerarse si utilizan índices para encontrar los documentos a actualizar o eliminar.
Si los índices que se utilizan caben en memoria, MongoDB funciona mucho mejor. De hecho este es uno de los criterios más importante a la hora de crear índices, intentar que quepan en memoria.
Si tenemos una colección con operaciones de escritura muy frecuentes, no es muy recomendable tener un montón de índices. Cuantos más documentos tenga una colección, más beneficioso será tener índices que aceleren las lecturas. Existe un compromiso entre operaciones de lectura y escritura a la hora de decidir qué índices creamos en una colección.
En las operaciones de actualización, si el nuevo documento no cabe hay que guardarlo en otro lugar del fichero, es decir se mueve el documento. Todos las claves de los índices de la colección han de actualizarse para que apunten al nuevo emplazamiento del documento. Si se pueden evitar operaciones que provocan estos movimientos de documentos mejor.
05:25
En algunos casos, el uso de un índice no mejora mucho el rendimiento de las consultas. Por ejemplo, los operadores de comparación ($gt, $lt,...) pueden beneficiarse del uso de un índice, pero si la condición se traduce en que la gran mayoría de los documentos de la colección la cumplen, no es muy eficiente.
Las condiciones que niegan también son ineficientes ($ne, $nin, $exists : false), y además puede que tengan que acceder a todos los documentos de la colección incluso con índices.
Otro tipo de operador de consultas que puede no ser eficiente, incluso con el uso de índices, es $regex. Si la condición es sobre el inicio de un campo ($regex : /^Prueba/) puede usar eficientemente un índice. En caso contrario no.
Si creamos un índice sobre un campo con poca selectividad, la mejora de rendimiento tampoco será muy importante.
08:13
Vamos a ver diversos ejemplos de operaciones que no se benefician demasiado de la existencia de índices. Para ello utilizaremos la colección de puntuaciones de nuevo.
Si queréis utilizar la colección en vuestro entorno de trabajo podéis descargar el archivo 2.IndicesNoAportanMucho.Ejemplos.zip y descomprimirlo. Luego ejecutáis mongo localhost:vuestroPuerto --shell ruta/cargaPuntuaciones.js.
Prestad atención a utilizar el puerto en el que escucha vuestra instancia de MongoDB y la ruta en la que habéis descargado el archivo anterior. Si le echáis un vistazo veréis que cambia de base de datos, vacía la colección puntuaciones y luego la alimenta con 1000000 documentos (puntuaciones aleatorias de 0 a 99.99).
Consideraciones sobre los índices
5 questions
03:23
Los índices 2d realizan sus cálculos suponiendo que la Tierra es un plano. Una posición sobre la Tierra se guarda con un par de valores ( [ x , y ] ), donde x es la longitud, e y la latitud.
Pueden formar parte de un índice compuesto, siempre que el campo de localización sea el primero y que el número total de campos del índice sea 2.
06:42
Los índices 2dsphere realizan sus cálculos suponiendo que la Tierra es una esfera. Una posición sobre la Tierra se guarda con un objeto GeoJSON del tipo Point ( { type : "Point" , coordinates: [ <longitud>, <latitud> ] } ).
Por defecto GeoJSON supone que el datum es WGS84. Además puede trabajar con otros tipos de objetos GeoJSON.
Los índices 2dsphere también pueden trabajar con posiciones del tipo 2d, convirtiendo cada par de valores x,y en un objeto GeoJSON del tipo Point. Pueden formar parte de un índice compuesto, en el que los campos pueden ocupar cualquier orden y el límite de campos es el general (31).
06:53
Las operaciones que se pueden realizar con campos indexados geoespacialmente son la inclusión ($geoWithin), la intersección ($geoIntersects) y la proximidad ($near).
Esta última es la única que requiere de un índice geoespacial para que funcione. La inclusión puede realizarse sin que se haya creado un índice geoespacial, si bien su creación mejorará su rendimiento.
La intersección no funciona con índice 2d, ya que trabaja con datos de una esfera.
Índices geoespaciales
5 questions
07:12
Para poder filtrar los datos de una colección (expresados como objetos GeoJSON) por la cercanía a una localización necesitaremos crear un índice 2dsphere. Para ello podemos hacer db.colección.ensureIndex( { localizacion : "2dsphere" } ).
Observad que los resultados de la operación de cercanía ($near) se ordenan por distancia ascendente, y que la máxima distancia se expresa en metros.
Para todas las operaciones con datos de tipo GeoJSON vamos a trabajar con una colección de tiendas. Si queréis utilizar la colección en vuestro entorno de trabajo podéis descargar el archivo 2.Indices2dsphereCercania.zip y descomprimirlo. Luego ejecutáis mongo localhost:vuestroPuerto --shell ruta/cargaTiendas3d.js.
Prestad atención a utilizar el puerto en el que escucha vuestra instancia de MongoDB y la ruta en la que habéis descargado el archivo anterior. Si le echáis un vistazo veréis que cambia de base de datos, vacía la colección tiendas3d y luego la alimenta con 2000 documentos (localizaciones aleatorias entre -10 , 37 y 3, 43, y entre -55, -75 y 11, -35).
Índices 2dsphere . Cercanía
2 questions
09:06
Podemos crear índices compuestos en los que un campo sea de tipo 2dsphere. Vamos a ver ejemplos con nuestra colección de tiendas con datos de geolocalización de tipo GeoJSON Point.
Para consultar las tiendas de un determinado tipo que estén cerca de un punto, crearemos db.tiendas3d.ensureIndex( { localizacion : "2dsphere" , tipo : 1 } ) y db.tiendas3d.ensureIndex( { tipo : 1 , localizacion : "2dsphere" } ) para hacer pruebas
Índices 2dsphere. Índice compuesto
2 questions
06:01
Las operaciones de inclusión ($geoWithin) que podemos hacer con objetos GeoJSON son dos: inclusión en un polígono ($polygon), y en un círculo sobre la superficie de la Tierra ($centerSphere). En este último caso el parámetro de distancia se expresa en radianes.
En este caso la creación de un índice 2dsphere no es obligatoria, aunque su existencia debería mejorar el rendimiento de las operaciones de inclusión.
Índices 2dsphere. Inclusión
2 questions
06:34
Las operaciones de intersección ($geoIntersects) que podemos hacer con objetos GeoJSON son tres: intersección con un punto, una línea o un polígono. Para cada una de ellas tendremos que usar un objeto GeoJSON distinto (Point, LineString, Polygon) en el campo $geometry.
En este caso la creación de un índice 2dsphere no es obligatoria, aunque su existencia debería mejorar el rendimiento de las operaciones de intersección.
Índices 2dsphere. Intersección
3 questions
06:17
Para poder filtrar los datos de una colección (expresados como un array de pares de coordenadas x, y) por la cercanía a la localización necesitaremos crear un índice 2d.
Para ello podemos hacer db.tiendas2d.ensureIndex( { localizacion : "2d" } ). Al igual que en el caso 2dsphere los resultados se ordenan por distancia ascendente.
Los resultados sobre un índice 2d siempre se limitan por defecto a 100. Podemos cambiarlo con cursor.limit(). La máxima distancia en este caso se expresa en las mismas unidades utilizadas en los valores de x e y.
Para todas las operaciones con datos de tipo par de coordenadas x e y vamos a trabajar con una colección de tiendas. Si queréis utilizar la colección en vuestro entorno de trabajo podéis descargar el archivo 2.Indices2dCercania.zip y descomprimirlo. Luego ejecutáis mongo localhost:vuestroPuerto --shell ruta/cargaTiendas2d.js.
Prestad atención a utilizar el puerto en el que escucha vuestra instancia de MongoDB y la ruta en la que habéis descargado el archivo anterior. Si le echáis un vistazo veréis que cambia de base de datos, vacía la colección tiendas2d y luego la alimenta con 2000 documentos (localizaciones aleatorias entre -10 , 37 y 3, 43, y entre -55, -75 y 11, -35).
Índices 2d. Cercanía
2 questions
04:36
En el caso de contar con datos de localización en el formato de array de pares de coordenadas x,y, también podemos crear un índice 2dsphere.
De esta manera podemos utilizar la sintaxis de las búsquedas por cercanía de 2dsphere, en la que la máxima distancia se expresa en metros, los resultados no se limitan a 100 por defecto, y se utiliza geometría esférica para realizar los cálculos.
Índices 2d. Cercanía con 2dsphere
2 questions
03:28
En el caso 2d, sólo tenemos una posibilidad para crear un índice compuesto. Primero el campo de los datos de localización, y después un único campo no especial.
Por ejemplo, db.tiendas2d.ensureIndex( { localizacion : "2d" , tipo : 1 } )
Índices 2d. Índice compuesto
2 questions
10:19
Las operaciones de inclusión ($geoWithin) que podemos hacer con pares de coordenadas x,y son tres: inclusión en un polígono ($polygon), en un rectángulo ($box), o en un círculo sobre la superficie de la Tierra ($center).
En el caso del polígono ($polygon) no hay incluir dos veces el primer punto, como si hay que hacerlo en el caso de tener objetos GeoJSON.
En el caso del círculo las unidades son las mismas que las utilizadas en las coordenadas x e y, y se utiliza geometría plana para llevar a cabo los cálculos.
El uso de un índice 2d no es obligatorio en este caso, si bien debería mejorar el rendimiento.
Índices 2d. Inclusión
2 questions
09:24
Las operaciones de intersección ($geoIntersects) que podemos hacer con pares de coordenadas x, y son tres: intersección con un punto, una línea o un polígono. Para cada una de ellas tendremos que usar un objeto GeoJSON distinto (Point, LineString, Polygon) en el campo $geometry.
En este caso la creación de un índice 2dsphere es obligatoria, ya que los datos de localiazción no están en formato GeoJSON.
Índices 2d. Intersección
1 question
05:39
A partir de la versión 2.4 de MongoDB se pueden crear índices de texto, que mejoran el rendimiento de las consultas que filtran campos de tipo texto o de tipo array de textos.
Esta funcionalidad no está activada por defecto, si bien esto cambia a partir de la versión 2.6.
Para crear un índice de tipo texto haremos db.nombre_collecion.ensureIndex( { campo : "text" } ). El índice no tiene en cuenta palabras como a, el, en,...
Índices de texto. Introducción
3 questions
04:04
Se puede especificar el lenguaje que ha de utilizar el índice, con db.colección.ensureIndex( { campo : "text" } , { default_language : "spanish" } ). El idioma por defecto es el inglés.
Podemos incluir la información de idioma dentro del propio documento. Si existe un campo language en el documento se utilizará su valor.
Si queremos que se utilice otro campo para este fin haremos db.colección.ensureIndex( { campo : "text" } , { language_override : "idioma"} ). Para ver un listado de idiomas soportados tenemos http://docs.mongodb.org/manual/reference/command/text/#text-search-languages.
03:43
Sólo se puede tener un índice de texto por colección, aunque puede contar con varios campos de tipo texto. Incluso hay una manera de indexar todos los campos que contengan textos, sin necesidad de saber cuáles son, con db.colección.ensureIndex( { "$**" : "text" } ).
La creación y el mantenimiento de este tipo de índices es costosa. Utilizarlos con prudencia.
Cuando se crea un índice de texto sobre varios campos, se puede otorgar distinta importancia al hecho de encontrar el término de búsqueda en cada uno de ellos. Por defecto, cada campo tiene una importancia de 1.
03:20
Se pueden crear índices compuestos con este tipo de índices, pero no pueden coexistir con índices multiclave o geoespaciales, y en el caso de que otros campos preceden al de tipo texto en el índice, para filtrar por texto tiene que haber una condición de igualdad sobre los campos que preceden al de texto.
Índices de texto. Opciones
4 questions
09:55
Para los ejemplos de búsquedas en MongoDB 2.4 vamos a usar una colección con documentos que tienen un campo tipo de 1 a 5, y otro campo texto con un número aleatorio de palabras.Si queréis utilizar la colección en vuestro entorno de trabajo podéis descargar el archivo 2.IndicesTexto.Busquedas2.4.zip y descomprimirlo.
Luego ejecutáis mongo localhost:vuestroPuerto --shell ruta/cargaTextos.js. Prestad atención a utilizar el puerto en el que escucha vuestra instancia de MongoDB y la ruta en la que habéis descargado el archivo anterior.
Para realizar consultas utilizando los índices de texto hay que recurrir a la ejecución de un comando, cuyo resultado es un documento que tiene un campo resultados en el que se incluye un array de documentos que cumplen el criterio de búsqueda.
Por defecto los resultados se limitan a 100 documentos y se ordenan según un cómputo de lo bien que cumplen con el criterio. Veamos algunos ejemplos de uso del parámetro búsqueda.
Para buscar los documentos que contienen las palabras café o té, pero no leche en el campo texto haremos db.colección.runCommand( "text", { search: "café té -leche" } ).
12:45
A partir de la versión 2.6 se debe utilizar un operador en las consultas ($text), y se recomienda no utilizar el comando que hemos visto anteriormente. La sintaxis general es db.textos.find( { $text: { $search: búsqueda, $language: idioma } } ).
En este caso no se ordenan los resultados según un cómputo de lo bien que cumplen con el criterio, y no se limitan por defecto a 100 los resultados.
Se puede obtener el cómputo de lo bien que cumplen con el criterio, o incluso ordenar los resultados por ese cómputo.
Para buscar los documentos que contienen las palabras café o té, pero no leche en el campo texto haremos db.textos.find( { $text: { $search: "café té -leche" } } ).
Índices de texto. Búsquedas
4 questions
03:05
A partir de la versión 2.4 de MongoDB apareció un nuevo tipo de índice, de hash. Lo que hace es calcular un hash del valor del campo indexado. No funcionan bien para búsquedas por rango de valor.
Su utilidad principal está relacionada con el particionamiento, que veremos más adelante. No se recomienda su uso con números decimales, puesto que puede obtener el mismo hash para valores decimales próximos.
Se crea un índice hash con db.colección.ensureIndex( { campo: "hashed" } ).
Índices hash
1 question
08:19
Hasta la versión 2.4 de MongoDB, sólo se podía utilizar un índice en cada operación, salvo en el caso de utilizar $or. A partir de la versión 2.6 MongoDB es capaz de utilizar la intersección de índices. Es decir, puede utilizar varios índices para llevar a cabo una operación.
Vamos a ver como funciona la intersección de índices con una colección que ya hemos utilizado previamente. Si queréis utilizar la colección en vuestro entorno de trabajo podéis descargar el archivo 2.InterseccionIndices.zip y descomprimirlo.
Luego ejecutáis mongo localhost:vuestroPuerto --shell ruta/cargaPuntuaciones.js. Prestad atención a utilizar el puerto en el que escucha vuestra instancia de MongoDB y la ruta en la que habéis descargado el archivo anterior.
Supongamos que tenemos índices sobre los campos de tipo y puntuacion. Para recuperar los exámenes con una puntuación superior a 90, con db.puntuacion.find( { tipo : "examen" , puntuacion : { $gt : 90 } } ) MongoDB podría utilizar los dos índices y efectuar la intersección del resultado de aplicar ambos.
MongoDB puede decidir usar sólo uno de ellos. Analizará el coste de usar los dos, o uno de ellos, y decidirá cuál es el más eficiente. En la intersección de índices se puede utilizar un subconjunto cabecero de un índice compuesto.
Intersección de índices
1 question
03:32
Ya vimos que cada cierto tiempo MongoDB evalúa las alternativas que tiene para llevar a cabo una consulta. Es decir, prueba en paralelo la ejecución de la consulta con todos los índices candidatos para esa consulta y sin ellos.
Como resultado de este proceso, una de las alternativas es considerada la más adecuada, y se utiliza hasta que no vuelva a tener lugar una nueva evaluación. Esta información se almacena en la caché de planes de consulta (Query Plan Cache).
A partir de la versión 2.6 de MongoDB la ejecución de explain no elimina los planes de consulta de la operación en la caché. Además, podemos acceder a la información de la caché de planes de consulta. E incluso eliminar todos o algunos de los planes de consulta.
12:35
Podemos averiguar para qué operaciones realizadas sobre una colección existe un plan de consulta, es decir, ya se han evaluado las alternativas disponibles y se cuenta con información de cuál de ellas es la mejor.
Para hacer esto tenemos dos posibilidades: db.runCommand( { planCacheListQueryShapes: "colección" } ) ó db.colección.getPlanCache().listQueryShapes().
También podemos recuperar la información que está almacenada en la caché de planes de consulta para una determinada operación (db.colección.getPlanCache().getPlansByQuery).
O eliminar los planes de consulta de una operación (db.colección.getPlanCache().clearPlansByQuery) o de todas las que afectan a una colección (db.colección.getPlanCache().clear).
Caché de planes de consulta
2 questions
05:14
A partir de la versión 2.6 de MongoDB aparece lo que se conoce como filtros de índices. Sirven para limitar los índices que se evalúan antes de llevar a cabo una operación.
Con los filtros de índices lo que hacemos es limitar los índices que van a ser evaluados para una consulta determinada, que vendrá definida por el criterio de filtrado, la proyección, y el criterio de ordenación.
Cuando el servidor MongoDB se reinicia, lo hace sin filtros. Así que si queremos que sean permanentes, tendremos que crearlos cada vez que el servidor se reinicie.
Puesto que modifican el comportamiento estándar de MongoDB deben ser usados con cuidado. Aparece un campo nuevo al hacer un explain, filterSet. Si una operación utiliza un filtro de índices, este campo estará a true. Si no a false.
11:18
Existen una serie de comandos del shell que nos permiten gestionar los filtros de índices: planCacheListFilters, que recupera todos los filtros de índices existentes para una colección; planCacheSetFilter, que crea un filtro de índice para una colección; planCacheClearFilters, que elimina filtros de índices existentes para una colección.
Vamos a ver ejemplos de ellos con la colección de puntuaciones
Filtros de índices
3 questions
Section 3: Diseño de estructuras de datos
03:22
En esta sección veremos cómo se afronta en MongoDB el diseño de la estructura de nuestros datos, proceso muy distinto al que habitualmente se sigue en el mundo relacional.
Veremos cómo podemos contrarrestar la ausencia las características del mundo relacional que MongoDB no proporciona.
También conoceremos cómo se almacena la información en MongoDB, y echaremos un vistazo a dos alternativas de almacenamiento que se utilizan menos (colecciones limitadas y GridFS).
02:50
A menudo se asocia MongoDB con bases de datos no estructuradas (schemaless), pero en realidad cualquier aplicación que utilice MongoDB para almacenar la información requiere de un proceso de diseño de la base de datos.
El hecho de que los documentos de MongoDB no deban seguir una estructura rígida no significa que no la haya. Más bien deberíamos hablar de estructura flexible o dinámica.
Algunas de las partes de la estructura de datos son implícitas, mientras que otras son explícitas. Los documentos de una colección generalmente respetan una estructura, pero ésta no se ha de declarar previamente, ni se ha de seguir a rajatabla.
Los índices se crean de manera explícita, al igual que las claves de particionamiento (shard keys).
05:23
A la hora de diseñar una estructura de datos tendremos que decidir cuántas colecciones tendremos, cómo serán los documentos de cada colección, si incluimos documentos dentro de otros (embedding) o creamos una colección aparte (linking),...
Vamos a ver un poco más detenidamente lo que significa empotrar y enlazar documentos.
06:37
Los tres aspectos más importantes a la hora de diseñar nuestro modelo de datos son la forma en que se consultan los datos, la forma en que se particionan (sharding), y los requisitos de atomicidad.
Respecto a esto último hay que tener en cuenta que MongoDB no proporciona transacciones, sino que garantiza la atomicidad de las operaciones sólo a nivel de documento, es decir, todas las modificaciones que hagamos sobre un documento en una instrucción son atómicas.
Por esto a veces incluir documentos dentro de otros en vez de crear más colecciones, puede contrarrestar el hecho de que MongoDB no soporta transacciones.
Diseño de estructura de datos
5 questions
07:18
En el mundo relacional el diseño de la estructura de nuestros datos se lleva a cabo generalmente normalizando. Es decir, aplicando formas normales. Habitualmente se utilizan las tres primeras.
Vemos un pequeño ejemplo de normalización con los artículos de un blog.
Los objetivos básicos de la normalización son: evitar la redundancia de los datos, minimizar el rediseño de la estructura al extender, y evitar el favorecimiento de cualquier tipo de patrón de acceso. Es decir, la forma en la que se accede a los datos no se contempla a la hora de diseñar la estructura de datos.
07:05
El diseño del modelo de datos en MongoDB persigue unos objetivos similares a los de la normalización, salvo el último. Quizás el más importante, porque en MongoDB las forma en la que se acceden a los datos es uno de los principales factores a la hora de diseñar la estructura de datos.
En MongoDB también se ha de tratar de evitar la redundancia de datos, pero no es una obligación.
En cuanto al segundo objetivo, MongoDB favorece los cambios en la estructura de datos, ya que al no limitar la estructura de los documentos de una colección, pueden convivir en ella documentos con variadas estructuras sin causar graves problemas.
Eso sí la aplicación será la responsable de controlar si trabaja con los documentos que siguen una u otra estructura.
MongoDB y la normalización
2 questions
05:09
Veamos el ejemplo de una aplicación que almacena la información de una compañía de seguros (sólo la parte de seguros, no la de reclamaciones).
Se trata de pólizas, que cubren un bien, que cubren a una o varias partes, que cuentan con una o más coberturas, que guardan información histórica de sus precios, que cuentan con uno o más contratos, y en el que intervienen uno o más de los empleados de la aseguradora.
Para las partes intervinientes probablemente usaríamos otra colección, puesto que los clientes pueden aparecer en varias pólizas, y así evitamos redundancia y posibles problemas de inconsistencia. Para los empleados también podríamos tener otra colección, por los mismos motivos que explicamos en el caso de las partes intervinientes.
Las colecciones de partes intervinientes y empleados tendrían campos para datos identificativos (nombre, apellidos, fecha nacimiento,...) y algunos más, según el caso.
09:05
En el ejemplo de documento que representa una póliza, vemos que hay campos en los que se hace referencia al campo _id de otras colecciones. Es lo que se conoce en MongoDB como enlazado (linking).
En otros campos hemos incluido documentos que podrían haber estado en otras colecciones, como el bien asegurado o las coberturas. A esto se le conoce como empotrado (embedding).
¿Cuándo usar enlazado y cuándo empotrado? En general se debe recurrir al enlazado cuando el empotrado supusiera tener mucha redundancia. El empotrado permite realizar operaciones atómicas y acceder a toda la información sin echar de menos los joins.
Se puede ver el empotrado como una especie de join previo. Otra forma de entender el empotrado es como una desnormalización, en contraste con la normalización de datos tan tradicional en el mundo relacional.
08:19
Al incluir el objeto bien en la colección de pólizas se podría dar cierto grado de redundancia, por ejemplo, al tener que introducir datos de coches con los mismos campos, pero no es algo que suceda muy menudo.
Respecto al enlazado, hay que subrayar que MongoDB no fuerza ningún tipo de comprobación a la hora de introducir los valores de _id de las colecciones enlazadas.
En definitiva, cada documento de la colección pólizas cuenta con gran cantidad de información que puede ser recuperada o modificada en una única consulta.
Por otro lado, podríamos haber pensado en almacenar a los intervinientes y a los empleados en una misma colección, de personas por ejemplo, y para los empleados tener algo más de información (sigue en la empresa, cuándo empezó, departamento,...).
Desde un punto de vista relacional la información de intervinientes y empleados se habría almacenado en tablas muy similares a las colecciones que aquí se han propuesto. Pero respecto a las pólizas seguramente hubiésemos acabado con varias tablas.
07:41
La ausencia de joins en MongoDB se resuelve principalmente incluyendo documentos en otros. Si empotramos, y hay que consultar dos o más colecciones simultáneamente, la aplicación tendrá que encargarse de realizar el join ella misma, lo que generalmente no ofrecerá un buen rendimiento.
Si tenemos que enlazar un documento con otro, MongoDB no va a hacer ninguna comprobación respecto a si el campo utilizado para enlazar existe en la otra colección. Pero fijaros que cuando incluimos un documento en otro no hay ninguna comprobación que realizar.
Si queremos que determinados grupos de acciones sean atómicas, han de afectar a un único documento. Por eso es muy importante tener en cuenta los requisitos de atomicidad de nuestra aplicación a la hora de diseñar su modelo de datos.
Si tenemos requisitos de atomicidad que afectan a operaciones sobre documentos distintos las únicas alternativas que nos quedan son tolerar su ausencia o implementar la atomicidad de operaciones en la aplicación.
Respecto a la implementación de las transacciones en la aplicación hay un ejemplo típico de movimientos de cuentas bancarias en http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/.
Vivir sin joins ni transacciones
3 questions
08:16
Supongamos el currículum de una persona. Cada curriculum pertenece a un persona, y cada persona tiene un único currículum. En este caso podríamos pensar en tener una única colección de personas.
Otra opción sería separar los datos de identificación y el currículum en dos colecciones. Con esto perderíamos la posibilidad de realizar operaciones atómicas. Si parte de la información que vamos a guardar en el currículum puede superar los 16MB, deberíamos almacenarla en otra colección.
Por otro lado, sí lo normal es que se acceda de manera separada a la información de identificación de la persona y a su curriculum, puede ser conveniente guardarlas en colecciones distintas, sobre todo si se accede más a menudo sólo a la información de identificación de la persona.
En ese caso, y si tuviésemos todo en una colección, cada vez que queremos recuperar la información de identificación de la persona, estamos trabajando con una colección en la que mayoría de información es del curriculum, y no nos es de utilidad.
05:52
Ahora veamos el caso de los datos identificativos de los clientes de un gimnasio. Cada cliente vive en una ciudad, y en una ciudad pueden vivir varios clientes. Podemos tener una única colección de personas, o la anterior más una de ciudades en la que guardamos el nombre y provincia de la ciudad de cada cliente.
En el primer caso lo normal es que tengamos mucha redundancia de datos en el campo de ciudad y provincia de los clientes, ya que en cada uno de los documentos tenemos el campo ciudad y provincia.
En el segundo caso, en los documentos de la colección clientes tendremos un campo ciudad con un código o el nombre. La redundancia se ha reducido.
Si no vamos a recuperar a menudo los datos del cliente y el nombre de la ciudad y la provincia en la que vive, lo lógico sería enlazar. Así evitaremos posibles anomalías en el nombre de la ciudad y la provincia. En este caso mantener la integridad referencial entre ambas colecciones es responsabilidad de la aplicación.
05:44
Veamos los comentarios de un artículo de un blog. Un artículo de un blog puede tener varios comentarios, y un comentario es de un sólo artículo del blog. Es una relación de uno a varios, como en el ejemplo de los clientes del gimnasio.
Sin embargo hay algunas diferencias. En primer lugar, es muy improbable que dos comentarios distintos sean iguales, mientras que es probable que dos clientes vivan en la misma ciudad. Por lo tanto, vamos a tener muy poca redundancia.
Además el número de comentarios de un artículo de un blog no es un número muy elevado, en general, mientras que los clientes que viven en una ciudad pueden ser muchos.
Y por último al recuperar la información de un artículo del blog querremos también los comentarios de éste. Por lo tanto, en este caso lo normal sería incluir los datos de cada comentario en el documento de cada artículo del blog.
06:56
Un ejemplo adicional es el de libros de una editorial. Un libro es publicado por una editorial, y una editorial edita varios libros. En este caso volvemos a tener redundancia, ya que los datos de la editorial de un montón de libros son exactamente iguales.
Por tanto, en principio lo lógico sería enlazar. Pero, ¿en qué sentido? Es decir, ¿incluimos en cada libro un identificador de editorial, o incluimos en cada editorial un array de identificadores de libros?
El patrón de acceso debería ayudarnos a tomar una decisión, pero introducimos en este caso un nuevo factor a tener en cuenta. En el primer caso tenemos un campo editorial que es prácticamente inmutable, y aunque cambiase pasaría de ser un identificador a otro (casi lo mismo).
En el segundo caso tenemos un array de identificadores de libros que va haciéndose más grande conforme la editorial saca nuevos libros, y más pequeño si eliminamos los libros descatalogados.
En definitiva un campo que va variando, creciendo y decreciendo de tamaño. Algo que puede provocar diversos movimientos en los documentos, lo que siempre se debe intentar evitar.
10:33
El ejemplo que veremos es el de los autores de un libro. Un libro puede tener varios autores, y un autor puede escribir varios libros. En este caso no es aconsejable incluir documentos, ya que se trataría de incluir el mismo documento (autor o libro) en varios documentos (libro o autor). Hay redundancia y posibilidad de anomalías en la actualización.
En todo caso, incluir documentos no es algo totalmente descartable. Si se decide enlazar, para decidir en qué sentido hacerlo tendremos que pensar en los patrones de acceso de la aplicación, y en lo que comentamos previamente sobre los movimientos de documentos.
En resumen, el mayor beneficio del empotrado frente al enlazado es la mejora de rendimiento. Recuperamos toda la información necesaria consultando una única colección en una única operación.
Uno de los motivos por los que el enlazado y el empotrado funcionan bien en MongoDB es por la existencia de los índices multiclave.
Relaciones en MongoDB
5 questions
06:10
Vamos a trabajar con una estructura de datos habitual: la estructura de árbol. Pongamos por caso la categoría de libros de Informática. Como primera opción vamos a probar almacenando en cada categoría una referencia a la categoría padre.
Esta opción simplifica la forma de crear y modificar el árbol, pero requiere de varias consultas para obtener una rama del árbol. Además una categoría no puede tener más de un padre.
05:28
Vamos a trabajar con una estructura de datos habitual: la estructura de árbol. Pongamos por caso la categoría de libros de Informática. Como segunda opción vamos a probar a almacenar en cada categoría un array con todas las referencias de las categorías hijas.
Esta opción también requiere de varias consultas para obtener una rama del árbol. En este caso una categoría puede tener más de un padre.
04:57
Vamos a trabajar con una estructura de datos habitual: la estructura de árbol. Pongamos por caso la categoría de libros de Informática. Como tercera opción vamos a probar a almacenar en cada categoría un array con todas las referencias de las categorías de su árbol por orden jerárquico.
Esta opción es muy útil para trabajar con subárboles. Es bastante eficiente para averiguar los descendientes y los ancestros de una categoría.
05:40
Vamos a trabajar con una estructura de datos habitual: la estructura de árbol. Pongamos por caso la categoría de libros de Informática. Como cuarta y última opción vamos a probar a almacenar en cada categoría una cadena con todas las referencias de las categorías de su árbol por orden jerárquico, separadas por una coma.
Esta opción es muy útil para trabajar con subárboles, siendo algo más rápida que la anterior. Es bastante eficiente para averiguar los descendientes y los ancestros de una categoría.
04:44
En MongoDB los documentos se guardan en disco alojados en registros. Cada documento ocupa un registro. El tamaño del registro puede ser mayor que el del documento, lo que le permitiría crecer sin tener que cambiar de registro (moverse).
En MongoDB hay que tratar de minimizar los movimientos de documentos. Los registros se almacenan de manera contigua en los archivos del disco de la colección.
Las colecciones se agrupan en bases de datos. Cuando en la colección de una base de datos tiene lugar una operación de escritura, se bloquea toda la base de datos, y no se pueden realizar otras operaciones mientras tanto.
07:37
La forma de decidir el tamaño del registro viene dada por el tipo de estrategia que escojamos. Existen dos.
La primera intenta que el espacio extra del que dispone el documento sea el menor posible (Exact Fit Allocation). Intenta adaptarse al tamaño del documento, más un pequeño espacio extra para posteriores modificaciones. Es la que se usaba por defecto en la versión 2.4 de MongoDB.
La segunda restringe el tamaño del registro a una potencia de 2, con un mínimo de 32 bytes (Use Power Of 2 Sizes). Esto hace que para MongoDB sea más fácil reusar registros existentes, lo que puede disminuir la fragmentación. Además reduce el número de operaciones que provocan movimientos.
Aunque usa más espacio extra, y por tanto, las colecciones ocupan más en disco. En la versión 2.6 ha pasado a ser la estrategia por defecto.
05:51
En versiones 2.2 y 2.4 de MongoDB, si queremos que una colección use la estrategia Use Power Of 2 Sizes, tendremos que hacer db.runCommand( { collMod: "colección", usePowerOf2Sizes : true } ). O podemos hacerlo al crearla, con db.createCollection("colección", { usePowerOf2Sizes : true } ).
En la versión 2.6 podemos hacer que todas las colecciones vuelvan a utilizar la estrategia Exact Fit Allocation con db.getSiblingDB('admin').runCommand( { setParameter : 1, newCollectionsUsePowerOf2Sizes : false } ) o con mongod --setParameter newCollectionsUsePowerOf2Sizes=false al arrancar el servidor.
Si queremos que una colección la utilice podemos hacer db.runCommand( { collMod: "colección", usePowerOf2Sizes : false } ), o db.createCollection("colección", { usePowerOf2Sizes : false } ).
Almacenamiento en MongoDB
6 questions
10:33
Las colecciones limitadas (capped collections) tienen un tamaño fijo y se crean de manera explícita. Desde el principio se ocupa todo el tamaño de la colección, aunque no haya documentos suficientes. También existe la posibilidad de limitar el número máximo de documentos.
No podemos borrar documentos, ni efectuar actualizaciones que hagan que el documento crezca, ni particionar estas colecciones. Las inserciones se guardan en el disco en el mismo orden en el que se hicieron.
De este modo la inserción y la consulta de documentos es más rápida. Cuando están llenas y llega una nueva inserción, se eliminan de manera automática y por orden decreciente de antigüedad, tantos documentos como sea necesario para llevarla a cabo.
Podemos convertir a limitada una colección o averiguar si una colección lo es.
Colecciones limitadas
2 questions
06:17
GridFS nos permite guardar objetos grandes en MongoDB, a pesar de la limitación de 16MB que tiene un documento BSON. GridFS divide el objeto grande en trozos de 256KB por defecto (255KB desde la versión 2.4.10).
Utiliza dos colecciones, una para almacenar los trozos (fs.chunks) y otra para guardar información acerca de dónde guarda cada trozo (fs.files).
La colección de trozos tiene un índice sobre los campos files_id y n. El primero hace referencia al identificador del fichero, mientras que el segundo hace referencia al número de secuencia del trozo.
No se recomienda su uso si hay que actualizar el contenido completo de los ficheros de manera atómica, ya que cada trozo del fichero estará en un documento de la colección fs.chunks.
07:24
MongoDB cuenta con una utilidad, mongofiles, que nos permite trabajar desde la consola con GridFS. Podemos guardar archivos, recuperar un listado de archivos o los propios archivos, e incluso eliminar archivos.
En la consola de MongoDB, para consultar lo que se ha guardado en la colección files haremos db.fs.files.find().pretty(). Para consultar lo que tenemos en la colección chunks haremos db.fs.chunks.findOne( { } , { data : 0 } ) para ver la información de un trozo, y db.fs.chunks.find().count() para ver cuántos trozos tenemos.
07:48
En el archivo 3.GridFS.NodeJS.zip tenéis el código de un ejemplo NodeJS que utiliza GridFS. Este ejemplo no es adecuado para trabajar con archivos muy grandes podemos tener problemas con la memoria, ya que se cargan completos en ella.
En estos casos utilizaremos otras soluciones. En el siguiente enlace tenéis un ejemplo http://mongodb.github.io/node-mongodb-native/api-articles/nodekoarticle2.html#advanced-gridfs-or-how-not-to-run-out-of-memory
GridFS
5 questions
Section 4: Agregación
06:09
En MongoDB existen cuatro alternativas para realizar este tipo de operaciones. La menos potente de todas es la agregación mediante operaciones de propósito simple. No vamos a profundizar más ya que apenas se utiliza (versiones anteriores a la 2.2).
El sistema de agregación de MongoDB, introducido en la versión 2.2, funciona con el modelo de tuberías, de modo que los documentos de una única colección pasan a través de varias fases, que los van modificando, filtrando o agrupando.
MongoDB incluye una implementación de MapReduce que también trabaja sobre los documentos de una única colección. A groso modo podríamos decir que MapReduce hace lo mismo que el sistema de agregación de MongoDB, y además puede hacer operaciones más complejas.
A parte de la implementación que MongoDB incorpora de MapReduce, también existe la posibilidad de que MongoDB actúe como entrada o salida de Hadoop, una implementación de código abierto de MapReduce mantenida por Apache.
03:04
En general la recomendación de MongoDB es empezar con el sistema de agregación. Si la agregación que queremos hacer no se puede expresar fácilmente con el sistema de agregación, deberíamos probar con la implementación MapReduce de MongoDB.
Si tenemos que hacer un uso intensivo de agregación deberíamos pensar en el conector MongoDB para Hadoop. Este último es el único que realmente puede trabajar de manera paralela, puesto que MapReduce de MongoDB se ejecuta en un único hilo.
En producción y para uso intensivo de MapReduce, Hadoop debe ser la primera opción.
Alternativas de agregación en MongoDB
3 questions

Students Who Viewed This Course Also Viewed

  • Loading
  • Loading
  • Loading

Instructor Biography

José Antonio Sánchez Ortiz, Programador web y formador técnico

Ingeniero Superior de Telecomunicaciones, casi desde los inicios de mi vida laboral (finales de los 90) me especialice en acceder a bases de datos desde Internet, lo que hoy probablemente se conoce más como programador web.

Fundamentalmente he utilizado la plataforma LAMP, aunque en los últimos años estoy incorporando nuevas tecnologías a mi curriculum (Nginx, MongoDB, NodeJS,...).

También he impartido cursos relacionados con la informática (programación de aplicaciones, office, internet,...)

Ready to start learning?
Take This Course