Eliminaciones en MongoDB: delete y soft delete | Nicolás Garzón
Inicio Wiki MongoDB Eliminaciones Volver a MongoDBMongoDB
Eliminaciones Revisa deleteOne, deleteMany y estrategias de soft delete, retención y borrado seguro con filtros precisos, auditoría y efectos sobre índices.
Última actualización Actualizada 25 de jul de 2026
Eliminar datos es una decisión de dominio y operación, no solo una llamada a . Antes de borrar debes definir retención, referencias, auditoría, recuperación, privacidad y qué significa que un recurso deje de existir.
Nota anteriorUpserts Nota siguiente Bulk operations deleteOne
MongoDB ofrece eliminación física mediante deleteOne y deleteMany. También pueden implementarse soft delete, TTL, archival y anonimización según el caso.
JavaScript
Copiar const result = await db. products. deleteOne ( {
_id : productId,
businessId : session. businessId
} ) ; deletedCount indica cuántos documentos fueron eliminados. Si es cero, puede significar no encontrado, tenant incorrecto o precondition no satisfecha.
La eliminación se replica. Un replica set no conserva una copia “sin borrar” para recuperar el dato: los secondaries aplican la misma operación.
Los datos pueden dejar de ser necesarios por:
petición del usuario;
política de retención;
cierre de una cuenta;
limpieza de sesiones;
corrección de imports;
requisitos legales;
expiración temporal.
Pero borrar puede afectar:
referencias desde otras colecciones;
reportes históricos;
auditoría;
backups;
Change Streams;
índices unique;
procesos asíncronos;
obligaciones de conservación.
Nunca ejecutes deletes con filtros construidos directamente desde input.
TypeScript
Copiar await collection. deleteMany ( request. body) ; TypeScript
Copiar await collection. deleteOne ( {
_id: parseObjectId ( input. id) ,
businessId: session. businessId,
status: 'draft'
} ) ; El filtro incluye identidad, tenant y estado permitido.
Elimina como máximo un documento. Es apropiado para recursos individuales.
JavaScript
Copiar db. sessions. deleteOne ( {
_id : sessionId,
userId
} ) ; Comprueba deletedCount y transforma el resultado según dominio.
JavaScript
Copiar db. sessions. deleteMany ( {
userId,
expiresAt : { $lt : new Date ( ) }
} ) ; Cada documento se elimina atómicamente, pero el conjunto no es una transacción. Una operación grande puede:
producir mucho trabajo de índice;
aumentar oplog y replication lag;
competir con tráfico normal;
durar más que timeouts;
ser difícil de reanudar.
Para grandes volúmenes, utiliza batches y checkpoints.
Consiste en conservar el documento y marcarlo:
JavaScript
Copiar {
deletedAt : ISODate ( '2026-07-24T12:00:00Z' ) ,
deletedBy : ObjectId ( '...' )
} JavaScript
Copiar db. products. updateOne (
{
_id : productId,
businessId,
deletedAt : null
} ,
{
$set : {
deletedAt : new Date ( ) ,
deletedBy : actorId,
updatedAt : new Date ( )
}
}
) ;
restauración sencilla;
conserva referencias e historia;
auditoría;
permite procesos de purga posteriores.
todas las queries deben excluir eliminados;
índices crecen;
uniqueness cambia;
datos sensibles siguen almacenados;
backups también los conservan;
aumenta complejidad de reporting.
Soft delete no cumple automáticamente una solicitud de borrado definitivo.
Un SKU puede ser único solo entre documentos activos:
JavaScript
Copiar db. products. createIndex (
{ businessId : 1 , sku : 1 } ,
{
unique : true ,
partialFilterExpression : {
deletedAt : null
}
}
) ; Debes definir si documentos antiguos tienen deletedAt: null o missing. La expresión del índice debe coincidir con el schema real.
Restaurar puede fallar por conflictos de unicidad:
Texto
Copiar producto A eliminado con SKU X
→ se crea producto B activo con SKU X
→ restaurar A viola unique indexLa aplicación debe decidir si renombra, fusiona, rechaza o mantiene archivado.
Un TTL index elimina documentos después de una fecha o duración:
JavaScript
Copiar db. sessions. createIndex (
{ expiresAt : 1 } ,
{ expireAfterSeconds : 0 }
) ; TTL es apropiado para sesiones, tokens temporales, cache o datos con expiración técnica.
eliminación en el segundo exacto;
orden entre documentos;
ejecución de lógica de negocio;
webhooks;
compensación;
archivo previo.
El monitor TTL trabaja periódicamente. No lo uses como scheduler de pagos o cancelaciones críticas.
Para datos históricos puede existir flujo:
Texto
Copiar datos activos
→ export verificado
→ almacenamiento de archivo
→ validación de conteos y checksums
→ delete por batches
→ prueba de recuperaciónCopiar y borrar sin verificación puede perder datos o duplicarlos.
MongoDB no ejecuta cascades automáticos. Antes de eliminar un cliente:
¿sus órdenes históricas deben conservarse?
¿se anonimiza el snapshot?
¿se prohíbe borrar mientras existen relaciones?
¿se eliminan sesiones y tokens?
¿se envía un job asíncrono?
¿se necesita transacción?
Una orden no debería perder sus items históricos porque se eliminó un producto.
A veces el requisito no es eliminar el documento sino retirar datos personales manteniendo métricas:
JavaScript
Copiar {
customer : {
id : null ,
nameAtPurchase : 'Usuario eliminado'
}
} La anonimización debe ser irreversible según el objetivo, cubrir duplicados y backups, y verificarse con asesoría legal cuando corresponda.
Un delete event puede incluir document key. Si consumidores necesitan el documento anterior, deben diseñarse pre-images cuando estén soportadas y habilitadas, o emitir un evento de dominio antes de borrar.
No supongas que un consumidor podrá reconstruir todos los campos después del delete.
Un backup es la defensa frente a borrados accidentales, pero solo si:
existe antes del incidente;
tiene retención suficiente;
puede restaurarse;
el RPO/RTO son aceptables;
se han probado restores;
incluye dependencias necesarias.
Replication no reemplaza backup porque el delete se replica correctamente.
Define filtro exacto y cuenta candidata.
Ejecuta muestra.
Crea backup o snapshot.
Registra aprobación.
Procesa batches por _id o fecha.
Limita impacto y observa lag.
Registra progreso.
Verifica conteos.
Revisa índices y espacio.
Conserva evidencia de ejecución.
No utilices un único deleteMany({}) en scripts reutilizables.
La operación debe ser idempotente o devolver estado claro.
La lectura debe manejarla y un job puede reconciliar.
Queries que filtran deletedAt: null pueden comportarse distinto con campos ausentes.
Debe existir una política.
Si el valor no es Date válido, no expira como se esperaba.
Debe considerar límites, retry y commit ambiguo.
El filtro debe incluir shard key cuando sea posible para evitar broadcast deletes.
El delete llega a las réplicas.
Los recursos “eliminados” siguen apareciendo.
No ofrece ejecución exacta ni lógica de negocio.
Fallos intermedios dejan datos inconsistentes.
Riesgo crítico de pérdida masiva.
Un backup no verificado es una suposición.
Prueba tenant incorrecto y estado no permitido.
Ejecuta delete repetido.
Revisa referencias.
Prueba restore con unique conflicts.
Observa TTL sin depender de tiempo exacto.
Simula fallo en cascada.
Verifica backups y restauración.
Mide lag durante batches.
Audita campos personales duplicados.
Confirma eventos y consumidores.
Eliminar es parte del lifecycle del dato. La eliminación física es inmediata desde el modelo lógico, soft delete conserva complejidad y TTL es eventual. Referencias, backups, auditoría y privacidad deben diseñarse antes de ejecutar el primer delete.
Comprueba lo aprendido
¿Por qué un replica set no recupera un delete accidental?
¿Qué costes introduce soft delete?
¿Cuándo usarías TTL?
¿Por qué TTL no sirve como scheduler exacto?
¿Qué puede impedir restaurar un documento?
¿Cómo ejecutarías una purga de millones de documentos?