franckstifler

franckstifler

How do you structure your queries?

Hey all. I have an organization question. How do you structure your contexts in relation to queries. Let’s say I have a User module and he has friends, images, profile. There are places I don’t need to preload all of those relationships. I might only need the user without preloads, or with only the friends and profile, or only the user with profile. It’s also possible I might need to modify my queries for things like ordering, filtering…
How do you overcome this issue.

Most Liked

kelvinst

kelvinst

Oh, also, something I started doing is to use Ecto.Query.dynamic/2 instead of actual queries to extract reusable query snippets, like this:

defmodule UserQueries do
  def from_workspace(workspace_id), do: dynamic([u], u.workspace_id == ^workspace_id)
  def by_role(role), do: dynamic([u], u.role == ^role)
  def confirmed(true), do: dynamic([u], not is_nil(u.confirmed_at))
  def confirmed(false), do: dynamic([u], is_nil(u.confirmed_at))
end

So you can use that as building blocks to build complex queries like:

import Ecto.Query
import UserQueries

User
|> where(^dynamic(
  ^from_workspace(1) and 
  ^confirmed(true) and 
  (^by_role("admin") or ^by_role("owner"))
)) 
|> Repo.all()

Which would not be possible on a chain of |> with functions that build new queries using wheres, for example. I know or_wheres exist, but note that you can’t fully customize its precedence on those pipe chains.

PS.: I also know my or example could be handle with an in, but hopefully you got my point.

dimitarvp

dimitarvp

Might not be a popular opinion but the way I read module and function names made me go for this:

defmodule MyApp.UserQuery do
  def get(queryable, id), do: ...
  def list(queryable), do: ...
  def with_profile(queryable), do: ...
  def with_projects(queryable), do: ...
end

So when I need to compose a query I do this:

import MyApp.UserQuery

users =
  MyApp.Accounts.User
  |> get(123)
  |> with_projects()

Pretty opinionated and not entirely in line with general recommended Phoenix practices but it’s how reading code flows in my head and it’s IMO quite readable and quick to parse for a human.

kelvinst

kelvinst

Not really something I do every time, so I would not call is “how I structure my queries”, but one pattern I used sometimes for filters for example is something like this:

defmodule Accounts do
  def list(filter \\ []) do
    User
    |> apply_filter(filter)
    |> Repo.all()
  end

  defp apply_filter(q, []), do: q
  defp apply_filter(q, [{:ids, ids} | rest]), do: q |> where([u], u.id in ^ids) |> apply_filter(rest)
  defp apply_filter(q, [{:role, role} | rest]), do: q |> where([u], u.role in ^ids) |> apply_filter(rest)
  defp apply_filter(q, [{:confirmed, true} | rest]), do: q |> where([u], not is_nil(u.confirmed_at)) |> apply_filter(rest)
  defp apply_filter(q, [{:confirmed, false} | rest]), do: q |> where([u], is_nil(u.confirmed_at)) |> apply_filter(rest)
  ...
end

I sometimes extract functions on a UserQuery module too, like @dimitarvp does, but I normally just do that for queries that are actually going to be reused on a new place. So I start adding the wheres on the contexts, and when I realize that the query could be reused, I start extracting functions for composing them.

Where Next?

Popular in Discussions Top

vans163
So useless benchmarks aside, Its possible to write a webserver that can serve 300k requests per second (perhaps more with optimizations)....
New
Donovan
Hello everyone, I’m so glad to have discovered this awesome community. Thanks for creating it! This is my second post, and apologies for...
New
mmport80
I have put far too much effort into Dialyzer over the last year or so - and basically - I doubt it’s worth the effort. It’s not as easy ...
New
mmmrrr
Just saw that dhh announced https://hotwire.dev/ Is it just me or is this essentially live view? :smiley: Although I like the “iFrame-e...
New
praveenperera
How We Replaced React with Phoenix By: Thought Bot
New
Ankhers
Just a little information upfront. Generally speaking, if I feel like I need to either break a pipe chain or use an anonymous function in...
New
jer
I’ve been using umbrellas for a while, and generally started off (on greenfield projects at least) by isolating subapps based on clearly ...
New
und0ck3d
Hello everyone! A few days ago I’ve created a topic here about how people were creating CMSs with Elixir and Phoenix. I’ve been studying...
New
jesse
Hi everyone, I hesitated to post this here because I don’t want you to think I’m spamming, but I’ve been working on a Platform-as-a-Serv...
New
AstonJ
Can you believe the first professionally published Elixir book was published just 8 years ago? Since then I think we’ve seen more books f...
New

Other popular topics Top

skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New

We're in Beta

About us Mission Statement