如何运行迁移来更改 Mongoid/MongoDB 中的字段类型而不丢失任何数据?
就我而言,我正在尝试从 BigDecimal (存储为字符串)转换为 Integer 以存储一些钱。我需要将字符串十进制表示形式转换为整数的美分。我不想丢失现有数据。
我假设步骤可能类似于:
- 使用新名称创建新的整数字段,例如
amount2
- 部署到生产环境并运行迁移(或 rake 任务),将每个
amount
转换为amount2
的正确值 - (整个过程中现有代码仍在使用
amount
,从用户的角度来看没有停机) - 关闭网站进行维护,再次运行迁移以捕获在过去几分钟内可能发生更改的任何
金额
字段 - 删除
amount
并将amount2
重命名为amount
- 部署新代码,要求
amount
为整数 - 恢复网站
看起来 Mongoid 提供了一个重命名
方法:http://mongoid.org/docs/persistence/atomic.html#rename
但是我有点困惑这是如何使用的。如果您有一个名为 amount2
的字段(并且您已经删除了 amount
),您只需运行 Transaction.rename :amount2, :amount
?然后我想这会立即破坏底层表示,因此您必须在此之后重新启动应用程序服务器?如果在 amount
仍然存在的情况下运行它会发生什么?它是否被覆盖、失败或尝试自行转换?
谢谢!
最佳答案
好吧,我成功了。我认为有一种更快的方法使用 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
请注意,由于 mongodb 游标,.all.each 工作正常(您不需要像 mysql 的常规 activerecord 那样使用 .find_each 或 .find_in_batches)。只要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
这是原子性的,不需要保存模型。
- 更新模型以反射(reflect)新的列类型
field :amount, type: Integer
- 部署并恢复网站
如上所述,我认为有更好的方法,所以如果有人有一些提示,请分享。谢谢!
关于ruby-on-rails - 更改 Mongoid 中的字段类型而不丢失数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9389548/