Curso
Como base de datos NoSQL muy popular, MongoDB ofrece una forma flexible y escalable de gestionar tus datos. Esta guía es tu punto de partida para dominar su intuitiva Query API, que te permite interactuar con tus datos usando BSON (Binary JSON), optimizado para la eficiencia y los tipos de datos.
A lo largo del artículo, veremos las operaciones básicas de crear, leer, actualizar y eliminar (CRUD), profundizaremos en consultas de precisión con filtros y proyección, exploraremos la potencia de las pipelines de agregación para transformar datos y hablaremos de indexación para potenciar el rendimiento. Al finalizar, tendrás una base sólida para recuperar y manipular datos con eficiencia en tus colecciones de MongoDB.
Conceptos fundamentales: operaciones CRUD
La columna vertebral de cualquier interacción con una base de datos es CRUD: crear, leer, actualizar y eliminar. Veamos cómo hacer cada una en MongoDB.
Creación de datos (insert)
Para añadir nuevos documentos a una colección, se usan los métodos insert. Los documentos en MongoDB son objetos con formato similar a JSON.
insertOne()
Este método añade un único documento a una colección.
// A sample document to insert
const newProduct = {
name: "Wireless Mouse",
brand: "Logitech",
price: 29.99,
inStock: true
};
// Insert the new document into the 'products' collection
db.products.insertOne(newProduct);
insertMany()
Este método añade varios documentos de una sola vez, mucho más eficiente que insertarlos uno a uno.
// An array of documents to insert
const newProducts = [
{ name: "Mechanical Keyboard", brand: "Corsair", price: 129.99, inStock: false },
{ name: "Gaming Headset", brand: "Razer", price: 99.99, inStock: true },
{ name: "Webcam", brand: "LogiTech", price: 79.99, inStock: true }
];
// Insert the array of documents into the 'products' collection
db.products.insertMany(newProducts);
Lectura de datos (find)
El método find() es tu herramienta principal para recuperar documentos de una colección. Al llamar a find(), no devuelve inmediatamente todos los documentos. En su lugar, retorna un cursor. Es una forma eficiente de manejar conjuntos de resultados grandes sin cargarlo todo en memoria a la vez.
La llamada más simple a find() no recibe argumentos y devolverá un cursor a todos los documentos de la colección.
// Find and return a cursor to all documents in the 'products' collection
db.products.find();
Así es como se ve ese cursor en mongosh (la consola de MongoDB) y cómo podrías interactuar con él:
// Find all products and assign the returned cursor to a variable
const productCursor = db.products.find();
// The variable 'productCursor' now holds the cursor object, not the documents themselves.
// To get the documents, you need to iterate over the cursor.
// You can convert the cursor to an array to see all the documents
const allProducts = productCursor.toArray();
console.log(allProducts);
// Or, you can manually iterate through the documents
while (productCursor.hasNext()) {
const product = productCursor.next();
console.log(product);
}
El cursor es un objeto con varios métodos como toArray(), hasNext() y next() que te permiten recuperar y trabajar con los documentos del resultado.
Actualización de datos (update)
Para modificar documentos existentes, se usan los métodos update. Reciben dos argumentos principales: un filtro para seleccionar los documentos a actualizar y un operador de actualización para indicar los cambios. Un operador común es $set, que asigna el valor de un campo.
updateOne()
Este método actualiza el primer documento que coincide con el filtro.
// Update the 'Wireless Mouse' document to set its price to 39.99
db.products.updateOne(
{ name: "Wireless Mouse" }, // Filter to find the document
{ $set: { price: 39.99 } } // Update operator
);
updateMany()
Este método actualiza todos los documentos que cumplen el filtro indicado.
// Update all 'LogiTech' brand products to be in stock
db.products.updateMany(
{ brand: "LogiTech" }, // Filter to find the documents
{ $set: { inStock: true } } // Update operator
);
Eliminación de datos (delete)
Para eliminar documentos, se usan los métodos delete. También reciben un filtro para determinar qué documentos se eliminan.
deleteOne()
Este método elimina el primer documento que coincide con el filtro.
// Delete the 'Webcam' document
db.products.deleteOne({ name: "Webcam" });
deleteMany()
Este método elimina todos los documentos que cumplen el filtro. Para borrar todos los documentos de una colección, puedes pasar un filtro vacío {}.
// Delete all documents that are not in stock
db.products.deleteMany({ inStock: false });
Recuperación de precisión: filtros de consulta
Los filtros de consulta están en el corazón de la potencia de MongoDB. Son el primer argumento en los métodos find() y update y te permiten seleccionar con precisión los documentos con los que quieres trabajar.
Operadores de comparación
Estos operadores te permiten comparar el valor de un campo con un valor concreto.
$eq (igual a)
Encuentra documentos donde un campo sea igual a un valor.
// Find all products that are made by 'Razer'
db.products.find({ brand: { $eq: "Razer" } });
// This can be simplified to:
// db.products.find({ brand: "Razer" });
$gt y $gte (mayor que, mayor o igual que)
Encuentran documentos donde el valor de un campo es mayor que ($gt) o mayor o igual que ($gte) un valor dado.
// Find all products with a price greater than 100
db.products.find({ price: { $gt: 100 } });
$lt y $lte (menor que, menor o igual que)
Encuentran documentos donde el valor de un campo es menor que ($lt) o menor o igual que ($lte) un valor dado.
// Find all products with a price less than or equal to 50
db.products.find({ price: { $lte: 50 } });
$ne (distinto de)
$ne encuentra documentos donde un campo no es igual a un valor concreto.
// Find all products that are NOT made by 'LogiTech'
db.products.find({ brand: { $ne: "LogiTech" } });
$in y $nin (en un array, no en un array)
Encuentran documentos donde el valor de un campo está ($in) o no está ($nin) en un array de valores especificado.
// Find all products from 'Razer' or 'Corsair'
db.products.find({ brand: { $in: ["Razer", "Corsair"] } });
Operadores lógicos
Los operadores lógicos combinan varias condiciones para crear consultas complejas.
$and
$and combina condiciones de consulta con un Y lógico. Todas las condiciones deben cumplirse.
// Find all products with a price greater than 50 AND are in stock
db.products.find({ $and: [{ price: { $gt: 50 } }, { inStock: true }] });
// This can be simplified by just listing the conditions:
// db.products.find({ price: { $gt: 50 }, inStock: true });
$or
Combina condiciones con un O lógico. Debe cumplirse al menos una condición.
// Find all products with a brand of 'Corsair' OR a price less than 40
db.products.find({ $or: [{ brand: "Corsair" }, { price: { $lt: 40 } }] });
Operadores de elementos y de evaluación
Estos operadores aportan aún más flexibilidad al filtrar.
$exists
$exists devuelve documentos que contienen un campo específico.
// Find all documents that have an 'inStock' field
db.products.find({ inStock: { $exists: true } });
$regex
Permite usar expresiones regulares para buscar patrones en campos de tipo cadena.
// Find all product names that start with 'Wireless'
db.products.find({ name: { $regex: /^Wireless/ } });
Da forma a tu salida: proyección
Cuando realizas una operación find(), puedes elegir recibir solo un subconjunto de campos de cada documento. A esto se le llama proyección y es el segundo argumento que pasas a find(). Ayuda a reducir la cantidad de datos transferidos y procesados.
Incluir campos
Puedes indicar qué campos devolver asignándoles el valor 1.
// Find all products and return only the 'name' and 'price' fields
db.products.find({}, { name: 1, price: 1 });
Excluir campos
Del mismo modo, puedes indicar qué campos ocultar asignándoles el valor 0.
// Find all products and hide the 'inStock' field
db.products.find({}, { inStock: 0 });
El campo _id siempre se devuelve por defecto, a menos que lo excluyas explícitamente.
El campo _id
Para ocultar el campo _id, debes establecerlo explícitamente en 0.
// Find all products, returning only 'name' and 'price', and hiding '_id'
db.products.find({}, { _id: 0, name: 1, price: 1 });
Transformación avanzada de datos: pipelines de agregación
La agregación es un marco potente para procesar datos en MongoDB. Una pipeline de agregación consiste en una serie de etapas que procesan documentos y pasan los resultados de una etapa a la siguiente.
$match
Una etapa $match filtra documentos usando un filtro de consulta, igual que en find(). Suele ser la primera etapa para reducir el número de documentos a procesar.
// Find all products with a brand of 'LogiTech'
db.products.aggregate([
{ $match: { brand: "LogiTech" } }
]);
$group
La etapa $group agrupa documentos por una expresión _id indicada y puede ejecutar operaciones acumuladoras como calcular la suma o la media de un campo.
// Group products by brand and calculate the total number of products for each brand
db.products.aggregate([
{ $group: { _id: "$brand", totalProducts: { $sum: 1 } } }
]);
$project
Una etapa $project reconfigura cada documento del flujo, similar a la proyección en find(). Puedes añadir campos nuevos, eliminar existentes y manipular datos.
// Project a new field 'nameAndPrice' for each document
db.products.aggregate([
{ $project: { name: 1, price: 1, nameAndPrice: { $concat: ["$name", " (", "$price", ")"] } } }
]);
$sort
La etapa $sort reordena los documentos según un campo. El valor 1 es para ascendente y -1 para descendente.
// Sort all products by price in descending order
db.products.aggregate([
{ $sort: { price: -1 } }
]);
$limit y $skip
Estas etapas se usan para paginación. $skip descarta un número indicado de documentos desde el inicio de la pipeline, y $limit limita el número de documentos que pasan a la siguiente etapa.
// Skip the first 10 products and then return the next 5
db.products.aggregate([
{ $skip: 10 },
{ $limit: 5 }
]);
Un ejemplo de pipeline sencillo
Aquí tienes un ejemplo completo que muestra cómo trabajan juntas estas etapas.
// Find the average price of all products that are currently in stock,
// grouped by brand.
db.products.aggregate([
{ $match: { inStock: true } }, // Filter for products in stock
{ $group: { _id: "$brand", averagePrice: { $avg: "$price" } } } // Group and calculate average price
]);
Impulsa el rendimiento: indexación
Los índices son estructuras de datos especiales que almacenan una parte pequeña y fácilmente recorrible de los datos de una colección. Mejoran notablemente el rendimiento de las consultas al permitir que MongoDB encuentre y recupere documentos rápidamente sin escanear toda la colección.
createIndex()
Creas un índice sobre un campo que consultas o ordenas con frecuencia.
// Create a single-field index on the 'brand' field
db.products.createIndex({ brand: 1 });
El 1 indica orden ascendente para el índice. Puedes usar -1 para descendente, útil si sueles ordenar en esa dirección.
Crear un índice en un campo como brand hará que las consultas que filtran por brand (p. ej., db.products.find({ brand: "Razer" })) se ejecuten mucho más rápido, especialmente a medida que crece la colección.
Consideraciones al elegir campos para indexar
Normalmente deberías crear índices en campos que usas con frecuencia en tus consultas, como los de un filtro de find() o en el $match de una agregación.
- Cardinalidad: los campos con muchos valores únicos (alta cardinalidad), como el correo electrónico de un usuario, son excelentes candidatos para índices.
- Selectividad de la consulta: un campo que devuelve un subconjunto pequeño de documentos para una consulta dada también es un gran candidato.
- Ordenación: si ordenas a menudo los resultados por un campo concreto, crear un índice sobre ese campo acelerará mucho la operación de ordenación.
Impacto en las consultas
Los índices tienen un impacto enorme en el rendimiento. Una consulta con índice suele ser varios órdenes de magnitud más rápida que la misma consulta sin índice.
// Conceptual illustration of performance difference
// Without an index on 'brand':
// MongoDB performs a full collection scan, checking every document.
// This is slow, especially with millions of documents.
db.products.find({ brand: "Razer" });
// With an index on 'brand':
// MongoDB uses the index to quickly locate and return only the matching documents.
// This is very fast.
db.products.find({ brand: "Razer" });
Para optimizar aún más, puedes usar el método db.collection.explain() para analizar el rendimiento de las consultas y ver cómo se utilizan los índices.
Conclusión: tu camino hacia la soltura con MongoDB
¡Acabas de completar un curso acelerado de la Query API de MongoDB! Ahora sabes cómo ejecutar las operaciones CRUD esenciales, usar filtros potentes para recuperar datos específicos, dar forma a la salida con proyección y transformar tus datos con pipelines de agregación. También tienes una comprensión básica de la indexación para mejorar el rendimiento.
Esta guía te da una base sólida, y la mejor forma de dominar estos conceptos es practicarlos. Te animo a experimentar con distintas consultas y a explorar la documentación completa para descubrir operadores y funciones más avanzados. ¡Feliz programación!
MongoDB Query API: preguntas frecuentes
¿Cuál es la principal diferencia entre find() y aggregate()?
El método find() se usa para recuperar documentos de forma sencilla con filtrado y proyección opcionales. En cambio, aggregate() es un marco potente para procesar datos en múltiples etapas, que te permite transformar, agrupar, ordenar y reconfigurar documentos de forma más compleja. Mientras que find() sirve para leer los documentos tal cual, aggregate() te ayuda a extraer nuevos insights de tus datos.
¿Por qué MongoDB devuelve un cursor en una operación find() en lugar de los documentos directamente?
Un cursor es una forma eficiente de gestionar conjuntos de resultados potencialmente grandes. Al devolver un cursor, MongoDB evita cargar todos los documentos en memoria de una vez. Esto es clave para el rendimiento y la escalabilidad, especialmente cuando trabajas con millones de documentos. El cursor te permite iterar sobre los resultados según lo necesites, obteniendo los documentos por lotes.
¿Cuándo debo usar updateOne() vs. updateMany()?
Usa updateOne() cuando necesites modificar solo el primer documento que coincide con tu filtro. Es ideal cuando esperas una sola coincidencia, como al filtrar por un identificador único. Usa updateMany() cuando quieras actualizar todos los documentos que cumplan los criterios del filtro.
¿Cómo mejoran los índices el rendimiento de las consultas?
Los índices son estructuras de datos especiales que almacenan un subconjunto pequeño y organizado de los datos de una colección. Aceleran significativamente las consultas al permitir que MongoDB localice rápidamente los documentos sin tener que escanear toda la colección. En lugar de comprobar documento por documento, la base de datos usa el índice para ir directamente a los documentos que coinciden, haciendo la operación muchísimo más rápida.
¿Puedo combinar proyección con agregación?
Sí, la proyección es una parte clave de la pipeline de agregación. La etapa $project se usa para reconfigurar documentos, añadir campos nuevos o eliminar existentes. A menudo se aplica después de etapas como $match o $group para dar formato a la salida de la pipeline y tener un control muy preciso sobre la estructura final de tus datos.
Karen es Ingeniera de Datos y le apasiona crear plataformas de datos escalables. Tiene experiencia en automatización de infraestructuras con Terraform y le entusiasma compartir sus aprendizajes en entradas de blog y tutoriales. Karen es una creadora de comunidades, y le apasiona fomentar las conexiones entre los profesionales de los datos.
