Todos los días, recibo un stock de documentos (una actualización). Lo que quiero hacer es insertar cada elemento que aún no existe.
- También quiero hacer un seguimiento de la primera vez que los inserté y la última vez que los vi en una actualización.
- No quiero tener documentos duplicados.
- No quiero eliminar un documento que se ha guardado anteriormente, pero que no está en mi actualización.
- El 95% (estimado) de los registros no se modifican día a día.
Estoy usando el controlador Python (pymongo).
Lo que hago actualmente es (pseudocódigo):
for each document in update:
existing_document = collection.find_one(document)
if not existing_document:
document['insertion_date'] = now
else:
document = existing_document
document['last_update_date'] = now
my_collection.save(document)
Mi problema es que es muy lento (40 minutos para menos de 100 000 registros, y tengo millones de ellos en la actualización). Estoy bastante seguro de que hay algo incorporado para hacer esto, pero el documento para la actualización () es mmmhhh ... un poco conciso ... ( http://www.mongodb.org/display/DOCS/Updating )
¿Alguien puede aconsejar cómo hacerlo más rápido?
A partir de MongoDB 2.4, puede usar $ setOnInsert ( http://docs.mongodb.org/manual/reference/operator/setOnInsert/ )
Establezca 'insertion_date' usando $ setOnInsert y 'last_update_date' usando $ set en su comando upsert.
Para convertir su pseudocódigo en un ejemplo de trabajo:
fuente
Siempre puede crear un índice único, lo que hace que MongoDB rechace un guardado conflictivo. Considere lo siguiente hecho usando el shell mongodb:
fuente
Puede usar Upsert con el operador $ setOnInsert.
fuente
1. Use Actualizar.
Basándose en la respuesta de Van Nguyen anterior, use la actualización en lugar de guardar. Esto le da acceso a la opción upsert.
NOTA : Este método anula todo el documento cuando se encuentra ( desde los documentos )
1.a. Use $ set
Si desea actualizar una selección del documento, pero no todo, puede usar el método $ set con la actualización. (de nuevo, desde los documentos ) ... Entonces, si quieres configurar ...
Envíalo como ...
Esto ayuda a evitar sobrescribir accidentalmente todos sus documentos
{ name: 'jason borne' }
.fuente
Resumen
Tenga en cuenta que supongo que PyMongo cambia para adaptarse al idioma que elija.
Instrucciones:
Cree la colección con un índice con unique = true para que no obtenga registros duplicados.
Itere sobre sus registros de entrada, creando lotes de ellos de 15,000 registros más o menos. Para cada registro en el lote, cree un archivo compuesto por los datos que desea insertar, suponiendo que cada uno será un nuevo registro. Agregue las marcas de tiempo 'creado' y 'actualizado' a estos. Publique esto como un comando de inserción por lotes con el indicador 'ContinueOnError' = verdadero, por lo que la inserción de todo lo demás ocurre incluso si hay una clave duplicada allí (lo que parece que habrá). ESTO OCURRIRÁ MUY RÁPIDO. Las inserciones masivas de rock, obtuve niveles de rendimiento de 15k / segundo. Más notas sobre ContinueOnError, consulte http://docs.mongodb.org/manual/core/write-operations/
Las inserciones de grabación suceden MUY rápido, por lo que terminará con esas inserciones en muy poco tiempo. Ahora es el momento de actualizar los registros relevantes. Haga esto con una recuperación por lotes, mucho más rápido que uno a la vez.
Itere sobre todos sus registros de entrada nuevamente, creando lotes de 15K más o menos. Extraiga las claves (mejor si hay una clave, pero no se puede evitar si no la hay). Recupere este grupo de registros de Mongo con una consulta db.collectionNameBlah.find ({field: {$ in: [1, 2,3 ...}). Para cada uno de estos registros, determine si hay una actualización y, de ser así, emita la actualización, incluida la actualización de la marca de tiempo 'actualizada'.
Desafortunadamente, debemos tener en cuenta que MongoDB 2.4 y posteriores NO incluyen una operación de actualización masiva. Están trabajando en eso.
Puntos clave de optimización:
fuente
No creo que mongodb admita este tipo de inserción selectiva. Tengo el mismo problema que LeMiz, y el uso de la actualización (criterios, newObj, upsert, multi) no funciona bien cuando se trata de una marca de tiempo 'creada' y 'actualizada'. Dada la siguiente declaración upsert:
Escenario n. ° 1: el documento con 'nombre' de 'abc' no existe: el nuevo documento se crea con 'nombre' = 'abc', 'creado' = 14/07/2010 11:11:11 y 'actualizado' = 2010-07-14 11:11:11.
Escenario # 2: el documento con 'nombre' de 'abc' ya existe con lo siguiente: 'nombre' = 'abc', 'creado' = 2010-07-12 09:09:09 y 'actualizado' = 2010-07 -13 10:10:10. Después del upsert, el documento ahora sería el mismo que el resultado en el escenario # 1. No hay forma de especificar en un upsert qué campos se establecerán si se insertan, y qué campos se dejarán solos si se actualizan.
Mi solución fue crear un índice único en los campos de criterios , realizar una inserción e inmediatamente después realizar una actualización solo en el campo 'actualizado'.
fuente
En general, usar la actualización es mejor en MongoDB, ya que solo creará el documento si aún no existe, aunque no estoy seguro de cómo hacerlo con su adaptador de Python.
En segundo lugar, si solo necesita saber si ese documento existe o no, count () que devuelve solo un número será una mejor opción que find_one, que supuestamente transfiere todo el documento desde su MongoDB causando tráfico innecesario.
fuente