Postgresql 使用自定义逻辑将字段迁移到不同类型的正确方法?
我有一个列Postgresql 使用自定义逻辑将字段迁移到不同类型的正确方法?,postgresql,elixir,phoenix-framework,database-migration,ecto,Postgresql,Elixir,Phoenix Framework,Database Migration,Ecto,我有一个列:from,它最初的类型是{:array,:string}。现在,我想将此列迁移为:string类型,将数组的第一个条目作为新值 在Rails中,您可以在迁移过程中使用一些自定义逻辑来实现这一点。我正试图用EXTO做同样的事情,但由于模式验证和变更集错误,我遇到了问题 defmodule Assistant.Repo.Migrations.ChangeFromFieldOnMails do use Ecto.Migration def up do dict_of_fr
:from
,它最初的类型是{:array,:string}
。现在,我想将此列迁移为:string类型,将数组的第一个条目作为新值
在Rails中,您可以在迁移过程中使用一些自定义逻辑来实现这一点。我正试图用EXTO做同样的事情,但由于模式验证和变更集错误,我遇到了问题
defmodule Assistant.Repo.Migrations.ChangeFromFieldOnMails do
use Ecto.Migration
def up do
dict_of_froms =
Assistant.Mail
|> Assistant.Repo.all()
|> Enum.reduce(%{}, fn mail, acc ->
Map.put(acc, mail.id, List.first(mail.from))
end)
alter table(:mails) do
remove :from
add :from, :string
end
Assistant.Mail
|> Assistant.Repo.all()
|> Enum.each(fn mail ->
changeset = Ecto.Changeset.change(mail, from: Map.get(dict_of_froms, mail.id))
Assistant.Repo.update!(changeset)
end)
end
def down do
dict_of_froms =
Assistant.Mail
|> Assistant.Repo.all()
|> Enum.reduce(%{}, fn mail, acc ->
Map.put(acc, mail.id, [mail.from])
end)
alter table(:mails) do
remove :from
add :from, {:array, :string}
end
Assistant.Mail
|> Assistant.Repo.all()
|> Enum.each(fn mail ->
changeset = Ecto.Changeset.change(mail, from: Map.get(dict_of_froms, mail.id))
Assistant.Repo.update!(changeset)
end)
end
end
问题是,在我的Mail
模式中,我还必须将field:from,{:array,:string}
更改为field:from,:string
,这会导致验证问题
在up
步骤中,Assistant.Repo.all()
将失败,因为由于类型不匹配,Ecto无法从旧数据库加载from
字段
在down
步骤中,Assistant.Repo.update!(变更集)
将失败,因为exto.changeset
报告了:from
上的类型不匹配错误
在Rails中,没有对模式进行严格的检查,因此您可以不受代码的影响
使用EXTO执行此类迁移的正确方法是什么?除了编写自定义SQL之外,没有其他方法了吗?您需要避免在迁移中使用结构和变更集。使用Repo.insert_all
,Repo.update_all
和模式命名
defmodule Assistant.Repo.Migrations.ChangeFromFieldOnMails do
use Ecto.Migration
import Ecto.Query
def up do
dict_of_froms =
"mails" # table name as string
|> Assistant.Repo.all()
|> Enum.reduce(%{}, fn mail, acc ->
Map.put(acc, mail.id, List.first(mail.from))
end)
alter table(:mails) do
remove :from
add :from, :string
end
dict_of_froms
|> Enum.each(fn {id, from} -> # changed this cycle little bit, so it would
"mails" # update record only if we have `from` for it
|> where(id: ^id)
|> update(set: [from: ^from])
|> Repo.update_all()
end)
end
def down do
dict_of_froms =
"mails"
|> Assistant.Repo.all()
|> Enum.reduce(%{}, fn mail, acc ->
Map.put(acc, mail.id, [mail.from])
end)
alter table(:mails) do
remove :from
add :from, {:array, :string}
end
dict_of_froms
|> Enum.each(fn {id, from} -> # changed this cycle little bit, so it would
"mails" # update record only if we have `from` for it
|> where(id: ^id)
|> update(set: [from: ^from])
|> Repo.update_all()
end)
end
end
我不确定所有的代码都是干净的和可编译的,但希望我的想法是清楚的基于apelsinka223的解决方案,我能够使它编译并工作
有几点值得注意:
- 我不得不中途调用
up
和down
函数,否则无法及时删除和添加列
- 如果查询不是基于模式的,则需要在查询中显式地使用
select
语句,以便Ecto运行它
update\u all()
至少需要两个参数。可以将[]
作为第二个参数传入
谢谢我认为我需要的正是一种绕过结构和变更集验证的方法。明天我将修改并测试解决方案。哪里定义了Where()
和update()
?您有一个useConto.Migration
语句,但我看了一下。娜达。好的,我在exto.Query
@7stud中找到了它们。你是对的,我在示例中添加了import-exto.Query
,我能够使代码编译并在一些修改后工作。我把最后的代码贴在下面。
defmodule Assistant.Repo.Migrations.ChangeFromFieldOnMails do
use Ecto.Migration
import Ecto.Query, only: [from: 2]
alias Assistant.Repo
def up do
query = from(m in "mails", select: {m.id, m.from})
dict_of_froms =
query
|> Repo.all()
|> Enum.reduce(%{}, fn {id, from}, acc ->
Map.put(acc, id, List.first(from))
end)
alter table(:mails) do
remove :from
add :from, :string
end
flush()
dict_of_froms
|> Enum.each(fn {id, fr} ->
query =
from(m in "mails",
where: m.id == ^id,
update: [set: [from: ^fr]]
)
Repo.update_all(query, [])
end)
end
def down do
query = from(m in "mails", select: {m.id, m.from})
dict_of_froms =
query
|> Repo.all()
|> Enum.reduce(%{}, fn {id, from}, acc ->
Map.put(acc, id, [from])
end)
alter table(:mails) do
remove :from
add :from, {:array, :string}
end
flush()
dict_of_froms
|> Enum.each(fn {id, fr} ->
query =
from(m in "mails",
where: m.id == ^id,
update: [set: [from: ^fr]]
)
Repo.update_all(query, [])
end)
end
end