Chaque jour, je reçois un stock de documents (une mise à jour). Ce que je veux faire, c'est insérer chaque élément qui n'existe pas déjà.
- Je veux également garder une trace de la première fois que je les ai insérés et de la dernière fois que je les ai vus dans une mise à jour.
- Je ne veux pas avoir de documents en double.
- Je ne souhaite pas supprimer un document qui a déjà été enregistré, mais qui ne figure pas dans ma mise à jour.
- 95% (estimé) des enregistrements ne sont pas modifiés au jour le jour.
J'utilise le pilote Python (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 enregistrements, et j'en ai des millions dans la mise à jour). Je suis sûr qu'il y a quelque chose de intégré pour faire cela, mais le document pour update () est mmmhhh .... un peu laconique .... ( http://www.mongodb.org/display/DOCS/Updating )
Quelqu'un peut-il conseiller comment le faire plus rapidement?
Depuis MongoDB 2.4, vous pouvez utiliser $ setOnInsert ( http://docs.mongodb.org/manual/reference/operator/setOnInsert/ )
Définissez 'insertion_date' en utilisant $ setOnInsert et 'last_update_date' en utilisant $ set dans votre commande upsert.
Pour transformer votre pseudocode en un exemple fonctionnel:
la source
Vous pouvez toujours créer un index unique, ce qui oblige MongoDB à rejeter une sauvegarde en conflit. Considérez ce qui suit en utilisant le shell mongodb:
la source
Vous pouvez utiliser Upsert avec l'opérateur $ setOnInsert.
la source
1. Utilisez Update.
En vous inspirant de la réponse de Van Nguyen ci-dessus, utilisez update au lieu de save. Cela vous donne accès à l'option upsert.
REMARQUE : cette méthode remplace le document entier lorsqu'il est trouvé (à partir de la documentation )
1.a. Utiliser $ set
Si vous souhaitez mettre à jour une sélection du document, mais pas le tout, vous pouvez utiliser la méthode $ set avec update. (encore une fois, à partir de la documentation ) ... Donc, si vous voulez définir ...
Envoyez-le comme ...
Cela permet d'éviter d'écraser accidentellement tous vos documents avec
{ name: 'jason borne' }
.la source
Résumé
Remarque, je suppose que PyMongo, changez en fonction de la langue de votre choix.
Instructions:
Créez la collection avec un index avec unique = true afin de ne pas obtenir d'enregistrements en double.
Parcourez vos enregistrements d'entrée, en créant des lots de 15 000 enregistrements environ. Pour chaque enregistrement du lot, créez un dict composé des données que vous souhaitez insérer, en supposant que chacun sera un nouvel enregistrement. Ajoutez-y les horodatages «créés» et «mis à jour». Émettez-le comme une commande d'insertion par lots avec le drapeau 'ContinueOnError' = true, de sorte que l'insertion de tout le reste se produit même s'il y a une clé en double (ce qui semble être le cas). CELA SE PASSE TRES RAPIDEMENT. Bulk inserts rock, j'ai obtenu des performances de 15k / seconde. Notes complémentaires sur ContinueOnError, voir http://docs.mongodb.org/manual/core/write-operations/
Les insertions d'enregistrement se produisent TRÈS rapidement, vous en aurez donc fini avec ces insertions en un rien de temps. Il est maintenant temps de mettre à jour les enregistrements pertinents. Faites-le avec une récupération par lots, beaucoup plus rapidement qu'une à la fois.
Répétez tous vos enregistrements d'entrée en créant des lots d'environ 15 Ko. Extraire les clés (mieux s'il y a une clé, mais ne peut pas être aidé s'il n'y en a pas). Récupérez ce groupe d'enregistrements de Mongo avec une requête db.collectionNameBlah.find ({field: {$ in: [1, 2,3 ...}). Pour chacun de ces enregistrements, déterminez s'il existe une mise à jour et, le cas échéant, exécutez la mise à jour, y compris la mise à jour de l'horodatage «mis à jour».
Malheureusement, nous devons noter que MongoDB 2.4 et les versions antérieures n'incluent PAS une opération de mise à jour en masse. Ils y travaillent.
Points d'optimisation clés:
la source
Je ne pense pas que mongodb supporte ce type de soulèvement sélectif. J'ai le même problème que LeMiz, et l'utilisation de la mise à jour (critères, newObj, upsert, multi) ne fonctionne pas correctement lorsqu'il s'agit à la fois d'un horodatage «créé» et «mis à jour». Compte tenu de la déclaration upsert suivante:
Scénario n ° 1 - le document avec 'nom' ou 'abc' n'existe pas: un nouveau document est créé avec 'name' = 'abc', 'created' = 2010-07-14 11:11:11 et 'updated' = 2010-07-14 11:11:11.
Scénario n ° 2 - le document avec 'nom' de 'abc' existe déjà avec les éléments suivants: 'name' = 'abc', 'created' = 2010-07-12 09:09:09, et 'updated' = 2010-07 -13 10:10:10. Après l'upsert, le document serait désormais le même que le résultat du scénario n ° 1. Il n'y a aucun moyen de spécifier dans un upsert quels champs seront définis en cas d'insertion, et quels champs rester seuls en cas de mise à jour.
Ma solution était de créer un index unique sur les champs de critères , d'effectuer une insertion, et immédiatement après effectuer une mise à jour juste sur le champ «mis à jour».
la source
En général, l'utilisation de la mise à jour est meilleure dans MongoDB car elle créera simplement le document s'il n'existe pas encore, bien que je ne sache pas comment travailler avec votre adaptateur python.
Deuxièmement, si vous avez seulement besoin de savoir si ce document existe ou non, count () qui ne retourne qu'un nombre sera une meilleure option que find_one qui est censé transférer le document entier de votre MongoDB provoquant un trafic inutile.
la source