Best practices for writing Ecto Queries in Phoenix

Hi all,

I’ve been really enjoying learning Elixir, Phoenix, and Ecto. Spending a lot of time in the docs. I’m wondering what the best practice for writing queries.

I wrote a query this way because it was closer to SQL and easier for me to think in, over Elixir/Phoenix approaches.

# SQL-like Version
Repo.all(
  from u in User,
    where: u.id not in subquery(
      from p in Participant,
      where: p.meeting_id == ^meeting_id,
      select: p.user_id
    )
)

But I wasn’t satisfied with not giving it a more Elixir/Phoenix look and feel using pipes. So I came up with this:

# Pipe Operator Version
User
|> where([u], u.id not in subquery(
      Participant
      |> where([p], p.meeting_id == ^meeting_id)
      |> select([p], [p.user_id])
)) |> Repo.all

I’ll admit, I’m kind of hooked on the |> operator, but looking at that code it doesn’t look as clean as the first way.

Aesthetics aside, I’m wondering is there a reason to choose one way over the other. Also, keen to hear any feedback on the Pipe Operator Version (e.g., is there a better or cleaner way to write it).

Cheers!

FWIW - I do it the first way the vast majority of the time because it looks more like the SQL of my past.

1 Like

Actually, they both are Elixir approaches :slight_smile:

Usually I try to break query into smaller functions, give them descriptive names and use the second approach, while these small functions can be implemented in whatever style depending on aesthetics.
I use the first approach when I have a big complex query. Definition of “big complex query” is very subjective, but usually such queries have multiple bindings. I also like the pipe operator version because it allows applying Repo.all without extra variable. So, instead of this

query = from p in Participant, where: p.meeting_id == ^meeting_id
Repo.all(query)

I’d prefer the second approach:

Participant
|> where(meeting: ^meeting_id)
|> Repo.all()
1 Like