Рефакторинг Ecto транзакции для повышения эффективности с помощью Ecto.Multi - PullRequest
1 голос
/ 07 марта 2019

У меня есть функция, которая присваивает пользователю несколько навыков. Навыки уже существуют и имеют отношения 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 для эффективности базы данных

В дополнение к этому было бы неплохо вернуть созданные умения, а не только атом :ok, который возвращает Enum.each.

Каков наилучший способ рефакторинга кода?

1 Ответ

2 голосов
/ 07 марта 2019

Одно улучшение - это не назначать каждому навыку одно за другим

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 не был загружен, в противном случае будет только один запрос, который извлекает навыки).

Кроме того, поскольку мы делаем набор изменений, он не сбрасывает все целиком.Он только удаляет или добавляет то, что необходимо.

...