Zasadniczo masz 3 przypadki:
- istnieje zarówno książka, jak i recenzja. To jest prosty
$set
- książka istnieje, ale nie recenzja. To wymaga
$push
- książka nie istnieje. To wymaga
{upsert:1}
i$setOnInsert
Nie byłem w stanie znaleźć sposobu na zjednoczenie dwóch z nich bez naruszania integralności danych w przypadku awarii (pamiętaj, że MongoDB nie ma transakcji atomowej).
Więc moje najlepszy pomysł jest następujący:
// 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}
)
Możesz na ślepo uruchomić te trzy aktualizacje w trybie raw, ponieważ nie ma między nimi nakładających się przypadków. Piękno tej rzeczy polega na tym, że wszystkie te operacje są idempotent
. Możesz więc zastosować je raz lub kilka razy i zawsze uzyskać ten sam efekt. Jest to szczególnie ważne w przypadku przełączania awaryjnego. Ponadto nie ma możliwości, aby baza danych była niespójna lub utraciła istniejącą dane w przypadku awarii. W najgorszym przypadku opinia nie zaktualizowany. Wreszcie to powinno zagwarantować spójność danych nawet w przypadku równoczesnych aktualizacji (tzn. w takim przypadku jedna aktualizacja zastąpi drugą, ale nie powinno się skończyć na posiadaniu dwóch dokumentów dla tej samej książki lub dwóch recenzji tego samego użytkownika dla tej samej książki).
Ten późniejszy punkt musi zostać potwierdzony, ponieważ jest tu późno, więc moja analiza może być nieco wątpliwa.
Na koniec, jeśli chcesz zmniejszyć liczbę podróży w obie strony między MongoDB a Twoją aplikacją, możesz spojrzeć na update
polecenie bazy danych
co pozwala na umieszczenie kilku aktualizacji w jednym poleceniu.