Explica cómo combinar búsqueda e inserción con upsert, qué campos forman el documento nuevo y cómo evitar duplicados mediante filtros e índices únicos.
Un combina dos caminos: si un documento coincide con el filtro, se actualiza; si no coincide, MongoDB crea uno nuevo a partir del filtro y del update. Es útil para configuraciones, sincronizaciones, contadores y operaciones idempotentes, pero no garantiza unicidad por sí mismo.
buscar documento
→ si existe, actualizar
→ si no existe, insertar
Ese patrón tiene una carrera:
Texto
A busca → no existe
B busca → no existe
A inserta
B inserta
El upsert reduce el flujo a una operación, pero dos upserts concurrentes todavía pueden insertar duplicados si la identidad lógica no está protegida por un índice unique.
Cuando no existe coincidencia, MongoDB construye el nuevo documento a partir de elementos de igualdad del filtro y operadores del update. Evita filtros complejos cuya contribución al documento insertado sea ambigua.
Ejemplo claro:
JavaScript
filter:{
businessId,
externalId
}
Ejemplo riesgoso:
JavaScript
filter:{status:{$ne:'deleted'},score:{$gte:10}}
La inserción debe recibir campos explícitos mediante $setOnInsert y $set.
Esto es atómico sobre el documento diario. Reintentar el mismo evento puede duplicar el contador, por lo que se necesita idempotencia si el procesamiento es at-least-once.
Bajo concurrencia, un upsert puede competir con otro. El índice unique evita duplicados, pero una operación puede recibir duplicate key. Según el caso, la aplicación puede:
volver a ejecutar como update sin upsert;
leer el documento existente;
tratarlo como éxito idempotente;
propagar conflicto.
No reintentes ciegamente cualquier duplicate key: podría indicar una regla distinta violada.
Upsert es una operación condicional, no una restricción de unicidad. El filtro define identidad, $setOnInsert define creación y el índice unique protege concurrencia. La idempotencia depende de que repetir la operación no duplique efectos ni permita que eventos antiguos sobrescriban estado nuevo.
Comprueba lo aprendido
¿Qué caminos puede tomar un upsert?
¿Por qué necesita un índice unique?
¿Qué diferencia existe entre $set y $setOnInsert?
¿Cómo evitarías que un mensaje antiguo sobrescriba uno nuevo?
¿Por qué un contador upsertado puede no ser idempotente?