2012-05-20 7 views
18

Considérant une structure simple document mongo:MongoDB: upserting: valeur que définir si le document est inséré

{_id, firstTime, lastTime}

Le client a besoin d'insérer un document avec une pièce d'identité connue, ou mettre à jour un document existant. Le paramètre 'lastTime' doit toujours être défini sur une date limite. Pour le 'firstTime', si un document est inséré, le 'firstTime' doit être réglé sur l'heure actuelle. Cependant, si le document est déjà créé, 'firstTime' reste inchangé. Je voudrais le faire purement avec upserts (pour éviter les recherches).

J'ai exploré le http://www.mongodb.org/display/DOCS/Updating, mais je ne vois pas comment cette opération particulière peut être effectuée.

Je ne crois pas que ce soit quelque chose de déraisonnable, il ya des opérations $ push et $ addToSet qui le font effectivement sur les champs de tableau, rien de ce qui ferait la même chose sur des champs simples. C'est comme s'il devait y avoir quelque chose comme l'opération $ setIf.

+0

* si le document est déjà créé, le champ doit rester inchangé * il ne s'appelle pas upsert (comme dans votre titre) –

+0

@ om-nom-nom J'ai décrit le cas le plus simple, j'ai mis à jour le description, il y a donc un champ qui est toujours mis à jour. –

+1

On dirait que cette fonctionnalité est ciblée pour 2.4: https://jira.mongodb.org/browse/SERVER-340 – JohnnyHK

Répondre

27

J'ai couru dans le même problème et il n'y avait pas de solution simple pour < 2.4 mais depuis 2.4 $ setOnInsert opérateur Faisons-le exactement.

db.collection.update(<query>, 
         { $setOnInsert: { "firstTime": <TIMESTAMP> } }, 
         { upsert: true } 
        ) 

Voir le 2.4 release notes of setOnInsert pour plus d'informations.

1

Il n'y a aucun moyen de le faire avec un seul upsert. Vous devez le faire en 2 opérations - essayez d'abord d'insérer le document, s'il existe déjà, l'insertion échouera en raison d'une violation de clé en double sur l'index _id. Ensuite, vous effectuez une opération de mise à jour pour définir LastTime à maintenant.

+0

À mon humble avis, cela va vaincre le but, faire, et en attendant, la première opération revient à rechercher synchrone. Je ne vois pas de différence dans "tenter d'échouer" et trouver et mettre à jour ... –

+1

Il existe une condition de concurrence avec l'approche de trouver et mettre à jour et mettre à jour avec plusieurs auteurs simultanés. Si un autre auteur entre et crée/change le document entre le moment où le premier thread a fait la lecture et le moment où il écrit, vous pouvez finir par écraser l'écriture que l'autre thread a fait. – stbrody

+0

C'est pourquoi je le veux en une seule opération write(), donc demander à Mongo de l'exécuter atomiquement. –

1

je suis tombé sur un problème similaire lors d'une tentative de upsert documents basés sur le contenu existant - peut-être que cette solution fonctionne pour vous aussi:

Essayez de supprimer l'attribut _id de votre dossier et ne l'utiliser que dans la requête partie de votre mise à jour (vous devrez traduire de pymongo parler ...)

myid = doc.get('_id') 
del doc['_id'] 
mycollection.update({'_id':myid}, {'$set':doc}, upsert=True) 
Questions connexes