Fondamentalmente hai 3 casi:
- sia il libro che la recensione esistono. Questo è un semplice
$set
- il libro esiste ma non la recensione. Questo richiede un
$push
- il libro non esiste. Questo richiede
{upsert:1}
e un$setOnInsert
Non sono riuscito a trovare un modo per unificare due di questi senza compromettere l'integrità dei dati in caso di errore (ricorda che MongoDB non ha una transazione atomica).
Quindi mio la migliore idea è la seguente:
// 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}
)
Puoi eseguire alla cieca questi tre aggiornamenti in un raw poiché non ci sono casi sovrapposti tra di loro. Il bello è che tutte queste operazioni sono idempotent
. Quindi puoi applicarli una o più volte e ottenere sempre lo stesso risultato. Ciò è particolarmente importante in caso di failover. Inoltre, non c'è modo che il tuo DB sia incoerente o perda esistente dati in caso di guasto. Nel peggiore dei casi, la recensione non aggiornato. Infine questo dovrebbe garantire la coerenza dei dati anche in caso di aggiornamenti simultanei (es.:in tal caso, un aggiornamento sovrascriverà l'altro, ma non dovresti avere due documenti per lo stesso libro o due recensioni dello stesso utente per lo stesso libro).
Questo punto successivo deve essere confermato poiché qui è tardi, quindi la mia analisi potrebbe essere alquanto dubbia.
Come nota finale, se desideri ridurre il numero di viaggi di andata e ritorno tra MongoDB e la tua app, potresti dare un'occhiata a update
comando database
permettendoti di racchiudere diversi aggiornamenti in un unico comando.