使用 Ecto.Multi 重构 Ecto 事务以提高效率
Refactor Ecto transaction for efficiency using Ecto.Multi
我有一个为用户分配多项技能的功能。技能已经存在并且与用户有 many_to_many
关系。
# skills will look like [%{id: "1"}, %{id: "4"}, %{id: "5"}] etc.
def reset(user, skills) do
user
|> Repo.preload(:skills)
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_assoc(:skills, [])
|> Repo.update()
ids = Enum.map(skills, &Map.get(&1, :id))
query = Repo.all(from(p in Skill, where: p.id in ^ids))
skills = Enum.each(query, &Skill.create(user, &1))
end
目前这可行,但感觉效率低下:
- 我们可能会删除将再次添加回来的技能
- 可能不需要 运行
Repo.all
来获取技能,如果它们存在,可以通过 ID 加入它们
- 可以包装在
Ecto.Multi
中以提高数据库效率
除此之外,return 创造的技能而不仅仅是 Enum.each
return 的 :ok
原子会更好。
重构代码的最佳方法是什么?
一个改进是不一一分配每个技能
def reset(user, skills) do
ids = Enum.map(skills, &Map.get(&1, :id))
skills = Repo.all(from(p in Question.Skill, where: p.id in ^ids))
user
|> Repo.preload(:skills)
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_assoc(:skills, skills)
|> Repo.update()
end
如果不需要更改(即对用户技能的任何删除或添加),这将对数据库进行两次查询(假设 :skills
尚未加载,否则只有一个query, 正在获取技能)。
另外,因为我们正在制作变更集,所以它不会重置整个内容。它只删除或添加必要的内容。
我有一个为用户分配多项技能的功能。技能已经存在并且与用户有 many_to_many
关系。
# skills will look like [%{id: "1"}, %{id: "4"}, %{id: "5"}] etc.
def reset(user, skills) do
user
|> Repo.preload(:skills)
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_assoc(:skills, [])
|> Repo.update()
ids = Enum.map(skills, &Map.get(&1, :id))
query = Repo.all(from(p in Skill, where: p.id in ^ids))
skills = Enum.each(query, &Skill.create(user, &1))
end
目前这可行,但感觉效率低下:
- 我们可能会删除将再次添加回来的技能
- 可能不需要 运行
Repo.all
来获取技能,如果它们存在,可以通过 ID 加入它们 - 可以包装在
Ecto.Multi
中以提高数据库效率
除此之外,return 创造的技能而不仅仅是 Enum.each
return 的 :ok
原子会更好。
重构代码的最佳方法是什么?
一个改进是不一一分配每个技能
def reset(user, skills) do
ids = Enum.map(skills, &Map.get(&1, :id))
skills = Repo.all(from(p in Question.Skill, where: p.id in ^ids))
user
|> Repo.preload(:skills)
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_assoc(:skills, skills)
|> Repo.update()
end
如果不需要更改(即对用户技能的任何删除或添加),这将对数据库进行两次查询(假设 :skills
尚未加载,否则只有一个query, 正在获取技能)。
另外,因为我们正在制作变更集,所以它不会重置整个内容。它只删除或添加必要的内容。