【问题标题】:Change type of field in Mongoid without losing data在不丢失数据的情况下更改 Mongoid 中的字段类型
【发布时间】:2012-02-22 05:29:12
【问题描述】:

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

就我而言,我正在尝试将 BigDecimal(存储为字符串)转换为 Integer 以存储一些钱。我需要将字符串十进制表示形式转换为整数的美分。我不想丢失现有数据。

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

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

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

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

谢谢!

【问题讨论】:

  • 听起来合乎逻辑。但我不确定您是否需要重新启动。一方面,您需要更新模型中的字段名称,对吧?至于覆盖,我不确定,但是这个链接whyjava.wordpress.com/2012/02/07/… 的 upsert 部分似乎确实暗示它会尝试写入,并且如果存在,可以选择覆盖该字段。所以它不像常规的基于 SQL 的重命名,而是单独移动每个文档值。有意义吗?
  • 嗯...不确定,仍然希望从头到尾获得一个工作示例。如果我的成功,我会发布一个。

标签: ruby-on-rails mongodb mongoid


【解决方案1】:

好的,我成功了。我认为使用 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

这是原子的,不需要保存模型。

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

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

【讨论】:

  • 只是一个问题。对于大型数据集,您的光标是否有可能超时?
  • 是的,我认为 mongo 的默认光标超时时间是 10 分钟,这肯定会发生。如果您可以幂等地编写它(检查amount2是否为nil)并多次运行它,这可能会起作用。不过看起来确实比应该做的要难。
  • 更新,我可能应该使用这个:github.com/nviennot/mongoid_lazy_migration 看起来比我做的更容易
  • 哦...我主要遵循您之前的建议,解决了超时问题。感谢分享宝石
猜你喜欢
  • 1970-01-01
  • 2012-03-20
  • 2019-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-29
  • 2017-07-15
  • 1970-01-01
相关资源
最近更新 更多