xticks barplot python mongodb bulkinsert mongodb-query

python - barplot - pandas plot



mongodb: inserte si no existe (8)

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 haya guardado previamente, pero no está en mi actualización.
  • El 95% (estimado) de los registros no se modifican día a día.

Estoy usando el controlador de Python (pymongo).

Lo que hago actualmente es (pseudo-có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 por 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 escueto .... ( http://www.mongodb.org/display/DOCS/Updating )

¿Alguien puede aconsejarme cómo hacerlo más rápido?


1. Usa la actualización.

A partir de la respuesta anterior de Van Nguyen, use la actualización en lugar de guardar. Esto le da acceso a la opción de inserción.

NOTA : Este método anula todo el documento cuando se encuentra ( http://www.mongodb.org/display/DOCS/Updating )

var conditions = { name: ''borne'' } , update = { $inc: { visits: 1 }} , options = { multi: true }; Model.update(conditions, update, options, callback); function callback (err, numAffected) { // numAffected is the number of updated documents })

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, http://www.mongodb.org/display/DOCS/Updating ) ... Entonces, si quieres establecer ...

var query = { name: ''borne'' }; Model.update(query, ***{ name: ''jason borne'' }***, options, callback)

Enviarlo como ...

Model.update(query, ***{ $set: { name: ''jason borne'' }}***, options, callback)

Esto ayuda a evitar la sobrescritura accidental de todos sus documentos con { name: ''jason borne'' } .


A partir de MongoDB 2.4, puede usar $ setOnInsert ( http://docs.mongodb.org/manual/reference/operator/setOnInsert/ )

Establezca ''inserción_date'' usando $ setOnInsert y ''last_update_date'' usando $ set en su comando upsert.

Para convertir su pseudocódigo en un ejemplo de trabajo:

now = datetime.utcnow() for document in update: collection.update_one( {"_id": document["_id"]}, { "$setOnInsert": {"insertion_date": now}, "$set": {"last_update_date": now}, }, upsert=True, )


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 el documento completo de su MongoDB causando tráfico innecesario.


No creo que mongodb apoye este tipo de upserting selectivo. 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:

update( { "name": "abc" }, { $set: { "created": "2010-07-14 11:11:11", "updated": "2010-07-14 11:11:11" }}, true, true )

Escenario n.º 1: el documento con ''nombre'' de ''abc'' no existe: el nuevo documento se crea con ''nombre'' = ''abc'', ''created'' = 2010-07-14 11:11:11 y ''updated'' = 2010-07-14 11:11:11.

Escenario n.º 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 una actualización qué campos se deben establecer si se insertan, y qué campos se deben dejar solos si se actualizan.

Mi solución fue crear un índice único en los campos de critera , realizar una inserción e inmediatamente después realizar una actualización solo en el campo ''actualizado''.


Parece que quieres hacer un "upsert". MongoDB tiene soporte integrado para esto. Pase un parámetro adicional a su llamada de actualización (): {upsert: true}. Por ejemplo:

key = {''key'':''value''} data = {''key2'':''value2'', ''key3'':''value3''}; coll.update(key, data, upsert=True); #In python upsert must be passed as a keyword argument

Esto reemplaza completamente tu bloque if-find-else-update. Se insertará si la clave no existe y se actualizará si lo hace.

Antes de:

{"key":"value", "key2":"Ohai."}

Después:

{"key":"value", "key2":"value2", "key3":"value3"}

También puede especificar qué datos desea escribir:

data = {"$set":{"key2":"value2"}}

Ahora su documento seleccionado actualizará el valor de "key2" solamente y dejará intacto todo lo demás.


Puede usar Upsert con el operador $ setOnInsert.

db.Table.update({noExist: true}, {"$setOnInsert": {xxxYourDocumentxxx}}, {upsert: true})


Siempre puede crear un índice único, lo que hace que MongoDB rechace un guardado en conflicto. Considere lo siguiente hecho usando el shell mongodb:

> db.getCollection("test").insert ({a:1, b:2, c:3}) > db.getCollection("test").find() { "_id" : ObjectId("50c8e35adde18a44f284e7ac"), "a" : 1, "b" : 2, "c" : 3 } > db.getCollection("test").ensureIndex ({"a" : 1}, {unique: true}) > db.getCollection("test").insert({a:2, b:12, c:13}) # This works > db.getCollection("test").insert({a:1, b:12, c:13}) # This fails E11000 duplicate key error index: foo.test.$a_1 dup key: { : 1.0 }


Resumen

  • Usted tiene una colección de registros existente.
  • Tiene un conjunto de registros que contienen actualizaciones de los registros existentes.
  • Algunas de las actualizaciones realmente no actualizan nada, duplican lo que ya tienes.
  • Todas las actualizaciones contienen los mismos campos que ya existen, solo valores posiblemente diferentes.
  • Desea realizar un seguimiento cuando se modificó por última vez un registro, donde realmente cambió un valor.

Tenga en cuenta que presumo PyMongo, cambie para que se adapte a su idioma de elección.

Instrucciones:

  1. Cree la colección con un índice con unique = true para que no obtenga registros duplicados.

  2. Itera 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 dict consistente en los datos que desea insertar, suponiendo que cada uno será un nuevo registro. Agregue las marcas de tiempo ''creado'' y ''actualizado'' a estos. Emita esto como un comando de inserción por lotes con el indicador ''ContinueOnError'' = true, de modo que la inserción de todo lo demás se produzca incluso si hay una clave duplicada allí (lo que parece que habrá). ESTO SUCEDERÁ MUY RÁPIDO. Bulk inserta rock, obtuve niveles de rendimiento de 15k / segundo. Notas adicionales sobre ContinueOnError, vea http://docs.mongodb.org/manual/core/write-operations/

    Las inserciones de registro suceden MUY rápido, por lo que habrá terminado con esas inserciones en 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.

  3. Vuelva a iterar sobre todos sus registros de entrada, creando lotes de 15K o menos. Extraiga las claves (lo mejor es que haya una clave, pero no se puede evitar si no la hay). Recupere este conjunto 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 siguientes NO incluyen una operación de actualización masiva. Están trabajando en eso.

Puntos clave de optimización:

  • Las inserciones acelerarán enormemente sus operaciones a granel.
  • Recuperar registros en masa también acelerará las cosas.
  • Actualizaciones individuales son la única ruta posible ahora, pero 10Gen está trabajando en ello. Presumiblemente, esto será en 2.6, aunque no estoy seguro si estará terminado para entonces, hay muchas cosas que hacer (he estado siguiendo su sistema Jira).