使用Ecto.Multi重构Ecto事务以提高效率

时间:2019-03-07 13:44:47

标签: elixir ecto elixir-framework

我有一个为用户分配多种技能的功能。这些技能已经存在,并且与用户具有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,就可以通过ID加入它们
  • 可以将其包装在Ecto.Multi中以提高数据库效率

除此之外,还可以返回创建的技能,而不仅仅是返回:ok返回的Enum.each原子。

重构代码的最佳方法是什么?

1 个答案:

答案 0 :(得分:2)

一项改进就是不将一项技能一一分配给

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,否则将只有一个查询,正在获取技能)。

此外,因为我们正在进行变更集,所以它不会重置整个事情。它只会删除或添加必要的内容。