Moving data from database column to jsonb field

There are cases when data needs to be moved from regular database column to jsonb based field. Likewise with every database migration existing data needs to be retained as part of the process.

Let’s see how we can do it using regular SQL commands and then through Rails migration. Imagine we need to move token from users table into token inside credentials jsonb based field in the same table.

Firstly, we need to add a new column:

ALTER TABLE users
ADD COLUMN credentials jsonb;

Then we need to migrate the data:

UPDATE users
SET credentials = COALESCE(credentials, '{}')::jsonb || jsonb_build_object('token', users.token);

And finally, add index to that nested field:

CREATE UNIQUE INDEX users_credentials_token ON users USING btree ((credentials->>'token'::text));

Rails version

With Rails, it can be done within single migration:

def change
  add_column :users, :credentials, :jsonb
  add_index :users, "(credentials->>'token')", unique: true

  reversible do |dir|
    dir.up do
      execute <<~SQL
        UPDATE users
        SET credentials = COALESCE(credentials, '{}')::jsonb || jsonb_build_object('token', users.token)
      SQL
    end
  end
end