Comment convertir simultanément des documents dans MongoDb

Sep 24 2020

Je travaille sur un système distribué en temps réel avec plusieurs nœuds écrivant des mises à jour dans la même collection MongoDb.

Par souci de simplicité, le document ressemble à ceci:

{
  id: 1,
  name: foo,
  version: 1
}

Sur les mises à jour, la version est garantie d'augmenter mais il est possible que les messages arrivent dans le désordre. Je dois toujours enregistrer la dernière version de la collection.

En SQL, je ferais quelque chose comme ceci:

  BEGIN TRAN
 
    IF EXISTS ( SELECT * FROM myTable WITH (UPDLOCK) WHERE id= @id )
 
      UPDATE myTable
         SET Name= @Name,
             Version = @Version
       WHERE Id = @id 
       AND Version < @Version
 
    ELSE 
 
      INSERT myTable ( Id, Name, Version )
      VALUES ( @Id, @Name, @Version );
 
  COMMIT

MISE À JOUR en réponse aux commentaires: voici ce que j'ai essayé:

await _collection.ReplaceOneAsync(
   x => x.Id == id && x.Version <= payload.Version,
   options: new ReplaceOptions {IsUpsert = true},
   replacement: payload 
);

Lorsque vous essayez de mettre à jour un document existant, obtenez cette exception:

MongoDB.Driver.MongoWriteException: une opération d'écriture a généré une erreur. Collection d'erreurs de clé en double E11000: TEST_948e4e5acae041cca607e9f523dc0194.TestEntityParent index: id dup key: {_id: 500}

Réponses

1 Joe Sep 25 2020 at 17:02

Je ne sais pas comment faire cela avec C #. Un exemple de mise à jour $condvia le shell mongo:

function testUpdate(updated, value) {
    return db.test.update(
             {test: 1},
             [{$set: {
                     monkies: {
                         $cond:{ if: {$or:[
                                      {$gt: [updated, "$version"]},
                                      {$eq: [{$type:"$version"},"missing"]} ]}, then: value, else: "$monkies"
                         }
                     },
                     version: {
                         $cond:{ if: {$or:[
                                      {$gt: [updated, "$version"]},
                                      {$eq: [{$type:"$version"},"missing"]} ]}, then: updated, else: "$version"
                         }
                     }
             }}],
             {upsert: true}
     
    )
}

La première mise à jour sur une collection vide ne correspond à rien et ajoute 1 document:

replset:PRIMARY> testUpdate(1,1)
WriteResult({
    "nMatched" : 0,
    "nUpserted" : 1,
    "nModified" : 0,
    "_id" : ObjectId("5f6e2116d7a583eef6ad49f9")
})

Une mise à jour avec une version supérieure correspond à 1 document et en modifie 1:

replset:PRIMARY> testUpdate(2,3)
WriteResult({ 
     "nMatched" : 1, 
     "nUpserted" : 0, 
     "nModified" : 1 
})

Une mise à jour avec une version inférieure correspond à 1 document, mais met à jour 0:

replset:PRIMARY> testUpdate(1,4)
WriteResult({ 
    "nMatched" : 1, 
    "nUpserted" : 0, 
    "nModified" : 0 
})

Le document résultant après tout cela:

replset:PRIMARY> db.test.find()
{ 
  "_id" : ObjectId("5f6e2116d7a583eef6ad49f9"), 
  "test" : 1, 
  "monkies" : 3, 
  "version" : 2 
}