An Interest In:
Web News this Week
- April 27, 2024
- April 26, 2024
- April 25, 2024
- April 24, 2024
- April 23, 2024
- April 22, 2024
- April 21, 2024
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
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To