如何将零分配给 Ecto 查询的零结果?

How to assign zero to a nil result of Ecto query?

我正在尝试查询交易列表以获得总金额,然后将其添加到另一个金额。但是,如果用户没有交易,则会发生错误。所以我放了一个条件,如果结果为零,则分配 0,但它不会结转

query = from t in Transactions, where: (t.user_id == ^user), select: sum(t.amount)
balance = Repo.one(query) # get value
if is_nil(balance) do
  balancevalue = 0
else 
  balancevalue = Decimal.to_float(balance) # convert value to decimal        
end
total = balancevalue + ^anotherbalance  # add with other value
IO.puts "total: #{total}"

如何重写条件,以便在结果为 nil 时查询 return 为零?

如果你想使用 elixir is_nil, guard,因为你不确定 balance 是有效整数而不是 nil,你会:

balancevalue = if is_nil(balance), do: 0, else: Decimal.to_float(balance)

您想使用 coalesce/2https://hexdocs.pm/ecto/Ecto.Query.API.html#coalesce/2

balance = 
  from(t in Transactions, 
    where: t.user_id == ^user, 
    select: coalesce(sum(t.amount), 0)
  )
  |> Repo.one()

请记住,Elixir 中的所有内容都是一项任务。这意味着你必须摆脱许多在其他语言中很常见的模式,因为 Elixir 变量不共享超出其块的任何范围。例如,在许多 OO 语言中,您可能会执行以下操作来对列表项进行条件计数:

# pseudo code... This pattern will not work in Elixir!
cnt = 0
for i = 0; i < length(list); i++ {
  if list[i] == 'something'
    cnt++
  }
}

在 Elixir 中,你需要做一个“赋值”,在这种情况下,这意味着你必须退回到久经考验的 Enum 模块,例如使用 Enum.reduce/3:

cnt = Enum.reduce(list, 0, fn 
  "something", acc -> acc + 1 # function clause 1 (match)
  _, acc -> acc               # function clause 2 (no match)
end)

上面我们使用了 2 个函数子句来匹配特定的条件值(“某物”)而不是使用 if-statement -- if-statement最终在 Elixir 中很少见,因为事实证明你并不经常需要它们。

在您的示例中,您可以通过简单的 case 语句来利用赋值:

def user_balance(user) do
  query = from t in Transactions, where: (t.user_id == ^user), select:   sum(t.amount)

  case Repo.one(query) do
    nil -> 0
    balance -> Decimal.to_float(balance)
  end
end

如果您遵循执行流程,您会看到 Repo.one(query) 的结果立即在 case 语句中求值。同样,我们使用 pattern-matching 而不是 if 语句(请记住,最具体的匹配项必须首先出现)。所以如果结果是 nil0 得到 returned;如果是其他任何内容,则将其移交给 Decimal.to_float/1。因为 Elixir 函数依赖于隐式 returns,这个 case 语句的输出是 user_balance/1 函数的 return 值。

将事情分解成单独的函数不仅使您的代码更具可读性,还可以帮助您实现分配,使您的代码真正起作用。

nil 在 Elixir 中是一个假值,所以你可以简单地使用或运算符 (||).

balance = Repo.one(query) || 0
total = Decimal.to_float(balance) + another_balance

或者如果你想通过管道传输它

query
|> Repo.one()
|> Kernel.||(0)
|> Decimal.to_float()
|> Kernel.+(another_balance)