ecto - 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:
- We may be removing skills that will just be added back again
- Might not need to run
Repo.all
to fetch skills, can just join them by ID if they exist - Could be wrapped in
Ecto.Multi
for database efficiency
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?
解决方案
一项改进不是一一分配每项技能
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
尚未加载,否则将只有一个查询,即获取技能)。
也因为我们正在做一个变更集,它不会重置整个事情。它只删除或添加必要的内容。
推荐阅读
- android - 如何在 kotlin 的片段类中更改 ActionBar 标题?
- node.js - 真正删除所有全局 npm 模块
- android - 我想做一个 asynctask 类,asynctask 可以操作字符串、整数、URL
- c++ - 从 QDataStream 读取数据
- python - 在python中生成复合对象的映射字典
- rest - 我应该在自己的项目之间使用 Web API 吗?
- javascript - JS:如何计算字母
- node.js - 使用 ExpressJS 添加多个 flash
- javascript - 反应路由器没有响应 - 没有错误
- c - 程序的编译是否在执行前将一些数据存储在缓存中?(C,Linux)