2013-02-06 17 views
8

Niedawno zacząłem używać MongoDB i mam pytanie dotyczące aktualizacji tablic w dokumencie. Mam strukturę takiego:MongoDB aktualizuje wiele rekordów tablicy

{ 
"_id" : ObjectId(), 
"post" : "", 
"comments" : [ 
     { 
       "user" : "test", 
       "avatar" : "/static/avatars/asd.jpg", 
       "text" : "....." 
     } 
     { 
       "user" : "test", 
       "avatar" : "/static/avatars/asd.jpg", 
       "text" : "....." 
     } 
     { 
       "user" : "test", 
       "avatar" : "/static/avatars/asd.jpg", 
       "text" : "....." 
     } 
     ... 
    ] 
} 

Próbuję wykonać następujące zapytanie:

update({"comments.user":"test"},{$set:{"comments.$.avatar": "new_avatar.jpg"}},false,true) 

Problemem jest to, że aktualizuje wszystkie dokumenty, ale aktualizuje tylko pierwszy element tablicy w każdym dokument. Czy istnieje sposób aktualizacji wszystkich elementów tablicy lub powinienem spróbować zrobić to ręcznie? Dzięki.

Odpowiedz

19

Nie można modyfikować wielu elementów tablicy w pojedynczej operacji aktualizacji. W związku z tym konieczne będzie powtórzenie aktualizacji w celu przeprowadzenia migracji dokumentów wymagających modyfikacji wielu elementów tablicy. Można to zrobić przez iteracja każdego dokumentu w kolekcji, wielokrotnie zastosowaniu aktualizacji z $elemMatch aż dokumencie wszystkich swoich uwag wymienić, np:

 
db.collection.find().forEach(function(doc) { 
    do { 
    db.collection.update({_id: doc._id, 
          comments:{$elemMatch:{user:"test", 
               avatar:{$ne:"new_avatar.jpg"}}}}, 
         {$set:{"comments.$.avatar":"new_avatar.jpg"}}); 
    } while (db.getPrevError().n != 0); 
}) 

Zauważ, że jeśli skuteczność tej operacji jest wymóg w swojej aplikacji powinieneś znormalizować swój schemat tak, aby lokalizacja awatara użytkownika była przechowywana w jednym dokumencie, a nie w każdym komentarzu.

+0

dzięki za odpowiedź. Cóż, odkąd mongo nie ma złączeń, chciałbym stworzyć system blogów z komentarzami i chciałbym pokazać każdemu użytkownikowi awatara obok jego komentarzy. Wszelkie sugestie, w jaki sposób powinienem strukturyzować mój schemat? –

+0

Konceptualnie trzeba przechowywać gdzieś użytkownika => mapę awatara. Możesz przechowywać awatar każdego użytkownika w dokumencie użytkownika (w kolekcji użytkowników), lub możesz przechowywać mapę użytkownika => awatara dla komentujących dany wpis blogu w polu tego wpisu wpisu blogu. Zobacz http://docs.mongodb.org/manual/applications/database-references/ dla lekkiego traktowania normalizacji w MongoDB i http://docs.mongodb.org/manual/use-cases/storing-comments/ dla analiza przypadków użycia "komentarzy użytkownika". –

+0

Tak, znalazłem rozwiązanie tuż przed tym, jak zobaczyłem twoją odpowiedź. W każdym razie bardzo dziękuję @Jason. –

5

Jednym z rozwiązań może być utworzenie funkcji, która będzie używana z opcją forEach i jej ocena (tak, aby działała szybko). Zakładając, że twoja kolekcja to "artykuł", możesz uruchomić:

-1

Wydaje się, że można to zrobić:

db.yourCollection.update({"comments.user":"test"},{$set:{"comments.0.avatar": "new_avatar.jpg", "comments.1.avatar": "new_avatar.jpg", etc...}) 

Więc jeśli masz małą liczbę znanych elementów tablicy, to może być trochę łatwiej zrobić. Jeśli chcesz czegoś takiego jak "comments. *. Avatar" - nie wiesz, jak to zrobić. To prawdopodobnie nie jest tak dobry, że masz tyle duplikacji danych tho ..

1

Jeśli znasz indeksy chcesz zaktualizować można to zrobić bez żadnych problemów, takich jak ten:

var update = { $set: {} }; 
for (var i = 0; i < indexesToUpdate.length; ++i) { 
    update.$set[`comments.${indexesToUpdate[i]}. avatar`] = "new_avatar.jpg"; 
} 
Comments.update({ "comments.user":"test" }, update, function(error) { 
    // ... 
}); 
  • mieć świadomość to IDE nie zaakceptuje składni, ale możesz ją zignorować.
Powiązane problemy