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"]