【问题标题】:Ecto Repo.exists?() do something only when falseEcto Repo.exists?() 仅在 false 时执行某些操作
【发布时间】:2022-01-15 00:38:42
【问题描述】:

我对 Elixir/Ecto 很陌生,与其他语言不同,我发现控制流的工作方式有点令人困惑 我只想在Repo.exists?() 返回 false 时做一些事情。根据文档,这将返回一个布尔值。我试过了

**/Orginal OP code**
alias App.MyModule

params.id
    |> MyModule.check_if_person_exist()
    |> Repo.exists?()
    |> if false do
      IO.puts("\n\nFALSEEE\n\n")
      MySchema
      |> MySchema.changeset(%{data: data})
      |> Repo.insert_or_update()


...
...
App.MyModule file

def check_if_person_exist(id) do
  from(s in Schema
   where id = ^id,
   select %{
     person: s.person
   }
 )
end
case do
  false ->
    //do stuff

但我收到一条错误消息,提示 no case clause matching true

我只想在错误的情况下做某事,而不是检查是否为真。这不能用 Ecto/Elixir 做吗?

//**MyQuery.Module**
def myqueryfunc(id) do
 query = from(s in Schema
   where id = ^id,
   select %{
     person: s.person
   }
 )
end
//**Some other file**

alias MyQuery.Module

query = Module.myqueryfunc(id)

case Repo.one(query) do
 false ->
  MySchema
  |> MySchema.changeset(%{data: data})
  |> Repo.insert_or_update()

我必须考虑true吗?

我只关心它是否是假的。

如果我必须处理 true,当它是 true 时,我能做什么什么都不做?

这行得通吗

query
|>Repo.exists?()
|> false
|> if(do: //insert or update)

【问题讨论】:

  • 如果您对相反的条件感兴趣,而不是if/2,您可能对unless/2 感兴趣。您可以像query |> Repo.exists?() |> unless do .. end 一样直接通过管道传输它。和|> Kernel.!() |> if基本一样

标签: elixir ecto


【解决方案1】:

如果没有匹配的子句,代码会引发CaseClauseError

iex|?|1 ▸ case true do
...|?|1 ▸   false -> :ok
...|?|1 ▸ end

** (CaseClauseError) no case clause matching: true

是的,您需要使用case/2 涵盖所有可能的结果。 OTOH,我们有if/2,它更宽容,如果没有else: 子句,它会静默返回nil

iex|?|2 ▸ false |> if(do: :ok)
nil

因此您可以使用if/2 或将case/2 表达式中的所有子句处理为

query
|> Repo.exists?()
|> case do
  false -> ...
  true -> :ok # or `nil` or whatever
end

【讨论】:

  • 抱歉,我无法按照您的 if 示例进行操作。所以这将适用于 Repo.exists 吗?如果 Repo.exists?() |> false |> if(do: //insert or update) 也在 op 中更新
  • 你能检查一下我的操作,看看底部更新的代码是否有效吗?
  • 不,不是。在我的例子中falseif/2 的输入,在你的例子中是Repo.exists?。只需从您的链中删除|> falsefalse |> if(do: :ok)if false, do: ok 相同,根据管道定义。
  • 我想我试过了,但我收到了一条错误消息,指出 if/3 不存在
【解决方案2】:

函数式语言有纯函数的概念。纯函数必须始终返回一个值,elixir 中没有过程这样的东西。

案例陈述必须详尽无遗,在您的案例中,您的 Repo.exists?/1 返回 true,而在您的案例陈述中,您不处理它。

您可以通过多种方式解决此问题:

  1. 重构为if
if !Repo.exists?(query) do
  MySchema
  |> MySchema.changeset(%{data: data})
  |> Repo.insert_or_update()
end

然而,这个解决方案并不是最好的,因为函数将返回 nil 或您更新的条目,相反我会执行以下操作:

case Repo.one(query) do
  nil -> 
    MySchema
    |> MySchema.changeset(%{data: data})
    |> Repo.insert_or_update()
  result -> result
end

通过这种方式,您可以保证在这两种情况下都会返回一个条目。这种做法甚至在 ecto internals 中也得到了广泛应用。

【讨论】:

  • 我尝试了第二个示例,但它没有进入 nil
  • @hnhl 请更具体一些。此答案中的示例非常有效。
  • 我更新了操作,你能再检查一下吗?
【解决方案3】:

首先,你不应该使用这样的管道操作符

code
|> Repo.exists?()
|> if do

上面是一条不明确的管道语句,你可以看到elixir pipe operator

您可以使用下面的case 语句来进行控制流

params.id
|> MyModule.check_if_person_exist()
|> Repo.exists?()
|> case do
    true -> nil
    false -> 
         IO.puts("\n\nFALSEEE\n\n")
         MySchema
         |> MySchema.changeset(%{data: data})
         |> Repo.insert_or_update()
end

【讨论】:

  • 首先,您不应该将您的[不一定正确]意见作为必须陈述。 |> if do 是完全合法的。除此之外,这个答案对已经存在的答案没有任何增加。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-29
  • 2019-06-16
相关资源
最近更新 更多