Du har i princip 3 fall:
- både boken och recensionen finns. Detta är en enkel
$set
- boken finns men inte recensionen. Detta behöver en
$push
- boken finns inte. Detta behöver
{upsert:1}
och en$setOnInsert
Jag kunde inte hitta ett sätt att förena två av dessa utan att kompromissa med dataintegriteten i händelse av fel (kom ihåg att MongoDB inte har atomär transaktion).
Så min bästa idén är följande:
// Case 1:
db.books.update({isbn:'1234567890',
review: { $elemMatch: {userID: '01234'}}},
{$set: {'review.$.rating': NEW_RATING}}
)
// Case 2:
db.books.update({isbn:'1234567890',
review: { $not: { $elemMatch: {userID: '01234'}}}},
{$push: {review: {rating: NEW_RATING, userID:'01234'}}}
)
// Case 3:
db.books.update({isbn:'1234567890'},
{$setOnInsert: {review: [{rating: NEW_RATING, userID:'01234'}]}},
{upsert:1}
)
Du kan blint köra dessa tre uppdateringar i en raw eftersom det inte finns något överlappande fall mellan dem. Det fina med saken är att alla dessa operationer är idempotenta
. Så du kan applicera dem en eller flera gånger och alltid få samma resultat. Detta är särskilt viktigt vid failover. Dessutom finns det inget sätt för din DB att vara inkonsekvent eller att förlora existerande data vid fel. I värsta fall är recensionen inte uppdaterad. Äntligen bör detta garantera datakonsistens även vid samtidiga uppdateringar (dvs:i så fall kommer en uppdatering att skriva över den andra, men du bör inte ha två dokument för samma bok eller två recensioner av samma användare för samma bok).
Den senare punkten måste bekräftas eftersom det är sent här så min analys kan vara något tveksam.
Som sista anmärkning, om du vill minska antalet tur- och returresor mellan MongoDB och din app, kan du ta en titt på update
databaskommando
så att du kan slå flera uppdateringar i ett kommando.