mongodb: insérer si n'existe pas
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à.
- J'ai aussi envie de garder une trace de la première fois que j'ai inséré, et la dernière fois que j'ai vu dans une mise à jour.
- Je ne veux pas avoir de doublons de documents.
- Je ne veux pas supprimer un document qui a déjà été enregistré, mais n'est pas dans ma mise à jour.
- 95% (estimation) des enregistrements ne sont pas modifiées au jour le jour.
Je suis en utilisant le pilote Python (pymongo).
Ce que je fais (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 enregistrements, et j'ai des millions d'entre eux dans la mise à jour).
Je suis assez sûr il y a quelque chose builtin pour le faire, mais le document de mise à jour() est mmmhhh.... un peu laconique.... (http://www.mongodb.org/display/DOCS/Updating )
Quelqu'un peut conseiller sur la façon de le faire plus vite?
Vous devez vous connecter pour publier un commentaire.
Dirait que vous voulez faire un "upsert". MongoDB a bâti-support pour cela. Passer un paramètre supplémentaire à votre mise à jour() appel: {upsert:true}. Par exemple:
Cela remplace votre si-trouver-autre-mise à jour de bloquer l'intégralité. Il va insérer si la clé n'existe pas et sera mise à jour si elle n'.
Avant:
Après:
Vous pouvez également spécifier les données que vous voulez écrire:
Maintenant votre document sélectionné se mettra à jour la valeur de "key2", et laisser tout le reste intact.
Que de MongoDB, 2.4, vous pouvez utiliser $setOnInsert (http://docs.mongodb.org/manual/reference/operator/setOnInsert/)
Set 'insertion_date' à l'aide de $setOnInsert et "last_update_date' à l'aide de $set dans votre upsert commande.
De transformer votre pseudo dans un exemple de travail:
Vous pouvez toujours faire un index unique, ce qui provoque MongoDB pour rejeter un conflit d'enregistrer. Considérez les points suivants fait à l'aide de la mongodb shell:
Vous pouvez utiliser Upsert avec $setOnInsert opérateur.
1. L'Utilisation De Mise À Jour.
Dessin de Van Nguyen réponse ci-dessus, utilisez mise à jour au lieu de la sauver. Cela vous donne accès à la upsert option.
NOTE: Cette méthode remplace la totalité du document, une fois trouvé (À partir de la documentation)
1.un. Utilisez $set
Si vous voulez mettre à jour une sélection du document, mais pas l'ensemble de la chose, vous pouvez utiliser le $méthode de jeu avec la mise à jour. (encore une fois, À partir de la documentation)...
Donc, si vous souhaitez définir...
L'envoyer en tant que...
Cela permet d'éviter de remplacer accidentellement tous vos document(s) avec
{ name: 'jason borne' }
.Je ne pense pas que mongodb prend en charge ce type de sélectivité upserting. J'ai le même problème que LeMiz, et à l'aide de mise à jour(critères, newObj, upsert, multi) ne fonctionne pas bien lorsque vous traitez avec un 'créé' et 'mise à jour' timestamp. Le suivant upsert déclaration:
Scénario #1 - le document avec le 'nom' de 'abc' n'existe pas:
Un nouveau document est créé avec 'nom' = 'abc', 'created' = 2010-07-14 11:11:11, et de "mise à jour' = 2010-07-14 11:11:11.
Scénario #2 - le document avec le 'nom' de 'abc' existe déjà avec les éléments suivants:
'name' = 'abc', 'created' = 2010-07-12 09:09:09 et 'mis à jour' = 2010-07-13 10:10:10.
Après la upsert, le document serait désormais le même que le résultat dans le scénario n ° 1. Il n'y a aucun moyen de spécifier dans un upsert les champs si l'insertion, et les champs à être laissé seul si la mise à jour.
Ma solution a été de créer un index unique sur la critères champs, effectuer une insertion, et par la suite effectuer une mise à jour juste sur la 'mise à jour' de champ.
Résumé
Remarque, je suis en supposant PyMongo, modifier pour l'adapter à la langue de votre choix.
Instructions:
Créer la collection avec un index unique=true si vous n'obtenez pas les enregistrements en double.
Itérer sur les enregistrements d'entrée, la création de lots de 15 000 dossiers environ. Pour chaque enregistrement dans le lot, créer un dict comprenant les données que vous souhaitez insérer, en supposant chacun va être un nouveau record. Ajouter le "créé" et "mise à jour" les horodatages pour ces. Question ce qu'un lot insérer la commande avec le "ContinueOnError' flag=true, afin que l'insertion de tout le reste se passe même si il y a un double de la clé là-bas (on dirait qu'il y en aura). CELA VA ARRIVER TRÈS VITE. Des insertions de rock, j'ai pris de l'15k/deuxième niveaux de performance. Plus de détails sur l'ContinueOnError, voir http://docs.mongodb.org/manual/core/write-operations/
Enregistrement insère arriver TRÈS vite, de sorte que vous allez faire avec les insertions en un rien de temps. Maintenant, il est temps de mettre à jour les dossiers pertinents. Faire cela avec un lot de récupération, beaucoup plus vite qu'un à la fois.
Itérer sur tous les enregistrements d'entrée, de nouveau, la création de lots de 15K ou donc. Extrait des clés (mieux si il y a une clé, mais ne peut pas être aidé, si il n'y en a pas). Récupérer ce tas de dossiers de Mongo avec un db.collectionNameBlah.find({ field : { $dans : [ 1, 2,3 ...}) de la requête. Pour chacun de ces dossiers, de déterminer si une mise à jour, et si oui, la question de la mise à jour, y compris la mise à jour de la 'mise à jour' timestamp.
Malheureusement, nous devons noter, MongoDB 2.4 et ci-dessous n'incluent PAS la mise à jour globale de l'opération. Ils travaillent sur que.
Clé De L'Optimisation Des Points:
En général, l'utilisation de la mise à jour est mieux dans MongoDB comme il vient de créer le document s'il n'existe pas encore, si je ne suis pas sûr de la façon de travailler avec votre python adaptateur.
Seconde, si vous avez seulement besoin de savoir si ce document existe, count() qui retourne uniquement un numéro sera une meilleure option que find_one qui soi-disant transfert de l'ensemble du document à partir de votre MongoDB provoquant un trafic inutile.