Domanda

Ogni giorno ricevo uno stock di documenti (un aggiornamento).Quello che voglio fare è inserire ogni elemento che non esiste già.

  • Voglio anche tenere traccia della prima volta che li ho inseriti e dell'ultima volta che li ho visti in un aggiornamento.
  • Non voglio avere documenti duplicati.
  • Non voglio rimuovere un documento che è stato precedentemente salvato, ma non è nel mio aggiornamento.
  • Il 95% (stimato) dei record non viene modificato di giorno in giorno.

Sto usando il driver Python (pymongo).

Quello che attualmente faccio è (pseudo-codice):

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)

Il mio problema è che è molto lento (40 minuti per meno di 100.000 record e ne ho milioni nell'aggiornamento).Sono abbastanza sicuro che ci sia qualcosa di integrato per farlo, ma il documento per update() è mmmhhh....un po' conciso....(http://www.mongodb.org/display/DOCS/Updating )

Qualcuno può consigliare come farlo più velocemente?

È stato utile?

Soluzione

Sembra che si desidera fare un "upsert". MongoDB è dotato di supporto per questo. Passare un parametro in più per la vostra chiamata update (): {upsert: true}. Ad esempio:

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

Questo sostituisce del tutto il tuo blocco if-find-else-aggiornamento. Si inserirà se la chiave non esiste e aggiornerà se lo fa.

Prima:

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

Dopo:

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

È inoltre possibile specificare i dati che si desidera scrivere:

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

Ora il documento selezionato verrà aggiornato il valore di "key2" solo e lasciare tutto il resto intatto.

Altri suggerimenti

A partire dal MongoDB 2.4, è possibile utilizzare $ setOnInsert ( http: // docs. mongodb.org/manual/reference/operator/setOnInsert/ )

Set 'insertion_date' utilizzando $ setOnInsert e 'LAST_UPDATE_DATE' utilizzando $ set nel comando upsert.

Per trasformare il vostro pseudocodice in un esempio di lavoro:

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

Si può sempre fare un indice univoco, che fa sì che MongoDB per rifiutare una in conflitto Salva. Si consideri il seguente fatto utilizzando la 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 }

È possibile utilizzare upsert con $ operatore setOnInsert.

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

1.Usa Aggiorna.

Attingendo alla risposta di Van Nguyen sopra, usa aggiorna invece di salva.Questo ti dà accesso all'opzione upsert.

NOTA:Questo metodo sovrascrive l'intero documento quando viene trovato (Dai documenti)

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.Usa $set

Se desideri aggiornare una selezione del documento, ma non l'intero documento, puoi utilizzare il metodo $set con update.(Ancora, Dai documenti)...Quindi, se vuoi impostare...

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

Invialo come...

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

Ciò aiuta a prevenire la sovrascrittura accidentale di tutti i tuoi documenti con { name: 'jason borne' }.

Non credo che supporti questo tipo di mongodb upserting selettivo. Ho lo stesso problema di LeMiz, e con update (criteri, newobj, upsert, Multi) non funziona bene quando si tratta sia con un 'creato' e 'aggiornata' timestamp. Data la seguente dichiarazione upsert:

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

Scenario # 1 - documento con 'nome' di 'abc' non esiste: Nuovo documento viene creato con 'nome' = 'abc', 'creato' = 2010-07-14 11:11:11, e 'aggiornata' = 2010-07-14 11:11:11.

Scenario # 2 - documento con 'nome' di 'abc' esiste già con il seguente: 'Name' = 'abc', 'creato' = 2010-07-12 09:09:09, e 'aggiornato' = 2010-07-13 10:10:10. Dopo l'upsert, il documento sarebbe ora lo stesso del risultato nello scenario # 1. Non c'è modo di specificare in quali campi upsert essere impostati se l'inserimento, e che i campi essere lasciato solo, se l'aggiornamento.

La mia soluzione era quella di creare un indice univoco sulla critera campi, eseguire un'operazione di inserimento, e subito dopo eseguire un aggiornamento solo sul campo 'aggiornata'.

Riepilogo

  • Hai una raccolta esistente di record.
  • Si dispone di un insieme di record che contengono aggiornamenti ai record esistenti.
  • Alcuni aggiornamenti in realtà non aggiornano nulla, duplicano ciò che hai già.
  • Tutti gli aggiornamenti contengono gli stessi campi già presenti, solo possibilmente valori diversi.
  • Si desidera tenere traccia dell'ultima modifica di un record e del punto in cui un valore è stato effettivamente modificato.

Nota, presumo che PyMongo cambi per adattarsi alla tua lingua preferita.

Istruzioni:

  1. Crea la raccolta con un indice con unique=true in modo da non ottenere record duplicati.

  2. Itera sui record di input, creandone batch di circa 15.000 record.Per ogni record nel batch, crea un dict composto dai dati che desideri inserire, presupponendo che ognuno sarà un nuovo record.Aggiungi i timestamp "creato" e "aggiornato" a questi.Emettilo come comando di inserimento batch con il flag 'ContinueOnError'=true, quindi l'inserimento di tutto il resto avviene anche se c'è una chiave duplicata (che sembra che ci sarà).QUESTO ACCADERA' MOLTO VELOCEMENTE.Inserendo in blocco il rock, ho ottenuto livelli di prestazioni di 15k/secondo.Ulteriori note su ContinueOnError, vedere http://docs.mongodb.org/manual/core/write-operazioni/

    Gli inserimenti dei record avvengono MOLTO velocemente, quindi avrai finito con quegli inserti in pochissimo tempo.Ora è il momento di aggiornare i record pertinenti.Fallo con un recupero batch, molto più veloce di uno alla volta.

  3. Ripeti nuovamente tutti i record di input, creando batch di circa 15K.Estrai le chiavi (meglio se ce n'è una, ma non puoi aiutare se non c'è).Recupera questo gruppo di record da Mongo con db.collectionNameBlah.find({ field :{$in:[ 1, 2,3 ...}) interrogazione.Per ciascuno di questi record, determina se è presente un aggiornamento e, in tal caso, rilascia l'aggiornamento, incluso l'aggiornamento del timestamp "aggiornato".

    Sfortunatamente, dobbiamo notare che MongoDB 2.4 e versioni precedenti NON includono un'operazione di aggiornamento di massa.Ci stanno lavorando.

Punti chiave di ottimizzazione:

  • Gli inserti velocizzeranno notevolmente le vostre operazioni di massa.
  • Anche il recupero dei record in massa accelererà le cose.
  • Gli aggiornamenti individuali sono l'unica via possibile ora, ma la 10Gen ci sta lavorando.Presumibilmente, sarà nella versione 2.6, anche se non sono sicuro che sarà finito per allora, ci sono molte cose da fare (ho seguito il loro sistema Jira).

In generale, usando update è migliore in MongoDB come sarà solo creare il documento se non esiste ancora, anche se non sono sicuro di come il lavoro che con la scheda di pitone.

In secondo luogo, se avete solo bisogno di sapere se quel documento esiste, count (), che restituisce solo un numero sarà una scelta migliore rispetto find_one che presumibilmente trasferire l'intero documento dal MongoDB causando traffico superfluo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top