首页 文章

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

提问于
浏览
1

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

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

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

  • 使用新名称创建新的整数字段,例如 amount2

  • 部署到 生产环境 并运行迁移(或rake任务),将每个 amount 转换为 amount2 的正确值

  • (这整个现有代码仍在使用 amount ,从用户的角度来看没有停机时间)

  • 关闭网站进行维护,再次运行迁移以捕获最近几分钟内可能发生变化的任何 amount 字段

  • 删除 amount 并将 amount2 重命名为 amount

  • 部署新代码,期望 amount 为整数

  • 让网站备份

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

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

谢谢!

1 回答

  • 3

    好的,我完成了 . 我认为有一种更快的方式使用mongo控制台:MongoDB: How to change the type of a field?

    但我无法使转换工作,因此在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工作正常(你不需要使用.find_each或.find_in_batches,如使用mysql的常规activerecord)因为mongodb游标 . 只要identity_map关闭,它就不会填满内存 .

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

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

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


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

    这是原子的,不需要在模型上保存 .

    • 更新您的模型以反映新列类型 field :amount, type: Integer

    • 部署并恢复网站

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

相关问题