Organizando queries com query composition no elixir

O Elixir fornece um módulo chamado Ecto.Query para que possa ser possível efetuar consultas e alterações nos dados de uma aplicação.

Dessa forma podemos escrever queries sem a necessidade da escrita de SQLs, dando maior facilidade e flexibilidade na e…


This content originally appeared on DEV Community and was authored by Marcos Vinicius O. Silveira

O Elixir fornece um módulo chamado Ecto.Query para que possa ser possível efetuar consultas e alterações nos dados de uma aplicação.

Dessa forma podemos escrever queries sem a necessidade da escrita de SQLs, dando maior facilidade e flexibilidade na escrita de consultas.

Porém em projetos grandes, podemos acabar tendo queries mais complexas e maiores, o que pode tornar a utilização do Ecto.Query um problema se não tiver um certo cuidado.

Para exemplificar vamos pegar um exemplo onde fazemos algumas consultas de pessoas usuárias de um determinado sistema:

defmodule MyApp.Users do
  import Ecto.Query, only: [from: 2, where: 2]

  alias MyApp.Users.User

  def get_user_by_email(email) do
    Repo.get_by(User, email: email)
  end

  def get_users_by_city_name(city_name) do
    from(user in User, join: city in City, on: city.id == user.city_id, where: city == ^city_name)
    |> Repo.all()
  end

  def get_user_by_email_and_city(email, city_name) do
    from(user in User,
      join: city in City,
      on: city.id == user.city_id,
      where: city.name == ^city_name,
      where: user.email == ^email
    )
    |> Repo.one()
  end
end

Perceba que temos aqui 3 buscas, uma por email, uma por city, e uma terceira que busca uma pessoa por email e pela city registrada. Se analisarmos com calma, podemos ver que a terceira consulta é na verdade uma junção das 2 primeiras. Então podemos utilizar Query composition para diminuir a duplicação de código:

defmodule MyApp.Users do
  import Ecto.Query, only: [from: 2]

  alias MyApp.Repo

  def get_user_by_email(email) do
    email
    |> user_by_email()
    |> Repo.one()
  end

  def get_users_by_city_name(city_name) do
    city_name
    |> users_by_city_name()
    |> Repo.all()
  end

  def get_user_by_email_and_city(email, city) do
    email
    |> user_by_email()
    |> users_by_city_name(city)
    |> Repo.one()
  end

  defp user_by_email(query \\ base(), email) do
    from(user in query,
      where: user.email == ^email
    )
  end

  defp users_by_city_name(query \\ base(), city_name) do
    from(user in query,
      join: city in City,
      on: city.id == user.city_id,
      where: city.name == ^city_name
    )
  end

  defp base, do: __MODULE__
end

Mas agora vamos supor que esse sistema vá crescendo e o contexto fique cada vez maior, com mais funções e mais queries. Para resolver esse problema podemos criar um módulo específico para as queries desse contexto:

# my_app/lib/my_app/users/user_queries.ex
defmodule MyApp.Users.UserQueries do
  import Ecto.Query, only: [from: 2]

  alias MyApp.Users.User

  def user_by_email(query \\ base(), email) do
    from(user in query,
      where: user.email == ^email
    )
  end

  def users_by_city_name(query \\ base(), city_name) do
    from(user in query,
      join: city in City,
      on: city.id == user.city_id,
      where: city.name == ^city_name
    )
  end

  defp base, do: User
end

# my_app/lib/my_app/users.ex
defmodule MyApp.Users do
  alias MyApp.Repo

  alias MyApp.Users.UserQueries

  def get_user_by_email(email) do
    email
    |> UserQueries.user_by_email()
    |> Repo.one()
  end

  def get_users_by_city_name(city) do
    city
    |> UserQueries.users_by_city_name()
    |> Repo.all()
  end

  def get_user_by_email_and_city(email, city_name) do
    email
    |> UserQueries.user_by_email()
    |> UserQueries.users_by_city_name(city_name)
    |> Repo.one()
  end
end

A função Repo.one() retorna o registro, caso encontrado, ou nil. Para não deixarmos o tratamento desse erro para camadas superiores do projeto, podemos fazer uma última refatoração no contexto de Users , tratando esses erros e deixar os retornos mais nítidos:

defmodule MyApp.Users do
  alias MyApp.Users.{User, UserQueries}

  def get_user_by_email(email) do
    case do_get_user_by_email(email) do
      %User{} = user -> {:ok, user}
      nil -> {:error, {:not_found, "User not found"}}
    end
  end

  def get_users_by_city_name(city) do
    city
    |> UserQueries.users_by_city()
    |> Repo.all()
  end

  def get_user_by_email_and_city_name(email, city_name) do
    case do_get_user_by_email_and_city_name(email, city_name) do
      %User{} = user -> {:ok, user}
      nil -> {:error, {:not_found, "User not found"}}
    end
  end

  defp do_get_user_by_email(email) do
    email
    |> UserQueries.user_by_email()
    |> Repo.one()
  end

  defp do_get_user_by_email_and_city_name(email, city_name) do
    email
    |> UserQueries.user_by_email()
    |> UserQueries.users_by_city_name(city_name)
    |> Repo.one()
  end
end


This content originally appeared on DEV Community and was authored by Marcos Vinicius O. Silveira


Print Share Comment Cite Upload Translate Updates
APA

Marcos Vinicius O. Silveira | Sciencx (2021-07-29T13:16:14+00:00) Organizando queries com query composition no elixir. Retrieved from https://www.scien.cx/2021/07/29/organizando-queries-com-query-composition-no-elixir/

MLA
" » Organizando queries com query composition no elixir." Marcos Vinicius O. Silveira | Sciencx - Thursday July 29, 2021, https://www.scien.cx/2021/07/29/organizando-queries-com-query-composition-no-elixir/
HARVARD
Marcos Vinicius O. Silveira | Sciencx Thursday July 29, 2021 » Organizando queries com query composition no elixir., viewed ,<https://www.scien.cx/2021/07/29/organizando-queries-com-query-composition-no-elixir/>
VANCOUVER
Marcos Vinicius O. Silveira | Sciencx - » Organizando queries com query composition no elixir. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/07/29/organizando-queries-com-query-composition-no-elixir/
CHICAGO
" » Organizando queries com query composition no elixir." Marcos Vinicius O. Silveira | Sciencx - Accessed . https://www.scien.cx/2021/07/29/organizando-queries-com-query-composition-no-elixir/
IEEE
" » Organizando queries com query composition no elixir." Marcos Vinicius O. Silveira | Sciencx [Online]. Available: https://www.scien.cx/2021/07/29/organizando-queries-com-query-composition-no-elixir/. [Accessed: ]
rf:citation
» Organizando queries com query composition no elixir | Marcos Vinicius O. Silveira | Sciencx | https://www.scien.cx/2021/07/29/organizando-queries-com-query-composition-no-elixir/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.