将JSON列类型迁移到HSTORE列类型

时间:2014-06-17 08:54:03

标签: postgresql activerecord ruby-on-rails-4 hstore

我目前有以下数据库架构:

create_table :user_actions do |t|
  t.integer  :emitter_id
  t.string   :emitter_type
  t.integer  :target_id
  t.string   :target_type
  t.json     :payload
  t.timestamps
end

我想将payload字段从json迁移到hstore

执行以下操作:

change_column :user_actions, :payload, :hstore

导致以下错误消息:

PG::DatatypeMismatch: ERROR:  column "payload" cannot be cast automatically to type hstore
HINT:  Specify a USING expression to perform the conversion.

不确定如何使用USING提示以及在不丢失任何数据的情况下执行此迁移的最佳方法是什么?

3 个答案:

答案 0 :(得分:11)

提示:指定USING表达式以执行转换

实际上格式为:

change_column :user_actions, :payload, '[type_to_which_you_want_to_change] USING CAST(data AS [type_to_which_you_want_to_change])'

所以在你的情况下:

change_column :user_actions, :payload, 'hstore USING CAST(payload AS hstore)'

<强>参考:

https://stackoverflow.com/a/25032193/1222852

答案 1 :(得分:2)

Taimoor Changaiz的答案部分不正确,因为postgresql无法在不使用自定义函数的情况下将JSON强制转换为HSTORE。假设您的JSON没有嵌套,那么这将起作用:

  def self.up
    execute <<-SQL
        CREATE OR REPLACE FUNCTION my_json_to_hstore(json)
              RETURNS hstore
              IMMUTABLE
              STRICT
              LANGUAGE sql
            AS $func$
              SELECT hstore(array_agg(key), array_agg(value))
              FROM   json_each_text($1)
            $func$;
        SQL
    change_column :user_actions, :payload, 'hstore USING my_json_to_hstore(payload)'
    end

  def self.down
    change_column :user_actions, :payload, 'json USING CAST(payload AS json)'
    execute "DROP FUNCTION my_json_to_hstore(json)"
  end

在这里感谢pozs的自定义postgresql函数:Casting JSON to HSTORE in Postgres 9.3+?

答案 2 :(得分:1)

寻求一个简单但有效的解决方案。创建一个名为parameters的新列并执行一个简单的迁移脚本:

def up
  add_column :user_actions, :parameters, :hstore

  UserAction.find_each do |o|
    o.parameters = o.payload
    o.save!
  end

  remove_column :user_actions, :payload
end

def down
  add_column :user_actions, :payload, :json

  UserAction.find_each do |o|
    o.payload = o.parameters
    o.save!
  end

  remove_column :user_actions, :parameters
end