Question

Chaque jour, je reçois un stock de documents (une mise à jour). Ce que je veux faire est d'insérer chaque élément qui n'existe pas déjà.

  • Je veux aussi garder une trace de la première fois que je les inséra, et la dernière fois que je les ai vus dans une mise à jour.
  • Je ne veux pas avoir des documents en double.
  • Je ne veux pas supprimer un document qui a déjà été enregistré, mais pas dans ma mise à jour.
  • 95% (estimation) des enregistrements ne sont pas modifiés de jour en jour.

J'utilise le pilote Python (de pymongo).

Ce que je fais actuellement est (pseudo-code):

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)

Mon problème est qu'il est très lent (40 minutes pour moins de 100 000 dossiers, et j'ai des millions d'entre eux dans la mise à jour). Je suis assez sûr qu'il ya quelque chose builtin pour ce faire, mais le document pour la mise à jour () est hummm .... un peu laconique .... ( http://www.mongodb.org/display/DOCS/Updating )

Quelqu'un peut-il conseiller sur la façon de le faire plus vite?

Était-ce utile?

La solution

On dirait que vous voulez faire une « upsert ». MongoDB a un support intégré pour cela. Passez un paramètre supplémentaire à votre appel mise à jour (): {upsert: true}. Par exemple:

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

Il remplace votre bloc si-find-else mise à jour entièrement. Il insérera si la clé n'existe pas et mettra à jour si elle le fait.

Avant:

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

Après:

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

Vous pouvez également spécifier les données que vous voulez écrire:

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

Maintenant, votre document sélectionné mettra à jour la valeur de « key2 » seulement et laisser tout le reste intact.

Autres conseils

Au MongoDB 2.4, vous pouvez utiliser setOnInsert $ ( http: // docs. mongodb.org/manual/reference/operator/setOnInsert/ )

Set 'insertion_date' en utilisant $ setOnInsert et 'last_update_date' en utilisant $ set dans votre commande upsert.

Pour transformer votre pseudo dans un exemple de travail:

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

Vous pouvez toujours faire un index unique, ce qui provoque MongoDB de rejeter un conflit sauvegarde. Considérez ce qui suit fait à l'aide du 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 }

Vous pouvez utiliser Upsert avec l'opérateur de setOnInsert $.

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

1. Utilisez Mise à jour.

Dessin de la réponse de Van Nguyen ci-dessus, utilisez la mise à jour au lieu de sauver. Cela vous donne accès à l'option upsert.

NOTE : Cette méthode remplace le document entier lorsque trouvé ( A partir de la documentation )

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. Utilisez ensemble $

Si vous souhaitez mettre à jour une sélection du document, mais pas tout, vous pouvez utiliser la méthode $ set avec mise à jour. (Encore une fois, De la documentation ) ... Donc, si vous voulez définir ...

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

Envoyer comme ...

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

Cela permet d'éviter tout écrasement accidentel de votre document (s) avec { name: 'jason borne' }.

Je ne pense pas que MongoDB supporte ce type de upserting sélectif. J'ai le même problème que LeMiz, et en utilisant mise à jour (critères, newobj, upsert, multi) ne fonctionne pas correctement lorsqu'il s'agit à la fois un horodatage « créé » et « mis à jour ». Compte tenu de la déclaration upsert suivante:

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

Scénario 1 - document avec « nom » de « abc » n'existe pas: Nouveau document est créé avec 'nom' = 'abc', 'créé' = 2010-07-14 11:11:11, et 'mis à jour' = 2010-07-14 11:11:11.

Scénario 2 - document avec « nom » de « abc » existe déjà avec les éléments suivants: 'Name' = 'abc', 'créé' = 2010-07-12 09:09:09, et 'mis à jour' = 2010-07-13 10:10:10. Après la upsert, le document serait maintenant la même chose que le résultat dans le scénario n ° 1. Il n'y a aucun moyen de préciser dans un upsert quels champs régler si l'insertion, et quels domaines rester seul si la mise à jour.

Ma solution a été de créer un index unique sur la critera champs, effectuer une insertion, et immédiatement après une mise à jour effectuer uniquement sur le champ mis à jour.

Résumé

  • Vous avez une collection existante de dossiers.
  • Vous avez un ensemble de dossiers qui contiennent les mises à jour des enregistrements existants.
  • Certaines mises à jour ne mettent pas à jour vraiment quoi que ce soit, ils reproduire ce que vous avez déjà.
  • Toutes les mises à jour contiennent les mêmes champs qui sont déjà là, juste des valeurs peut-être différentes.
  • Vous voulez savoir quand un enregistrement a été changée où une valeur réellement changé.

Note, je présume PyMongo, changer en fonction de langue de votre choix.

Instructions:

  1. Création de la collection avec un index avec unique, = true si vous ne recevez pas les doublons.

  2. itérer sur vos enregistrements d'entrée, créant ainsi des lots d'entre eux de 15.000 dossiers environ. Pour chaque enregistrement dans le lot, créez un dict comprenant les données que vous souhaitez insérer, en présumant chacun va être un nouveau record. Ajouter les timestamps « créé » et « mis à jour » ces derniers. Problème cela comme une commande d'insertion de lot avec le drapeau « ContinueOnError » = true, donc l'insertion de tout se passe même si le reste il y a une clé en double là-dedans (ce qui sonne comme il y aura). Ce sera le cas très rapide. rock inserts en vrac, je l'ai obtenu 15k / deuxième niveaux de performance. D'autres notes sur ContinueOnError, consultez http://docs.mongodb.org/manual/core/ écrire-opérations /

    inserts de disques se produisent très rapidement, de sorte que vous allez être avec les inserts en un rien de temps. Maintenant, il est temps de mettre à jour les dossiers pertinents. Pour ce faire, avec une extraction par lots, beaucoup plus rapide que l'un à la fois.

  3. itérer sur tous vos enregistrements d'entrée à nouveau, créer des lots de 15K ou plus. Extraire les clés (mieux s'il y a une clé, mais ne peut pas être aidé s'il n'y a pas). Récupérer ce groupe d'enregistrements de Mongo avec un db.collectionNameBlah.find ({champ: {$ dans: [1, 2,3 ...}) requête. Pour chacun de ces dossiers, déterminer s'il y a une mise à jour, et le cas échéant, émettre la mise à jour, y compris la mise à jour de l'horodatage « mise à jour ».

    Malheureusement, il faut noter, MongoDB 2.4 et ci-dessous ne comprennent pas une opération de mise à jour groupée. Ils travaillent là-dessus.

Points d'optimisation clés:

  • Les inserts vont accélérer considérablement vos opérations en vrac.
  • Récupérer des enregistrements en masse va accélérer les choses aussi.
  • Mises à jour individuelles sont la seule voie possible maintenant, mais 10gen travaille là-dessus. On peut supposer que ce sera en 2.6, bien que je ne sais pas si ce sera fini d'ici là, il y a beaucoup de choses à faire (j'ai suivi leur système Jira).

En général, en utilisant la mise à jour est mieux MongoDB car il suffit de créer le document si elle n'existe pas encore, mais je ne suis pas sûr de savoir comment fonctionne qu'avec l'adaptateur python.

En second lieu, si vous avez besoin de savoir si oui ou non ce document existe, count () qui retourne un nombre sera une meilleure option que find_one qui transfèrent soi-disant le document entier de votre MongoDB causant le trafic inutile.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top