Elixir 将多个可链接的update\u all调用减少为单个update语句
假设我有一个用于用户的模块,它有两个函数,接受变更集并向其添加变更,如->Elixir 将多个可链接的update\u all调用减少为单个update语句,elixir,ecto,Elixir,Ecto,假设我有一个用于用户的模块,它有两个函数,接受变更集并向其添加变更,如-> defmodule MyApp.User do def confirm(changeset_or_struct) do changeset_or_struct |> Ecto.Changeset.change(confirmed: true, confirmed_at: Timex.now()) end def update_session(changeset_or_struct,
defmodule MyApp.User do
def confirm(changeset_or_struct) do
changeset_or_struct
|> Ecto.Changeset.change(confirmed: true, confirmed_at: Timex.now())
end
def update_session(changeset_or_struct, ip_address) do
changeset_or_struct
|> Ecto.Changeset.change(session_token: "token", ip_address: ip_address)
end
end
因此,如果我需要将这两个更改应用于特定用户并保存它们,我可以像这样轻松地链接这些函数调用
some_user
|> User.confirm()
|> User.update_session("ip_address")
|> Repo.update!()
所以一切都很好
现在,假设出于某种原因,我需要以原子方式同时确认和更新许多用户的会话。显然,获取所有这些用户,锁定他们,然后在事务中逐个更新并不是一个很好的选择,因此我们需要利用update_all函数只发送一条update语句
所以我们需要的是->
User
|> Repo.update_all([set: confirmed: true, confirmed_at: Timex.now(), session_token: "token", ip_address: "ip_address"])
但是在用户模块之外调用此代码似乎不是一个好主意,因为我只希望用户模块知道如何处理确认和会话更新。因此,下一个明显的决定是在用户模块中创建一个函数并将代码放在那里,但问题是它不灵活,因为这两个动作确认和会话更新现在耦合在一起。但是我们可以把它分成两个函数
def confirm_many(query) do
query
|> Repo.update_all([set: confirmed: true, confirmed_at: Timex.now()])
end
def update_session_many(query, ip_address) do
query
|> Repo.update_all([set: session_token: "token", ip_address: ip_address])
end
即使现在它们没有耦合,但它们都不是可链接的,所以即使我可以分别使用这两个,很可能我最终会得到一段难看的代码和一堆更新语句,而这很容易简化为一个。现在的问题是:
我该怎么做?有没有一种方法可以将一组独立的更新链接起来,最终可以简化为一个update\u all调用
因此,底线是我希望能够做这样的事情:
User
|> where([u], u.id in bunch_of_ids)
|> User.confirm_many()
|> User.update_session_many("ip_address")
|> Repo.update_all()
您可以使用exto.Query.update以增量方式构建更新查询,并仅在最后将其传递给Repo.update\u all:
defmodule MyApp.User do
...
def confirm_many(query) do
query
|> update([set: [confirmed: true, confirmed_at: ^Ecto.DateTime.utc()]])
end
def update_session_many(query, ip_address) do
query
|> update([set: [session_token: "token", ip_address: ^ip_address]])
end
end
使用此选项,将生成以下代码:
import Ecto.Query
alias MyApp.{Repo, User}
User
|> where([u], u.id in [1, 2, 3])
|> User.confirm_many()
|> User.update_session_many("ip_address")
|> Repo.update_all([])
执行查询:
UPDATE "users" AS u0
SET "confirmed" = TRUE,
"confirmed_at" = $1,
"session_token" = 'token',
"ip_address" = $2
WHERE (u0."id" IN (1,2,3))
[{{2017, 5, 21}, {11, 13, 43, 0}}, "ip_address"]