Filtros y operadores de consulta en MongoDB | Nicolás Garzón
Inicio Wiki MongoDB Filtros y operadores de consulta Volver a MongoDBMongoDB
Filtros y operadores de consulta Revisa operadores de comparación, lógicos, existencia, tipos y expresiones para construir filtros precisos y compatibles con índices en MongoDB.
Última actualización Actualizada 25 de jul de 2026
Un filtro de MongoDB es un documento BSON que describe qué condiciones debe cumplir un documento. Los operadores permiten expresar igualdad, comparación, lógica, existencia, tipos, arrays y expresiones.
Nota anteriorCRUD: lecturas Nota siguiente Documentos anidados y dot notation JavaScript
Copiar {
businessId : ObjectId ( '...' ) ,
status : { $in : [ 'pending' , 'confirmed' ] } ,
createdAt : { $gte : start, $lt : end }
} La sintaxis parece sencilla, pero su semántica depende de tipos BSON, null, campos ausentes, arrays, collation e índices.
Mostrar órdenes confirmadas o pendientes de un negocio creadas durante julio.
JavaScript
Copiar const filter = {
businessId,
status : { $in : [ 'pending' , 'confirmed' ] } ,
createdAt : {
$gte : new Date ( '2026-07-01T00:00:00Z' ) ,
$lt : new Date ( '2026-08-01T00:00:00Z' )
}
} ; El rango utiliza inicio inclusivo y final exclusivo. Esta convención evita problemas con milisegundos al construir periodos consecutivos.
JavaScript
Copiar { status : 'pending' } JavaScript
Copiar { status : { $eq : 'pending' } } La comparación es sensible al tipo. Esto no coincide con un ObjectId:
JavaScript
Copiar { businessId : '66a2...' } JavaScript
Copiar { businessId : ObjectId ( '66a2...' ) }
$gt: mayor que;
$gte: mayor o igual;
$lt: menor que;
$lte: menor o igual;
$ne: diferente.
JavaScript
Copiar {
stock : { $gte : 1 } ,
price : { $lt : Decimal128 ( '50000.00' ) }
} Mantén tipos numéricos consistentes. Mezclar strings, double y Decimal128 dificulta interpretar resultados y rangos.
JavaScript
Copiar { status : { $in : [ 'pending' , 'confirmed' ] } } $in funciona bien con listas controladas y valores indexables. Una lista enorme aumenta tamaño del comando y trabajo del planner. Si la lista proviene de input, limita cantidad y valida cada valor.
$nin y $ne suelen ser menos selectivos porque pueden coincidir con gran parte de la colección, incluyendo consideraciones sobre campos missing.
JavaScript
Copiar {
businessId,
status : 'pending'
} Ambas condiciones deben cumplirse.
Útil cuando necesitas combinar expresiones repetidas sobre la misma clave o construir dinámicamente:
JavaScript
Copiar {
$and : [
{ stock : { $gte : 1 } } ,
{ stock : { $lte : 100 } }
]
} A menudo puede simplificarse:
JavaScript
Copiar { stock : { $gte : 1 , $lte : 100 } } JavaScript
Copiar {
$or : [
{ status : 'pending' } ,
{ priority : 'urgent' }
]
} Cada rama debe evaluarse con índices. Un $or con una rama no indexable puede elevar el coste completo.
Coincide cuando ninguna expresión se cumple. Su semántica con campos ausentes puede sorprender; prueba casos reales.
Niega una condición sobre un campo:
JavaScript
Copiar { price : { $not : { $gt : 100 } } } No es un operador lógico general equivalente a envolver cualquier documento arbitrario.
JavaScript
Copiar { confirmedAt : { $exists : true } } Comprueba presencia del campo, no que tenga un valor no null.
JavaScript
Copiar { total : { $type : 'decimal' } } Es útil para auditorías y migraciones. No debería ser necesario en cada query normal si el schema está gobernado.
JavaScript
Copiar { discount : null } puede coincidir con documentos donde discount es null y documentos donde el campo no existe.
JavaScript
Copiar {
discount : null ,
discount : { $exists : true }
} No puedes repetir la misma clave en un objeto JavaScript porque la segunda sobrescribe la primera. Debes combinar operadores:
JavaScript
Copiar {
discount : {
$eq : null ,
$exists : true
}
} Para distinguir con precisión durante una auditoría puede utilizarse $type o $expr.
JavaScript
Copiar {
'customer.id' : customerId,
'deliveryAddress.city' : 'Bogotá'
} Dot notation consulta subcampos. No crea una relación ni otra collection.
La igualdad contra un documento completo depende de forma y orden de campos, por lo que suele ser más robusto consultar rutas específicas.
Una igualdad simple sobre un campo array puede coincidir si algún elemento tiene ese valor:
JavaScript
Copiar { tags : 'coffee' } Para condiciones sobre el mismo elemento de un array de documentos utiliza $elemMatch:
JavaScript
Copiar {
items : {
$elemMatch : {
productId,
quantity : { $gte : 2 }
}
}
} Sin $elemMatch, condiciones separadas pueden cumplirse en elementos distintos:
JavaScript
Copiar {
'items.productId' : productA,
'items.quantity' : { $gte : 2 }
} Eso no garantiza que el item de productA tenga quantity mayor o igual a 2.
JavaScript
Copiar { tags : { $all : [ 'coffee' , 'organic' ] } } Exige que el array contenga todos los valores.
JavaScript
Copiar { items : { $size : 3 } } Exige tamaño exacto. $size no expresa rangos directamente y puede no aprovechar índices como esperas.
JavaScript
Copiar { sku : / ^ CAF-/ } Una regex anclada por prefijo puede aprovechar ciertos índices según collation y opciones. Una regex no anclada:
JavaScript
Copiar { name : / coffee / i } puede recorrer muchos valores y no reemplaza un motor de búsqueda.
Valida longitud y complejidad de patrones para evitar consumo excesivo. No construyas regex directamente desde input sin escaping o límites.
Collation cambia comparación y sort de strings:
JavaScript
Copiar db. customers. find ( { name : 'jose' } ) . collation ( {
locale : 'es' ,
strength : 2
} ) ; Para utilizar un índice, la collation de la query debe ser compatible con la del índice. Lowercase manual no reproduce todas las reglas lingüísticas.
Permite utilizar expresiones de aggregation dentro del filtro:
JavaScript
Copiar {
$expr : {
$gt : [ '$total' , '$creditLimit' ]
}
} Es útil cuando la condición compara campos o requiere cálculos. Puede limitar el uso de índices según expresión y versión. No lo utilices cuando un filtro simple resuelve la query.
Este código es peligroso:
TypeScript
Copiar const filter = request. body;
await users. findOne ( filter) ; Un atacante podría enviar:
JSON
Copiar {
"email" : { "$ne" : null }
} La mitigación es construir el filtro desde campos permitidos:
TypeScript
Copiar const filter: Record< string , unknown > = {
businessId: session. businessId
} ;
if ( input. status) {
filter. status = input. status;
}
valida tipos en runtime;
rechaza claves inesperadas;
limita operadores permitidos;
evita merge de objetos no confiables;
protege prototype pollution;
registra patrones anómalos.
TypeScript no valida datos recibidos en runtime.
Un filtro debe evaluarse como query shape.
JavaScript
Copiar {
businessId,
status : 'pending' ,
createdAt : { $gte : start, $lt : end }
} JavaScript
Copiar db. orders. createIndex ( {
businessId : 1 ,
status : 1 ,
createdAt : 1
} ) ; El orden depende de igualdad, sort y rangos. No crees índices aislados para cada operador.
Devuelve ninguna coincidencia. Decide si representa un filtro válido o error de entrada.
Un rango puede excluir silenciosamente valores almacenados como string. Audita y migra.
$ne, $nin y null pueden incluirlo de formas no intuitivas. Prueba ejemplos.
Un constructor de query debe decidir si significa “sin filtro” o “sin resultados”; no generes semántica accidental.
Puede consumir CPU y causar latencia global.
Convierte límites de zona horaria a instantes UTC antes de consultar, conservando la zona del dominio cuando corresponda.
Permite injection y filtros fuera del scope.
Produce resultados vacíos o parciales.
Combina condiciones de elementos distintos.
Escala mal y no ofrece relevancia lingüística.
Puede examinar gran parte de la collection.
La sintaxis correcta no garantiza un plan eficiente.
Define ejemplos que deben coincidir y no coincidir.
Incluye null, missing y tipo incorrecto.
Prueba arrays con condiciones en elementos distintos.
Valida input y allowlist.
Ejecuta explain('executionStats').
Revisa index bounds, keys y docs examinados.
Prueba valores frecuentes y outliers.
Observa latencia y query shape en producción.
Los filtros expresan semántica sobre BSON, no texto genérico. Tipo, existencia, arrays y collation cambian el resultado. La aplicación debe construir filtros seguros y el diseño debe comprobarlos con índices y datos reales.
Comprueba lo aprendido
¿Por qué un string con apariencia de ObjectId no coincide con ObjectId?
¿Qué diferencia existe entre null y missing?
¿Cuándo es necesario $elemMatch?
¿Por qué $ne puede ser poco selectivo?
¿Cómo evitarías operator injection?
¿Qué relación existe entre collation de query e índice?
Documentos anidados y dot notation.