Más allá de lo básico: Comandos avanzados de consulta MongoDB para análisis de datos

Utiliza filtros avanzados de MongoDB, proyecciones y etapas de agregación para analizar datos sin moverlos fuera de la base de datos.

Más allá de lo básico: Comandos avanzados de consulta MongoDB para análisis de datos

Las consultas de MongoDB se vuelven más útiles cuando vas más allá de las simples llamadas find(). Si necesitas filtrar documentos anidados, remodelar la salida o calcular resultados agrupados, los operadores de consulta avanzados y las etapas de agregación te permiten hacer ese trabajo cerca de los datos.

Los ejemplos a continuación se centran en comandos prácticos que puedes ejecutar en mongosh para informes, resolución de problemas y análisis puntuales.

Dominando el filtrado complejo con operadores de consulta

Mientras que el método básico find() maneja comprobaciones de igualdad simples, el análisis avanzado a menudo requiere combinar múltiples condiciones o consultar estructuras de campo específicas. MongoDB proporciona un rico conjunto de operadores de consulta para construir filtros granulares.

Operadores lógicos para consultas compuestas

Los operadores lógicos te permiten combinar múltiples condiciones de consulta, ofreciendo un control detallado sobre qué documentos se devuelven. Estos son esenciales para estructurar preguntas analíticas complejas.

  • $and / $and implícito: Se utiliza para especificar múltiples criterios que deben ser todos verdaderos. Aunque a menudo es implícito (listando condiciones secuencialmente en el objeto de consulta), $and es necesario cuando se consulta el mismo campo varias veces.
    // $and implícito: Encontrar usuarios mayores de 25 Y que vivan en Nueva York
    db.users.find({ age: { $gt: 25 }, city: "New York" });
    
    // $and explícito: Encontrar documentos donde 'score' > 90 O 'level' es 5
    db.results.find({ $and: [ { score: { $gt: 90 } }, { level: 5 } ] });
    
  • $or: Selecciona documentos que coincidan con cualquiera de las expresiones especificadas en el array.
    db.products.find({ $or: [ { category: "Electronics" }, { price: { $lt: 100 } } ] });
    
  • $not: Niega los resultados de la expresión especificada.

Operadores geoespaciales y de arrays

Para datos basados en ubicación o arrays complejos, operadores especializados proporcionan potencia analítica:

  • $geoWithin / $near: Esencial para encontrar datos dentro de un área geográfica o proximidad específica.
  • $elemMatch: Crucial para consultar arrays de documentos incrustados, asegurando que un elemento en el array coincida con todos los criterios especificados dentro de $elemMatch.
    // Encontrar pedidos donde al menos un artículo en el array 'items' cueste más de 500 Y tenga una cantidad mayor a 1
    db.orders.find({ items: { $elemMatch: { price: { $gt: 500 }, qty: { $gt: 1 } } } });
    

Proyección avanzada: Dando forma a la salida

La proyección, gestionada usando el segundo argumento en el método find(), determina qué campos se devuelven. La proyección avanzada va más allá de la simple inclusión/exclusión para transformar o dar forma a los datos devueltos.

Exclusión e inclusión de campos

  • 1 incluye un campo; 0 excluye un campo.
  • Nota importante: No puedes mezclar inclusión (1) y exclusión (0) excepto para el campo _id (que está incluido por defecto y puede ser excluido explícitamente configurándolo a 0).
// Incluir solo 'name' y 'email', excluir '_id'
db.users.find({}, { name: 1, email: 1, _id: 0 });

Segmentación y manipulación de arrays

La proyección puede limitar el número de elementos del array devueltos usando $slice:

  • $slice: N: Devuelve los primeros N elementos.
  • $slice: -N: Devuelve los últimos N elementos.
  • $slice: [M, N]: Devuelve N elementos comenzando desde el índice M.
// Devolver solo las últimas 3 entradas del array 'history'
db.logs.find({}, { history: { $slice: -3 } });

Analizando datos con el marco de agregación

El marco de agregación de MongoDB es la herramienta más poderosa para el análisis de datos complejo, permitiéndote procesar registros de datos a través de un pipeline de etapas. Cada etapa realiza una transformación u operación específica en los datos pasados desde la etapa anterior.

Etapas clave de agregación

La estructura básica usa db.collection.aggregate([...pipeline]).

1. $match (Filtrado)

Funciona de manera similar a find(), pero se aplica antes de las etapas posteriores, optimizando el rendimiento al reducir el conjunto de datos temprano.

2. $group (Agrupación y cálculo)

Esta etapa agrupa documentos por un identificador especificado (_id) y aplica operadores acumuladores para calcular estadísticas resumidas.

Acumuladores comunes:

  • $sum
  • $avg
  • $min, $max
  • $push (para recopilar datos de array del grupo)
// Calcular el puntaje promedio por departamento
db.scores.aggregate([
  { $group: { 
    _id: "$department", 
    averageScore: { $avg: "$score" },
    totalStudents: { $sum: 1 }
  } }
]);

3. $project (Reestructuración de documentos)

Se usa dentro de la agregación para reestructurar los documentos de salida, similar a la proyección de find(), pero a menudo se usa para crear nuevos campos calculados.

  • Campos calculados: Puedes realizar cálculos dentro de la etapa de proyección usando campos existentes.
// Calcular el margen de beneficio dentro del pipeline
db.sales.aggregate([
  { $project: { 
    _id: 0, 
    productName: 1,
    profit: { $subtract: ["$salePrice", "$cost"] }
  } }
]);

4. $lookup (Unión de datos)

La etapa $lookup agrega documentos coincidentes de otra colección, similar a una unión externa izquierda. Es útil cuando necesitas enriquecer documentos para informes sin hacer la unión en el código de la aplicación.

// Unir la colección 'orders' con la colección 'customers'
db.orders.aggregate([
  { $match: { status: "Pending" } },
  { $lookup: {
      from: "customers",         // Colección con la que unir
      localField: "customerId",  // Campo de los documentos de entrada (orders)
      foreignField: "_id",       // Campo de los documentos de la colección "from" (customers)
      as: "customerDetails"      // Nombre del campo de array de salida
  } }
]);

5. $unwind (Desestructuración de arrays)

Si un campo de array contiene múltiples elementos, $unwind crea un documento de salida separado para cada elemento en el array, desnormalizando efectivamente los datos para facilitar la agrupación o el filtrado en los contenidos del array.

Advertencia: $unwind puede aumentar significativamente el recuento de documentos. Úsalo con prudencia, típicamente después de $match para reducir el conjunto inicial.

Mejores prácticas para consultas analíticas

  1. Indexa los puntos de entrada: Indexa campos utilizados por etapas tempranas de $match y etapas de $sort respaldadas por índices. $group puede beneficiarse de menos documentos de entrada, pero no se vuelve automáticamente rápido solo porque el campo agrupado tenga un índice.
  2. Filtra temprano: Coloca las etapas $match lo más temprano posible en el pipeline de agregación. Reducir el recuento de documentos temprano ahorra una potencia de procesamiento significativa para etapas posteriores más costosas como $lookup o $group.
  3. Usa tipos de datos apropiados: Asegúrate de que los campos de comparación (como fechas o valores numéricos) se almacenen de manera consistente. Las discrepancias de tipo hacen que los operadores $match fallen silenciosamente o de manera ineficiente.

Usa estos comandos cuando la simple recuperación de datos no sea suficiente. Comienza con filtros selectivos, proyecta solo los campos que necesitas y pasa a la agregación cuando necesites agrupación, reestructuración o enriquecimiento de colección a colección.