9

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

4

4 回答 4

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

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

  • 这只是建议。在尝试生产之前在您的开发中尝试此操作。因为,我认为我的建议可能有问题。
于 2013-04-11T07:53:47.677 回答
1

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

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

  • 文档大小限制为 16MB - 这意味着嵌入文档的最大数量,即使您只是嵌入它们的 object-id
  • 如果您想从顶层搜索所有嵌入的文档,那么不要嵌入,而是使用引用的文档!
于 2013-04-19T03:27:30.720 回答
1

尝试以下步骤:

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

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

    现在,如果您embedded_booksUser实例调用方法 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
    

我没有测试过这个解决方案,但理论上应该可以,让我知道。

于 2013-04-29T16:40:57.540 回答
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]是访问文档属性而不是自动生成的方法的技巧,这些方法也存在没有做任何事情来删除它们。

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

于 2015-01-14T15:07:49.913 回答