El operador $expr en MongoDB te permite comparar dos campos del mismo documento dentro de una consulta, algo que los operadores tradicionales como $gt o $eq no resuelven solos. Si trabajas con datos financieros, métricas de uso o cualquier dataset donde necesites cruzar atributos internos, este operador te abre la puerta a queries mucho más potentes.
Qué es el expressive operator y cuándo usarlo
El $expr es un operador de MongoDB que habilita el uso de expresiones de aggregation dentro de un find normal. Su valor real aparece cuando dejas de comparar contra un número fijo y empiezas a comparar campos entre sí.
Imagina un dataset de presupuestos mensuales con dos atributos: budget (lo planeado) y spend (lo gastado). Con un $gt: 100 solo filtras gastos mayores a cien. Pero la pregunta interesante es otra: ¿cuáles presupuestos se pasaron? Para responderla necesitas comparar spend contra budget en el mismo documento, y ahí entra $expr [01:50].
¿Qué hace el operador $expr en MongoDB? Permite usar expresiones de aggregation dentro de un find para comparar campos del mismo documento entre sí, no solo contra valores fijos.
Cómo se escribe una consulta con $expr
La sintaxis cambia respecto a un find tradicional. Cuando usas $expr, el signo $ deja de ser solo un marcador de operador y pasa a referenciar un campo del documento.
Ejemplo para detectar presupuestos sobregirados:
javascript
db.monthly_budget.find({
$expr: { $gt: ["$spend", "$budget"] }
})
Aquí "$spend" y "$budget" no son strings: son referencias a los campos del documento. El resultado devuelve solo los registros donde el gasto superó el presupuesto, por ejemplo 450 sobre 400, 150 sobre 100 y 650 sobre 200 [03:30].
Por qué $expr cambia la forma de hacer queries
La diferencia clave es que abandonas la comparación contra valores estáticos. En lugar de preguntar “¿qué documentos tienen spend mayor a 100?”, puedes preguntar “¿qué documentos tienen spend mayor a su propio budget?”. Esa pregunta es imposible de responder con un $gt clásico.
Esto se vuelve útil en escenarios como:
- Detectar transacciones donde el monto excede un límite definido en el mismo registro.
- Comparar fecha de inicio contra fecha de fin en logs o reservas.
- Validar consistencia entre dos campos numéricos relacionados.
Ejemplo con la colección trips de bicicletas
Usando el dataset sample_training.trips, que contiene viajes de una app de bicicletas en Nueva York, puedes responder una pregunta concreta: ¿cuántos usuarios tomaron una bici, dieron una vuelta y la devolvieron en la misma estación?
La consulta compara start_station_id con end_station_id:
javascript
db.trips.find({
$expr: { $eq: ["$start_station_id", "$end_station_id"] }
}).count()
El resultado es 316 viajes que iniciaron y terminaron en el mismo punto [06:40]. Sin $expr tendrías que traer los documentos a tu aplicación y filtrarlos manualmente.
Cómo combinar $expr con operadores lógicos
Puedes encadenar $expr con $and para responder preguntas más específicas. Por ejemplo: ¿cuántos viajes empezaron y terminaron en la misma estación y además duraron más de 1200 segundos?
Aquí aparece un detalle importante: una vez que entras al modo expresivo, todos los operadores internos deben seguir la sintaxis de array con dos argumentos. Si intentas mezclar la sintaxis clásica { trip_duration: { $gt: 1200 } } dentro de un $expr, MongoDB lanza un error indicando que el operador esperaba dos argumentos.
La forma correcta:
javascript
db.trips.find({
$expr: {
$and: [
{ $eq: ["$start_station_id", "$end_station_id"] },
{ $gt: ["$trip_duration", 1200] }
]
}
}).count()
Este query devuelve 173 viajes que cumplen ambas condiciones [09:30].
¿Por qué $expr exige sintaxis de array? Porque opera con expresiones de aggregation, donde cada operador recibe sus argumentos como una lista ordenada: [campo, valor] o [campoA, campoB].
Regla práctica al usar $expr
Cuando entras al modo expresivo, ya no puedes volver a la sintaxis clásica dentro del mismo bloque. Toda comparación tiene que escribirse así: operador, atributo precedido de $, y el segundo argumento, que puede ser un valor fijo o otro campo del documento. Esa flexibilidad de comparar campo contra campo es la verdadera ganancia.
Dominar $expr te permite mover lógica que antes vivía en tu backend hacia la consulta misma, reduciendo transferencia de datos y simplificando el código de aplicación. ¿En qué query de tu proyecto reemplazarías un filtro manual por un $expr? Cuéntalo en los comentarios.