Comment convertir simultanément des documents dans MongoDb
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
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
}