【问题标题】:How to migrate from belongs_to, to embedded_in in Mongoid?如何在 Mongoid 中从 belongs_to 迁移到 embedded_in?
【发布时间】:2013-04-09 22:36:28
【问题描述】:

如果一个人首先使用belong_to 和has_many 关联构建他们的模型,然后意识到他们需要迁移到embedded_in 和embeds_many 关联,那么如何在不使数千条记录失效的情况下做到这一点?需要以某种方式迁移它们。

【问题讨论】:

    标签: ruby-on-rails mongoid


    【解决方案1】:

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

    假设你有模型 - 像这样

    #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 迁移

    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 类型迁移到 book 类型

    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 模型以完成更改。

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

    【讨论】:

    • 谢谢,我采纳了您的建议,并为迁移创建了一些 rake 任务。
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

      试试这些步骤:

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

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

        现在,如果您从 User 实例调用 embedded_books 方法 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】:

        我有一个更简洁的答案:

        假设您有相同的模型:

        #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] 是访问文档属性而不是自动生成的方法的技巧这也存在,因为你没有做任何事情来删除它们。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-05-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多