Изменить тип поля в Mongoid без потери данных - PullRequest
1 голос
/ 22 февраля 2012

Как запустить миграцию, чтобы изменить тип поля в Mongoid / MongoDB без потери каких-либо данных?

В моем случае я пытаюсь преобразовать BigDecimal (хранится в виде строки) в Integer для хранения денег. Мне нужно преобразовать строковое десятичное представление в центы для целого числа. Я не хочу потерять существующие данные.

Я предполагаю, что шаги могут быть что-то вроде:

  1. создать новое поле типа Integer с новым именем, скажем 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 еще существует? Он перезаписывается, завершается неудачно или пытается конвертировать самостоятельно?

Спасибо!

1 Ответ

3 голосов
/ 23 февраля 2012

Хорошо, я справился.Я думаю, что есть более быстрый способ использования консоли Монго с чем-то вроде этого: MongoDB: Как изменить тип поля?

Но я не смог заставить работать преобразование, поэтомувыбрал этот более медленный метод в консоли rails с большим временем простоя.Если у кого-нибудь есть более быстрое решение, пожалуйста, опубликуйте его.

  • создайте новое поле Integer с новым именем, скажем amount2
  • , преобразуйте каждое 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 как обычная activerecord с mysql) из-за курсоров 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
  • развернуть и восстановить сайт

Как уже упоминалось, я думаю, что есть лучший способ, поэтому, если у кого-то есть какие-то советы, пожалуйста, поделитесь. Спасибо!

...