Nếu một trong những người đầu tiên xây dựng mô hình của họ với một liên kết thuộc tính và has_many và sau đó nhận ra họ cần phải di chuyển đến một embedded_in và embeds_many hiệp hội, làm thế nào sẽ làm điều này mà không làm mất hiệu lực hàng ngàn hồ sơ? Cần di chuyển chúng bằng cách nào đó.Làm thế nào để di chuyển từ thuộc_to, sang embedded_in trong Mongoid?
Trả lời
Tôi không chắc chắn giải pháp của mình là đúng hay không. Đây là một cái gì đó bạn có thể cố gắng để thực hiện nó.
Giả sử Bạn có các mô hình - như thế này
#User Model
class User
include Mongoid::Document
has_many :books
end
#Book Model
class Book
include Mongoid::Document
field :title
belongs_to :user
end
Tại bước đầu tiên tôi sẽ tạo ra một mô hình tương tự như mô hình Book trên nhưng nó nhúng thay vì tham chiếu.
#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
Sau đó tạo một Mongoid Migration với một cái gì đó như thế này cho ví dụ
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
trên Sau khi chạy di cư. Từ đây, bạn có thể thấy sách tham chiếu được nhúng nhưng tên của mô hình được nhúng là EmbedBook và Model Book vẫn còn ở đó
Do đó, bước tiếp theo là làm sách mẫu thành dạng nhúng.
class Book
include Mongoid::Document
embedded_in :user
field :title
end
class User
include Mongoid::Document
embeds_many :books
embeds_many :embed_books
end
Vì vậy, tiếp theo sẽ là di chuyển loại embedbook để đặt loại
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
Bây giờ Nếu bạn thấy sách được thay đổi từ tham chiếu đến nhúng. Bạn có thể xóa mô hình EmbedBook để thực hiện thay đổi hoàn tất.
- Đây chỉ là gợi ý. Hãy thử điều này trên sự phát triển của bạn trước khi thử sản xuất. Vì, tôi nghĩ có thể có điều gì đó sai trong đề xuất của tôi.
10gen có một vài bài viết về mô hình dữ liệu mà có thể là hữu ích:
Hãy nhớ rằng có hai hạn chế trong MongoDB khi nói đến nhúng:
- tài liệu kích thước giới hạn là 16MB - điều này ngụ ý một số tối đa của tài liệu nhúng, ngay cả khi bạn chỉ cần nhúng đối tượng id của họ
- nếu bạn muốn tìm kiếm trên tất cả các tài liệu được nhúng từ cấp cao nhất, sau đó không nhúng, nhưng sử dụng tài liệu được tham chiếu thay thế!
Hãy thử những bước sau:
Trong
User
mô hình rời mối quan hệhas_many :books
, và thêm mối quan hệ nhúng với một cái tên khác để không ghi đè lên các phương phápbooks
.class User include Mongoid::Document has_many :books embeds_many :embedded_books, :class_name => "Book" end
Bây giờ nếu bạn gọi phương thức
embedded_books
từ mộtUser
dụ mongoid nên trả lại một mảng trống.Nếu không có thêm bất kỳ mối quan hệ nhúng để
Book
mô hình, viết kịch bản riêng chuyển đổi của bạn: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
Calling
Book.migrate
bạn nên có tất cả các Sách sao chép bên trong mỗi người dùng là ai gắn liền với mối quan hệ belongs_to.Bây giờ, bạn có thể xóa mối quan hệ
has_many
vàbelongs_to
và cuối cùng, chuyển sang làm sạch giải pháp nhúng.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
tôi đã không kiểm tra giải pháp này, nhưng về mặt lý thuyết nên làm việc, cho tôi biết.
Tôi có một câu trả lời ngắn gọn ngắn hơn nhiều:
Giả sử rằng bạn có mô hình tương tự:
#User Model
class User
include Mongoid::Document
has_many :books
end
#Book Model
class Book
include Mongoid::Document
field :title
belongs_to :user
end
Vì vậy, thay đổi nó để nhúng:
#User Model
class User
include Mongoid::Document
embeds_many :books
end
#Book Model
class Book
include Mongoid::Document
field :title
embedded_in :user
end
Và tạo ra một sự chuyển đổi mongoid như thế này:
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
Điều này làm việc vì khi bạn truy vấn từ cấp lớp, nó đang tìm kiếm bộ sưu tập cấp cao nhất (vẫn tồn tại ngay cả khi bạn thay đổi quan hệ của bạn), và book[:user_id]
là một mẹo để truy cập thuộc tính tài liệu thay vì các phương thức tự tạo cũng tồn tại vì bạn chưa làm gì để xóa chúng.
Vì vậy, ở đó bạn có nó, một di chuyển đơn giản từ quan hệ đến nhúng
Cảm ơn, tôi đã đề xuất của bạn và tạo ra một số nhiệm vụ cào để di chuyển. – Dean