I would like to go from this:
schema "products" do
field :name, :string
field :brand, :string
...more fields...
end
To this:
schema "products" do
field :name, :string
...more fields...
end
schema "brands" do
field :name, :string
...more fields...
end
schema "product_brand_joins" do
field :p_id, :integer
field :b_id, :integer
...more fields...
end
Without losing my current data which has products with a string :brand field.
I understand how to create the new brands table and product_brand_joins tables and remove ":brand" from products table, with an ecto migration. But is there a way to manipulate my current data into the new tables?
You can execute SQL statements using the execute/1
or execute/2
functions, the one that accepts two arguments is a rollbackable version of the first one, which expects a statement to execute when migrating and one to execute when rolling back.
So, you can use that to define what should happen when migrating and it becomes a matter of defining your statement. At the end of this docs page from PostgreSQL there's an example of inserting based on a subquery, which we'll use here.
In your migration .exs
file:
use Ecto.Migration
def up do
create table(:brands) do
add :name, :string,
# add other fields
end
create table("product_brand_joins") do
add :product_id, references: :products
add :brand_id, references: :brands
end
# Insert into the brands table a brand with each name found in products
execute(
"INSERT INTO brands(name)
SELECT DISTINCT(brand) FROM products"
)
# Insert into the joined table the product and brand ids
execute(
"INSERT INTO product_brand_joins(product_id, brand_id)
SELECT p.id, b.id FROM products p LEFT JOIN brands b ON p.brand = b.name"
)
# And finally delete the column from products
alter table(:products) do
remove :brand
end
end
And then for the rollback you would implement the down
function in the migration to revert the process using similar logic: creating the brand column in products
, filling it with the name of the corresponding brand depending on the product id, and removing the new tables.