如何从belongs_to迁移到Mongoid中的embedded_in?

时间:2013-04-09 22:36:29

标签: ruby-on-rails mongoid

如果首先使用belongs_to和has_many关联构建模型,然后意识到他们需要转移到embedded_in和embeds_many关联,那么如何在不使数千条记录失效的情况下执行此操作?需要以某种方式迁移它们。

4 个答案:

答案 0 :(得分:4)

我不太确定我的解决方案是否正确。这是你可能试图完成的事情。

假设您有模型 - 像这样

#User Model
class User
  include Mongoid::Document
  has_many :books
end

#Book Model
class Book
  include Mongoid::Document
  field :title
  belongs_to :user
end

在第一步,我将创建另一个类似于上面的Book模型的模型,但它是嵌入的而不是引用的。

#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

然后为上面的例子创建一个类似于此的Mongoid Migration

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

运行迁移后。从这里您可以看到嵌入式参考书籍,但嵌入式模型的名称是EmbedBook,模型Book仍在那里

所以下一步就是将模型书改为嵌入式。

class Book
  include Mongoid::Document
  embedded_in :user
  field :title
end

class User
  include Mongoid::Document
  embeds_many :books
  embeds_many :embed_books
end

所以下一步是将embedbook类型迁移到图书类型

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

现在如果您看到Book已从引用更改为嵌入式。 您可以删除EmbedBook模型以完成更改。

  • 这只是建议。在尝试生产之前尝试使用此开发。因为,我认为我的建议可能有问题。

答案 1 :(得分:1)

10gen有几篇关于数据建模的文章可能很有用:

请记住,在嵌入时,MongoDB有两个限制:

  • 文档大小限制为16MB - 这意味着嵌入文档的最大数量,即使您只是嵌入了他们的object-id
  • 如果您想要搜索顶层的所有嵌入式文档,请不要嵌入,而是使用引用的文档!

答案 2 :(得分:1)

尝试以下步骤:

  1. User模型中保留has_many :books关系,然后添加。{     具有不同名称的嵌入式关系不会覆盖books     方法

        class User
           include Mongoid::Document
    
           has_many :books
           embeds_many :embedded_books, :class_name => "Book"
        end
    

    现在,如果您从embedded_books实例调用User方法    mongoid应该返回一个空数组。

  2. 在不向Book模型添加任何嵌入关系的情况下,编写自己的关系 迁移脚本:

    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
    

    调用Book.migrate您应该在每个用户中复制所有图书 与belongs_to关系相关联。

  3. 现在您可以删除has_manybelongs_to关系了 最后切换到清洁嵌入式解决方案。

    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
    
  4. 我没有测试过这个解决方案,但理论上应该可行,请告诉我。

答案 3 :(得分:0)

我的答案简短得多:

我们假设你有相同的模型:

#User Model
class User
  include Mongoid::Document
  has_many :books
end

#Book Model
class Book
  include Mongoid::Document
  field :title
  belongs_to :user
end

所以改成嵌入:

#User Model
class User
  include Mongoid::Document
  embeds_many :books
end

#Book Model
class Book
  include Mongoid::Document
  field :title
  embedded_in :user
end

并生成像这样的mongoid迁移:

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

这是有效的,因为当您从类级别进行查询时,它正在查找顶级集合(即使您更改了关系,它仍然存在),book[:user_id]是访问文档属性的技巧,而不是自动生成的方法也存在,因为你没有做任何删除它们。

所以你有它,从关系到嵌入式的简单迁移