How to pattern match Ecto query error
Asked Answered
E

3

7

Like other functions in elixir (as well as Ecto's own transactions), I want to pattern match to handle potential errors with Ecto queries. Like this:

case Repo.get!(User, id) do
  {:ok, user} ->
    #do something
  {:error, message} ->
    #pass the error
end

Obviously this does not work, but how can I pattern match Ecto errors like Ecto.NotSingleResult and other potential query problems like preload errors?

Eternalize answered 11/4, 2015 at 17:21 Comment(0)
L
2

Use Repo.get which will return a value or nil. You can then pattern match on the expected struct or use if-clauses. Repo.get! raises on purpose (for the cases you expect a struct to be there and not being there is an error).

Lucho answered 11/4, 2015 at 20:11 Comment(3)
Yes this is kindof how I'm doing it now, I was just wondering why Ecto didn't return a tuple like lots of other things. The tuple response is a really nice way to deal with errors and flow.Eternalize
Elixir indeed has both. We typically use the tuple when there are different reasons for error (like in the functions in the File module). Here, however, the only reason for error is absence of a value which is properly expressed with nil.Algonquian
An additional reason for an error in Ecto queries is when the function expectes at most one result but instead multiple results are extracted by the query. So we have two different reason for an error, and two different way to handle them: nil when we get no result at all, and an exception when we get multiple results. With tuples it would easy and elegant to handle these cases because we could use the 'case' or 'with' statements especially when multiple Ecto queries are chained together.Ingraham
D
1

This might do the trick

case Repo.get(User, id) do
  user when is_map(user) -> {:ok, user}
  nil -> {:error, "not found"}
end

Reffering to this Elixir, Ecto pattern matching conditional with db query not behaving as expected

Dorotea answered 4/1, 2018 at 12:3 Comment(0)
C
0

Hey I am kinda new to elixir but I think you could use the guard clause here

case Repo.get!(User, id) do
  {:ok, user} ->
    #do something
  {:error, message} when :error === Ecto.NotSingleResult ->
    #pass the error
end
Customhouse answered 11/4, 2015 at 17:42 Comment(1)
The problem is that Ecto.Repo.whatever doesn't return a tuple like {:ok, ok} or {:error, error} and it doesn't match either case clause. I have it now in a private function basically doing a [Repo.get()] |> List.count() and matching nil to :error and 1 to :ok, but I thought there might be n easier way.Eternalize

© 2022 - 2024 — McMap. All rights reserved.