首页 > 解决方案 > Refactor Ecto transaction for efficiency using Ecto.Multi

问题描述

I have a function that assigns multiple skills to a user. The skills already exist and have a many_to_many relationship with the user.

# 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

Currently this works but feels inefficient:

In addition to this, it would be nice to return the created skills rather than just the :ok atom that Enum.each returns.

What would be the best way to refactor the code?

标签: ectoelixir

解决方案


一项改进不是一一分配每项技能

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尚未加载,否则将只有一个查询,即获取技能)。

也因为我们正在做一个变更集,它不会重置整个事情。它只删除或添加必要的内容。


推荐阅读