Reputation: 17631
I currently have the following db schema:
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
And I would like to migrate the payload
field from json
to hstore
.
Doing the following:
change_column :user_actions, :payload, :hstore
Result in the following error message:
PG::DatatypeMismatch: ERROR: column "payload" cannot be cast automatically to type hstore
HINT: Specify a USING expression to perform the conversion.
Not sure how to use the USING
hint and what's the best way to do this migration without loosing any data ?
Upvotes: 5
Views: 4509
Reputation: 10684
HINT: Specify a USING expression to perform the conversion
Actually the format is:
change_column :user_actions, :payload, '[type_to_which_you_want_to_change] USING CAST(data AS [type_to_which_you_want_to_change])'
So in your case:
change_column :user_actions, :payload, 'hstore USING CAST(payload AS hstore)'
Reference:
https://stackoverflow.com/a/25032193/1222852
Upvotes: 12
Reputation: 838
Taimoor Changaiz's answer is partially incorrect since postgresql cannot cast JSON to HSTORE without using a custom function. Assuming your JSON is not nested, here's what will work:
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
Credit to pozs for his custom postgresql function here: Casting JSON to HSTORE in Postgres 9.3+?
Upvotes: 3
Reputation: 17631
Went for a simple but working solution. Create a new column named parameters
and do a simple migration script:
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
Upvotes: 2