Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
July 29, 2021 01:16 pm GMT

Organizando queries com query composition no elixir

O Elixir fornece um mdulo chamado Ecto.Query para que possa ser possvel efetuar consultas e alteraes nos dados de uma aplicao.

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

Porm em projetos grandes, podemos acabar tendo queries mais complexas e maiores, o que pode tornar a utilizao do Ecto.Query um problema se no tiver um certo cuidado.

Para exemplificar vamos pegar um exemplo onde fazemos algumas consultas de pessoas usurias 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()  endend

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 juno das 2 primeiras. Ento podemos utilizar Query composition para diminuir a duplicao de cdigo:

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 funes e mais queries. Para resolver esse problema podemos criar um mdulo especfico para as queries desse contexto:

# my_app/lib/my_app/users/user_queries.exdefmodule 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: Userend# my_app/lib/my_app/users.exdefmodule 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()  endend

A funo Repo.one() retorna o registro, caso encontrado, ou nil. Para no deixarmos o tratamento desse erro para camadas superiores do projeto, podemos fazer uma ltima refatorao no contexto de Users , tratando esses erros e deixar os retornos mais ntidos:

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()  endend

Original Link: https://dev.to/viniciussilveira/organizando-queries-com-query-composition-no-elixir-301p

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To