更改Mongoid中的字段类型而不会丢失数据

如何在不丢失任何数据的情况下运行迁移以更改Mongoid / MongoDB中字段的类型?

在我的情况下,我试图从BigDecimal(存储为字符串)转换为整数来存储一些钱。 我需要将字符串十进制表示转换为整数的美分。 我不想丢失现有数据。

我假设步骤可能是这样的:

  1. 使用新名称创建新的整数字段,例如amount2
  2. 部署到生产并运行迁移(或rake任务),将每个amount转换为amount2的正确值
  3. (这整个时间现有代码仍在使用amount ,从用户的角度来看没有停机时间)
  4. 将网站停下来进行维护,再次运行迁移以捕获在过去几分钟内可能已更改的任何amount字段
  5. 删除amount并将amount重命名为amount
  6. 部署期望amount为整数的新代码
  7. 带回网站

看起来Mongoid提供了一种rename方法: http ://mongoid.org/docs/persistence/atomic.html#rename

但我有点困惑如何使用它。 如果您有一个名为amount2的字段(并且您已经删除了amount ),您是否只运行Transaction.rename :amount2, :amount ? 然后我想这会立即打破基础表示,所以你必须在那之后重启你的应用服务器? 如果您在amount仍然存在的情况下运行会发生什么? 是否会被覆盖,失败或尝试自行转换?

谢谢!

好的,我完成了。 我认为有一种更快的方式使用mongo控制台: MongoDB:如何更改字段的类型?

但我无法使转换工作,因此在rails控制台中选择了这种较慢的方法,停机时间更长。 如果有人有更快的解决方案请发布。

  • 使用新名称创建新的整数字段,例如amount2
  • 在控制台或rake任务中将每个amount转换为amount2的正确值

 Mongoid.identity_map_enabled = false Transaction.all.each_with_index do |t,i| puts i if i%1000==0 t.amount2 = t.amount.to_money break if !t.save end 

请注意.all.each工作正常(因为mongodb游标,你不需要像使用mysql的常规activerecord那样使用.find_each或.find_in_batches)。 只要identity_map关闭,它就不会填满内存。

  • 将网站停下来进行维护,再次运行迁移以捕获在过去几分钟内可能已更改的任何数量的字段(例如Transaction.where(:updated_at.gt => 1.hour.ago).each_with_index...

  • 注释掉field :amount, type: BigDecimal模型中的field :amount, type: BigDecimal ,你不希望mongoid再次知道这个字段,并推送此代码

  • 现在运行另一个脚本来重命名您的列(它会覆盖进程中任何旧的BigDecimal字符串值)。 您可能需要评论模型上对旧字段的任何validation。

 Mongoid.identity_map_enabled = false Transaction.all.each_with_index do |t,i| puts i if i%1000==0 t.rename :amount2, :amount end 

这是primefaces的,不需要在模型上保存。

  • 更新模型以反映新的列类型field :amount, type: Integer
  • 部署并恢复网站

如上所述,我认为有更好的方法,所以如果有人有一些提示请分享。 谢谢!