Jeśli najpierw budować swoje modele ze stowarzyszeniem belong_to i has_many a potem sobie sprawę, że trzeba przenieść do embedded_in i embeds_many zrzeszania się, w jaki sposób można by to zrobić bez unieważniania tysiące zapisów? Musisz je jakoś zmigrować.Jak przeprowadzić migrację z belongs_to do embedded_in w Mongoid?
Odpowiedz
nie jestem więc pewien, że moje rozwiązanie jest słuszne czy nie. To jest coś, co możesz spróbować osiągnąć.
Załóżmy, że masz modeli - jak to
#User Model
class User
include Mongoid::Document
has_many :books
end
#Book Model
class Book
include Mongoid::Document
field :title
belongs_to :user
end
W pierwszym etapie będę stworzyć kolejny model, który jest podobny do modelu Book powyżej, ale jest osadzony zamiast odwoływać.
#EmbedBook Model
class EmbedBook
include Mongoid::Document
field :title
embedded_in :user
end
#User Model (Update with EmbedBook Model)
class User
include Mongoid::Document
embeds_many :embed_books
has_many :books
end
Następnie utwórz Mongoid Migracja z czymś takim na powyższym przykładzie
class ReferenceToEmbed < Mongoid::Migration
def up
User.all.each do |user|
user.books.each do |book|
embed_book = user.embed_books.new
embed_book.title = book.title
embed_book.save
book.destroy
end
end
end
def down
# I am not so sure How to reverse this migration so I am skipping it here
end
end
Po przeprowadzeniu migracji. Z tego miejsca widać, że książki referencyjne są osadzone, ale nazwa wbudowanego modelu to EmbedBook, a model Book nadal istnieje.
Zatem następnym krokiem będzie stworzenie książki modelowej jako osadzonej.
class Book
include Mongoid::Document
embedded_in :user
field :title
end
class User
include Mongoid::Document
embeds_many :books
embeds_many :embed_books
end
Więc następnym będzie migrować typ embedbook do Book Type
class EmbedBookToBook < Mongoid::Migration
def up
User.all.each do |user|
user.embed_books.each do |embed_book|
book = user.books.new
book.title = embed_book.title
book.save
embed_book.destroy
end
end
def down
# I am skipping this portion. Since I am not so sure how to migrate back.
end
end
Teraz Jeśli widzisz Book zmienia się odwoływać do osadzonych. Możesz usunąć model EmbedBook, aby zmiany zostały zakończone.
- To tylko sugestia. Wypróbuj to w swoim rozwoju przed próbą produkcji. Ponieważ uważam, że w mojej sugestii może być coś nie tak.
10gen ma kilka artykułów na temat modelowania danych, które mogłyby być użyteczne:
Pamiętaj, że istnieją dwa ograniczenia MongoDB, jeśli chodzi o osadzanie:
- wielkość limitu dokument jest 16MB - oznacza to maksymalną liczbę osadzonych dokumentów, nawet jeśli tylko osadzić ich celem ID
- jeśli kiedykolwiek chcesz wyszukać we wszystkich osadzonych dokumentów z najwyższym poziomie, to nie osadzać, ale zamiast używać odwołuje dokumentów!
Spróbuj kroki:
W
User
modelu opuścićhas_many :books
relację i dodaj osadzony relację z inną nazwą, aby nie przesłonić metodębooks
.class User include Mongoid::Document has_many :books embeds_many :embedded_books, :class_name => "Book" end
Teraz, jeśli wywołać metodę z pomocą
User
instancji mongoidembedded_books
powinien powrócić pustą tablicę.bez dodawania żadnego wbudowanego stosunku do
Book
modelu napisać własny skrypt migracji:class Book include Mongoid::Document field :title, type: String field :price, type: Integer belongs_to :user def self.migrate attributes_to_migrate = ["title","price"] # Use strings not symbols, # we keep only what we need. # We skip :user_id field because # is a field related to belongs_to association. Book.all.each do |book| attrs = book.attributes.slice(*attributes_to_migrate) user = book.user // through belong_to association user.embedded_book.create!(attrs) end end end
Wywołanie
Book.migrate
trzeba mieć wszystkie książki skopiowane wewnątrz każdego użytkownika, który był związany z belongs_to relacji.Teraz możesz usunąć relacje
has_many
ibelongs_to
, a następnie w końcu przejść do czystego rozwiązania osadzonego.class User include Mongoid::Document embeds_many :books end class Book include Mongoid::Document field :title, type: String field :price, type: Integer embedded_in :user end
Nie testowałem tego rozwiązania, ale teoretycznie powinny działać, daj mi znać.
Mam dużo krótszy odpowiedź zwięzłą:
Załóżmy, że masz te same modele:
#User Model
class User
include Mongoid::Document
has_many :books
end
#Book Model
class Book
include Mongoid::Document
field :title
belongs_to :user
end
więc zmienić go na innych witrynach:
#User Model
class User
include Mongoid::Document
embeds_many :books
end
#Book Model
class Book
include Mongoid::Document
field :title
embedded_in :user
end
i wygenerować mongoid migracji tak:
class EmbedBooks < Mongoid::Migration
@@attributes_to_migrate = [:title]
def self.up
Book.unscoped.where(:user_id.ne => nil).all.each do |book|
user = User.find book[:user_id]
if user
attrs = book.attributes.slice(*@@attributes_to_migrate)
user.books.create! attrs
end
end
end
def self.down
User.unscoped.all.each do |user|
user.books.each do |book|
attrs = @@attributes_to_migrate.reduce({}) do |sym,attr|
sym[attr] = book[attr]
sym
end
attrs[:user] = user
Book.find_or_create_by(**attrs)
end
end
end
end
Działa to, ponieważ podczas zapytania z poziomu klasy, szuka kolekcji najwyższego poziomu (która nadal istnieje, nawet jeśli zmienisz swoje relacje), a book[:user_id]
jest sztuczką, aby uzyskać dostęp do atrybutu dokumentu, a nie automatycznie generowane metody, które również istnieją ponieważ nie zrobiłeś nic, aby je usunąć.
A więc masz to, prosta migracja z relacji do wbudowanego
Dzięki, wziąłem twoją sugestię i stworzył kilka zadań rake do migracji. – Dean