Cuando modelamos datos en MongoDB, una de las primeras decisiones es elegir entre Embedding o Referencing.
Ninguna opción es mejor. Depende principalmente de cómo vamos a leer y modificar nuestros datos.
1. Embedding
Con embedding, guardamos los datos directamente dentro del documento.
Por ejemplo:
Collection:Users
{
_id: 1,
name: "Nicolas",
address: {
city: "Buenos Aires",
country: "Argentina"
}
}
El campo address forma parte del documento User, por lo que NO necesitamos almacenarla en otra collection.
Conviene usar embedding cuando:
- Los datos son pequeños
- Normalmente los necesitamos juntos
- Queremos obtener todo con una sola consulta
- Los datos no van a ser utilizados por múltiples documentos
2. Referencing
Con referencing, guardamos el ID de otro documento.
Collection:Employees
{
_id: 1,
name: "Nicolas",
companyId: ObjectId("1")
}
Y la empresa vive en otra collection:
Collection:Companies
{
_id: ObjectId("1"),
name: "Amplify"
}
La relación sería:
Conviene usar referencing cuando:
- La entidad relacionada tiene su propio ciclo de vida
- Muchos documentos utilizan la misma información
- Requiere de una actualización independiente
También hay que tener en cuenta cómo cambian los datos.
Si muchos documentos comparten la misma información y queremos mantener una única fuente de verdad, referencing suele tener más sentido
3. Desnormalización
Usar referencing no significa que no podamos duplicar información
Por ejemplo:
Imaginemos que tenemos dos collections: una de empleados (Employees) y otra de Empresas (Companies)
Collection:Employees
{
_id: 1,
name: "Nicolas",
companyId: ObjectId("..."), // id de referencia
companyName: "Amplify"
}
Seguimos teniendo la referencia a Company, pero también guardamos el nombre de la empresa en el documento.
Esto se conoce como desnormalización
¿Por qué hacerlo?
Porque ahora podemos mostrar la información del empleado sin tener que hacer otra consulta para obtener el nombre de la empresa:
db.employees.findOne({ _id: 1 })
Ahora bien, el problema es que companyName está duplicado.
Si la empresa cambia su nombre, tenemos que actualizar también los documentos que lo tienen almacenado.
En general conviene utilizar desnormalización cuando los datos duplicados son datos que no van a ser actualizados nunca, o casi nunca.
En nuestro ejemplo el nombre de una empresa no es algo que suele cambiar todo el tiempo. Esto nos permite hacer lecturas más simples, ahorrándonos consultas a la base de datos, con la contra de tener que realizar una actualización posiblemente grande en el caso de que la empresa cambie su nombre.
La idea no es evitar la duplicación a toda costa, sino duplicar información de manera intencional cuando beneficia nuestros patrones de lectura.
4. findOne() vs $lookup
Imaginemos que NO usamos desnormalización y volvamos al ejemplo del empleado:
Collection:Employees
{
name: "Nicolas",
companyId: ObjectId("...")
}
Podemos obtener la empresa haciendo una segunda consulta:
const employee = db.employees.findOne({ name: "Nicolas" })
const company = db.companies.findOne({
_id: employee.companyId
})
Tenemos DOS operaciones contra MongoDB.
Otra opción es utilizar $lookup:
db.employees.aggregate([
{
$match: { name: "Nicolas" }
},
{
$lookup: {
from: "companies",
localField: "companyId",
foreignField: "_id",
as: "company"
}
}
])
$lookup nos permite relacionar documentos de distintas collections dentro de una aggregation.
Para obtener un solo empleado, hacer dos findOne() puede ser perfectamente razonable
Pero si estamos obteniendo muchos empleados y necesitamos también sus empresas, hacer una consulta adicional por cada empleado puede generar el problema conocido como consultas N+1. En ese caso, $lookup puede ser una mejor alternativa
Y algo importante: guardar un ID como referencia no crea una clave foránea. MongoDB no sigue automáticamente esa referencia. Somos nosotros quienes decidimos cómo obtener el documento relacionado. Ya sea con otra consulta, con $lookup, o incluso evitando la consulta mediante desnormalización.
TLDR
Pensá primero en cómo se van a leer y modificar los datos.
Embedding conviene utilizarse cuando los datos pertenecen sólo al documento y normalmente se utilizan juntos.
Referencing cuando muchos documentos van a utilizar la misma información, y esa información va a ser actualizada con frecuencia.
Desnormalización cuando queremos contar con una referencia a un documento de otra collection, pero para ahorrarnos consultas extras duplicamos información que no va a ser actualizada con frecuencia
Y para obtener los datos referenciados podemos usar consultas separadas con findOne() o $lookup cuando necesitamos combinar muchos documentos.
MongoDB no nos obliga a elegir una única estrategia. El objetivo es elegir el modelo que mejor se adapte a los patrones de acceso de nuestra aplicación.

Top comments (0)