Usar Referencias en MongoDB

MongoDBBeginner
Practicar Ahora

Introducción

En este laboratorio, aprenderá a utilizar referencias de MongoDB para modelar relaciones entre datos. Construirá un sistema simple de gestión de bibliotecas con colecciones de authors (autores) y books (libros). A través de pasos prácticos, aprenderá a crear documentos, enlazarlos usando referencias, consultar datos relacionados entre colecciones, actualizar estas referencias y mejorar el rendimiento de las consultas con índices. Este laboratorio proporciona una base práctica para el modelado de datos en MongoDB.

Crear Colecciones y Documentos de Referencia

En este paso, configurará su base de datos y creará dos colecciones: authors (autores) y books (libros). Aprenderá el concepto fundamental de referencias de documentos enlazando un libro a su autor.

Primero, abra la Shell de MongoDB. Esta shell interactiva es donde ejecutará todos sus comandos de base de datos.

mongosh

Una vez dentro de la shell, verá un prompt test>. Cambie a una nueva base de datos llamada library_db. Si la base de datos no existe, MongoDB la creará cuando almacene datos por primera vez.

use library_db

Ahora, cree su primer autor. Inserte un documento en la colección authors. Estamos especificando un _id personalizado para este autor para que sea fácil referenciarlo más tarde.

db.authors.insertOne({
    _id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    name: "Jane Austen",
    nationality: "British",
    birthYear: 1775
})

A continuación, inserte un documento en la colección books. El campo author_id contiene el ObjectId del autor que acaba de crear. Así es como se crea una referencia.

db.books.insertOne({
    title: "Pride and Prejudice",
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    published: 1813,
    genre: "Classic Literature"
})

Ahora ha creado una relación uno a uno. Para verificar esto, puede recuperar los documentos que acaba de crear.

Primero, busque al autor:

db.authors.findOne({ name: "Jane Austen" })

Salida de Ejemplo:

{
  _id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
  name: 'Jane Austen',
  nationality: 'British',
  birthYear: 1775
}

Ahora, busque el libro y observe el campo author_id, que enlaza con el autor.

db.books.findOne({ title: "Pride and Prejudice" })

Salida de Ejemplo:

{
  _id: ObjectId("..."),
  title: 'Pride and Prejudice',
  author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
  published: 1813,
  genre: 'Classic Literature'
}

Puede permanecer en la shell mongosh para los siguientes pasos.

Enlazar Múltiples Documentos

Un autor normalmente escribe más de un libro. En este paso, aprenderá a enlazar múltiples documentos "hijo" (libros) a un único documento "padre" (autor). Esto demuestra una relación uno a muchos.

Continúe trabajando en la shell mongosh. Añadamos dos libros más de Jane Austen. Utilice el comando insertMany para insertar múltiples documentos a la vez. Ambos libros nuevos harán referencia al mismo author_id.

db.books.insertMany([
    {
        title: "Sense and Sensibility",
        author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
        published: 1811,
        genre: "Classic Literature"
    },
    {
        title: "Emma",
        author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
        published: 1815,
        genre: "Classic Literature"
    }
])

Ahora que Jane Austen tiene tres libros en nuestra base de datos, recupérelos todos utilizando el método find() y filtrando por author_id.

db.books.find({ author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1") })

Salida de Ejemplo:

[
  {
    _id: ObjectId("..."),
    title: 'Pride and Prejudice',
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    published: 1813,
    genre: 'Classic Literature'
  },
  {
    _id: ObjectId("..."),
    title: 'Sense and Sensibility',
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    published: 1811,
    genre: 'Classic Literature'
  },
  {
    _id: ObjectId("..."),
    title: 'Emma',
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
    published: 1815,
    genre: 'Classic Literature'
  }
]

También puede contar rápidamente cuántos libros están asociados con un autor específico utilizando countDocuments.

db.books.countDocuments({ author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1") })

Salida de Ejemplo:

3

Esta simple consulta confirma eficientemente el número de documentos enlazados.

Consultar Entre Colecciones con $lookup

Hasta ahora, ha recuperado libros utilizando un author_id conocido. Un enfoque más potente es combinar datos de ambas colecciones en una única consulta. En este paso, utilizará la etapa de agregación $lookup para realizar una unión externa izquierda (left outer join) desde la colección books a la colección authors.

Primero, añadamos otro autor y un libro para hacer nuestra consulta más interesante.

db.authors.insertOne({
    _id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2"),
    name: "Charles Dickens",
    nationality: "British",
    birthYear: 1812
})
db.books.insertOne({
    title: "Oliver Twist",
    author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2"),
    published: 1837,
    genre: "Historical Fiction"
})

Ahora, construya un pipeline de agregación. Esta consulta comenzará con la colección books y "buscará" el autor correspondiente para cada libro.

db.books.aggregate([
    {
        $lookup: {
            from: "authors",
            localField: "author_id",
            foreignField: "_id",
            as: "author_details"
        }
    }
])

La etapa $lookup tiene los siguientes campos:

  • from: "authors": Especifica la colección con la que se unirá.
  • localField: "author_id": El campo de los documentos de entrada (de books).
  • foreignField: "_id": El campo de los documentos de la colección "from" (de authors).
  • as: "author_details": El nombre del nuevo campo de array que se añade a los documentos de entrada.

Salida de Ejemplo (para un documento):

{
  _id: ObjectId("..."),
  title: 'Pride and Prejudice',
  author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
  published: 1813,
  genre: 'Classic Literature',
  author_details: [
    {
      _id: ObjectId("6633c9a5b4e3e8a5c8a8f8b1"),
      name: 'Jane Austen',
      nationality: 'British',
      birthYear: 1775
    }
  ]
}

Como puede ver, la información del autor ahora está incrustada dentro de cada documento de libro bajo el campo author_details. Esto le permite consultar campos de ambas colecciones simultáneamente.

Actualizar y Mantener Referencias

Los datos no son siempre estáticos. Es posible que necesite corregir errores o eliminar datos, lo que requiere actualizar o eliminar documentos y sus referencias. En este paso, aprenderá a actualizar una referencia y a manejar documentos "huérfanos".

Imagine que descubre que el libro "Emma" fue atribuido erróneamente a Jane Austen y debería asignarse a Charles Dickens. Puede corregir esto utilizando el comando updateOne con el operador $set.

db.books.updateOne(
    { title: "Emma" },
    { $set: { author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2") } }
)

Verifique el cambio buscando nuevamente el libro y comprobando su author_id.

db.books.findOne({ title: "Emma" })

Salida de Ejemplo:

{
  _id: ObjectId("..."),
  title: 'Emma',
  author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2"),
  published: 1815,
  genre: 'Classic Literature'
}

Ahora, exploremos qué sucede cuando se elimina un documento padre. Si eliminamos un autor, cualquier libro que haga referencia a ese autor se convierte en "huérfano". Eliminemos a Charles Dickens de nuestra base de datos.

db.authors.deleteOne({ name: "Charles Dickens" })

El documento del autor ya no existe, pero los libros "Emma" y "Oliver Twist" todavía tienen un author_id que apunta al autor eliminado. Esto puede causar problemas de integridad de datos. En una aplicación real, implementaría lógica para manejar esto, como eliminar los libros huérfanos o reasignarlos.

Para este laboratorio, realizaremos una limpieza manual eliminando los dos libros huérfanos.

db.books.deleteMany({ author_id: ObjectId("6633c9a5b4e3e8a5c8a8f8b2") })

Este comando elimina todos los documentos de la colección books que hacen referencia al autor ahora eliminado, asegurando que nuestros datos permanezcan consistentes.

Mejorar el Rendimiento de Consultas con Índices

Cuando sus colecciones crecen, las consultas que filtran por un campo específico pueden volverse lentas. Esto se debe a que MongoDB tiene que escanear cada documento para encontrar coincidencias. Para optimizar esto, puede crear un índice en los campos que consulta con frecuencia. En nuestro caso, author_id en la colección books es un candidato perfecto.

En este paso, creará un índice en el campo author_id para acelerar las búsquedas de los libros de un autor.

Utilice el comando createIndex en la colección books. El argumento { author_id: 1 } le indica a MongoDB que cree un índice ascendente en el campo author_id.

db.books.createIndex({ author_id: 1 })

MongoDB procesará esto en segundo plano. Una vez completado, devolverá un mensaje confirmando que el índice se ha creado.

Salida de Ejemplo:

{
  "numIndexesBefore": 1,
  "numIndexesAfter": 2,
  "createdCollectionAutomatically": false,
  "ok": 1
}

Para verificar que el índice existe, puede usar el comando getIndexes. Esto listará todos los índices en la colección books.

db.books.getIndexes()

Debería ver dos índices: el índice predeterminado en _id y el nuevo índice author_id_1 que acaba de crear.

Salida de Ejemplo:

[
  { "v": 2, "key": { "_id": 1 }, "name": "_id_" },
  { "v": 2, "key": { "author_id": 1 }, "name": "author_id_1" }
]

Con este índice implementado, cualquier consulta que filtre o ordene por author_id, incluida la etapa $lookup que utilizó anteriormente, será significativamente más rápida en conjuntos de datos grandes.

Finalmente, puede salir del shell de MongoDB.

exit

Resumen

En este laboratorio, ha aprendido los fundamentos del uso de referencias de documentos en MongoDB. Comenzó creando colecciones y enlazando documentos con referencias ObjectId. Luego practicó la gestión de relaciones uno a muchos, la consulta entre colecciones utilizando la potente etapa de agregación $lookup y el mantenimiento de la integridad de los datos actualizando y limpiando referencias. Finalmente, mejoró el rendimiento de las consultas creando un índice en un campo de referencia. Estas habilidades son esenciales para construir aplicaciones escalables y eficientes con MongoDB.