17 - Documentos embebidos: definición de campos de arreglo con elementos de tipo documento

Una de las ventajas fundamentales del gestor de base de datos MongoDB es la posibilidad de crear esquemas de documentos muy flexibles y no tener que guardar datos relacionados en distintos documentos.

Desde los primeros ejemplos hemos visto que podemos definir campos en un documento de tipo arreglo:

db.libros.insertOne(
  {
    codigo: 1,  
    nombre: 'El aleph',
    autor: 'Borges',
    editoriales: ['Planeta','Siglo XXI']
  }
)

El campo 'editoriales' es de tipo arreglo y almacena dos cadenas de caracteres.

Podemos definir campos de tipo arreglo y almacenar en los mismos documentos embebidos, por ejemplo:

use base1
db.posts.drop()

db.posts.insertOne(
  {
    _id: 1,
    titulo: 'Lenguaje Java',
    contenido: 'Uno de los lenguajes más utilizados es ...',
    comentarios: [{
        autor: 'Marcos Paz',
        mail: 'pazm@gmail.com',
        contenido: 'Me parece un buen...'
    },
    {
        autor: 'Ana Martinez',
        mail: 'martineza@gmail.com',
        contenido: 'Todo ha cambiado en...'
    },
    {
        autor: 'Luiz Blanco',
        mail: 'blancol@outlook.com',
        contenido: 'Afirmo que es...'
    }]
  }
)

db.posts.find().pretty()

El campo comentarios almacena un arreglo de tres elementos, cada elemento es un documento.

La ejecución de este bloque:

MongoDB documentos embebidos con arreglos

La idea fundamental en MongoDB es disponer de todos los datos agrupados dependiendo de los accesos futuros a la información, es evidente que cuando queremos acceder a una entrada de un bloq también necesitemos acceder a los comentarios de dicha entrada.

Esta forma de organizar los datos nos permite implementar aplicaciones muy eficientes en comparación al modelo de base de datos relacional (MySQL, Oracle, SQL Server etc.)

Podemos generar documentos embebidos con más niveles, por ejemplo definir un campo en el arreglo que sea de tipo documento.

Consultas de los documentos embebidos

Ejecutemos el siguiente bloque de comandos en MongoDB para analizar las consultas en documentos embebidos dentro de arreglos:

use base1
db.posts.drop()

db.posts.insertOne(
  {
    _id: 1,
    titulo: 'Lenguaje Java',
    contenido: 'Uno de los lenguajes más utilizados es ...',
    comentarios: [{
        autor: 'Marcos Paz',
        mail: 'pazm@gmail.com',
        contenido: 'Me parece un buen...'
    },
    {
        autor: 'Ana Martinez',
        mail: 'martineza@gmail.com',
        contenido: 'Todo ha cambiado en...'
    },
    {
        autor: 'Luiz Blanco',
        mail: 'blancol@outlook.com',
        contenido: 'Afirmo que es...'
    }]
  }
)

db.posts.insertOne(
  {
    _id: 2,
    titulo: 'Lenguaje C#',
    contenido: 'Microsoft desarrolla el lenguaje C# con el objetivo ...',
    comentarios: [{
        autor: 'Pablo Rodriguez',
        mail: 'rodriguezp@gmail.com',
        contenido: 'Correcta idea.'
    },
    {
        autor: 'Maria Contreras',
        mail: 'contrerasm@gmail.com',
        contenido: 'Buen punto de vista...'
    }]
  }
)

db.posts.find({},{_id:0,titulo:1,'comentarios.autor':1}).pretty()

db.posts.find({'comentarios.autor':'Pablo Rodriguez'}).pretty()

db.posts.find({'comentarios.0.autor':'Pablo Rodriguez'}).pretty()

Si queremos recuperar los títulos de todos los posts y los nombres de los usuarios que hicieron comentarios en cada post, debemos implementar la siguiente consulta:

db.posts.find({},{_id:0,titulo:1,'comentarios.autor':1}).pretty()

Y como resultado tenemos:

MongoDB documentos embebidos con arreglos

Para recuperar todos los posts donde ha comentado el usuario 'Pablo Rodriguez':

db.posts.find({'comentarios.autor':'Pablo Rodriguez'}).pretty()

Y como resultado tenemos:

MongoDB documentos embebidos con arreglos

Podemos utilizar el subíndice del arreglo para analizar un documento en particular, por ejemplo si queremos recuperar todos los posts que comentó primero 'Rodriguez Pablo':

db.posts.find({'comentarios.0.autor':'Pablo Rodriguez'}).pretty()

Acotaciones

Cuando definimos documentos embebidos debemos tener en cuenta que un documento en MongoDB no puede tener un tamaño mayor a 16MB.

No podemos crear una colección 'ciudades' y almacenar documentos con el nombre de la ciudad y embeber documentos con los nombres de todos sus ciudadanos (podría haber millones por cada ciudad)

En el ejemplo de la colección 'posts' los comentarios por cada post serán una cantidad limitada y no millones.

Cuando uno define el esquema de los documentos a almacenar en MongoDB debe tener en cuenta cuanto pueden crecer los documentos embebidos para tomar la decisión de separar los mismos en documentos independientes.

Problemas propuestos

  1. Crear la colección 'libros' en la base de datos 'base1' (eliminar la colección previamente), cargar luego 4 documentos:

    db.series.insertOne(
      {
        _id: 1,  
        titulo: 'The big bang theory',
        productor: 'Chuck Lorre',
        actores: ['Johnny Galecki','Jim Parsons','Kaley Cuoco','Kunal Nayyar','Simon Helberg','Mayim Bialik','Melissa Rauch'],
        temporada1: [
          {
             capitulo1:{
               titulo:'Piloto',
               audiencia:8300000
             }
          },
          {
            capitulo2:{
               titulo:'La hipótesis del Gran Cerebro',
               audiencia:8700000
            },
          },
          {
            capitulo3:{
               titulo:'El Corolario de el Gato con Botas',
               audiencia:9200000
            }
          }
        ],
        temporada2: [
          {
            capitulo1:{
               titulo:'El paradigma del pescado malo',
               audiencia:10000000
            }
          },
          {
            capitulo2:{
               titulo:'La topología de la bragueta',
               audiencia:11000000
            }
          }
        ]
      }
    )
    
    db.series.insertOne(
      {
        _id: 2,  
        titulo: 'The Walking Dead',
        productor: 'Robert Kirkman',
        actores: ['Andrew Lincoln','Jon Bernthal','Sarah Wayne Callies','Laurie Holden','Jeffrey DeMunn','Steven Yeun'],
        temporada1: [
          {
            capitulo1:{
               titulo:'TS 19',
               audiencia:7000000
            }
          },
          {
            capitulo2:{
               titulo:'Wildfire',
               audiencia:8200000
            }
          },    
          {
            capitulo3:{
               titulo:'Díselo a las ranas',
               audiencia:9100000
            }
          }
        ],
        temporada2: [
          {
            capitulo1:{
               titulo:'Lo que queda por delante',
               audiencia:12000000
            }
          },
          {
            capitulo2:{
               titulo:'Sangría',
               audiencia:13000000
            }
          }
        ]
      }
    )
    
  2. Imprimir todos los documentos de la colección 'series'.

  3. Imprimir los datos de todas las temporadas de la serie 'The big bang theory'.

  4. Imprimir los datos de todos los capítulos de la primer temporada de la serie de 'The big bang theory'.

  5. Imprimir solo el primer capítulo de la primer temporada de 'The Walking Dead'.

Solución
use base1
db.series.drop()

db.series.insertOne(
  {
    _id: 1,  
    titulo: 'The big bang theory',
    productor: 'Chuck Lorre',
    actores: ['Johnny Galecki','Jim Parsons','Kaley Cuoco','Kunal Nayyar','Simon Helberg','Mayim Bialik','Melissa Rauch'],
    temporada1: [
      {
         capitulo1:{
           titulo:'Piloto',
           audiencia:8300000
         }
      },
      {
        capitulo2:{
           titulo:'La hipótesis del Gran Cerebro',
           audiencia:8700000
        },
      },
      {
        capitulo3:{
           titulo:'El Corolario de el Gato con Botas',
           audiencia:9200000
        }
      }
    ],
    temporada2: [
      {
        capitulo1:{
           titulo:'El paradigma del pescado malo',
           audiencia:10000000
        }
      },
      {
        capitulo2:{
           titulo:'La topología de la bragueta',
           audiencia:11000000
        }
      }
    ]
  }
)

db.series.insertOne(
  {
    _id: 2,  
    titulo: 'The Walking Dead',
    productor: 'Robert Kirkman',
    actores: ['Andrew Lincoln','Jon Bernthal','Sarah Wayne Callies','Laurie Holden','Jeffrey DeMunn','Steven Yeun'],
    temporada1: [
      {
        capitulo1:{
           titulo:'TS 19',
           audiencia:7000000
        }
      },
      {
        capitulo2:{
           titulo:'Wildfire',
           audiencia:8200000
        }
      },    
      {
        capitulo3:{
           titulo:'Díselo a las ranas',
           audiencia:9100000
        }
      }
    ],
    temporada2: [
      {
        capitulo1:{
           titulo:'Lo que queda por delante',
           audiencia:12000000
        }
      },
      {
        capitulo2:{
           titulo:'Sangría',
           audiencia:13000000
        }
      }
    ]
  }
)

db.series.find().pretty()

db.series.find({titulo:'The big bang theory'}).pretty()

db.series.find({titulo:'The big bang theory'},{'temporada1':1}).pretty()

db.series.find({titulo:'The Walking Dead'},{'temporada1.capitulo1':1}).pretty()