Dynamic Queries in Ecto (Elixir Lang)
bartoszgorka.com
bartoszgorka.com
Usually, there are two camps on data accessing:
1. Anti-ORM because object-relational impedance mismatch: ORM will always be a leaky abstraction, and it introduces its own learning curve and mental overhead.
2. Pro-ORM: despite the impedance mismatch, the alternatives usually suck when it comes to writing: The boilerplate, the ad-hoc-ness. Very often people would use query builders to build yet another leaky abstraction so ORM seems to be the necessary evil.
Meanwhile Ecto:
1. It's one of the best query builders on the market. Thanks to Elixir macros, it's very flexible and you can just use functions to parameterize and reuse them very easily.
2. It's not an ORM but there's also a Schema as a shorthand counterpart for regular things.
3. By the way, it doesn't lazy load so n+1 is not a problem anymore.
The main issue for me with ecto is the lack of helpers with mass inserts, thinks like activerecord import & in_batches of activerecord would be nice.
If anyone is interested, I found this article showing how to use changesets outside the database:
I feel like I always run into some thing that at best isn't intuitive to express/read and at worse, cannot be expressed. If I remember correctly, when I was learning Elixir/Ecto, https://github.com/elixir-ecto/ecto/issues/1616 issue and the lack of lateral join support caused me issues.
Want to create a user?
"insert into users (id, name, status) values ($1, $2, $3)"
Our query builder takes pretty raw SQL fragments:
q = Query.new()
|> Query.select("u.id, u.name")
|> Query.from("users u")
|> Query.from("join authentications a using (id)")
q = case params[:status] do
nil -> q
status -> Query.where(q, "u.status", :eq, status)
end
rows = Query.rows!(q)
Looking through the docs, in Ecto, that join would be: |> join(:inner, [u], a in Authentiction, on: u.id == a.id)
Which I find needlessly abstract.(I will admit that if you really do need to support multiple databases, the abstraction is useful).
from u in User,
join: a in assoc(u, :authentications),
select: %{id: u.id, name: u.name, some_column: a.some_column}
or if you just want to load all the authentications from u in User, preload: [:authentications]
(Note: both of these queries are from memory and not tested, syntax errors may apply)With Python didn't encounter such problems. I assume this is more of a user problem - as I wouldn't describe myself as an Ecto or a sqlite expert.
A bit of a shame that Elixir isn't as popular as other languages. Solving problems can get a bit tedious because you can't just google it and find your answer in a stackoverflow post.
A while back[1] I mentioned how I find it hard to live without Rails gems like Pagy, Ransack and Pundit.
But after a deep dive with Ecto I now find it almost silly that I was looking for packages to handle those things, they're dead simple with Elixir, Ecto and Phoenix.
I had similar case with migration between languages - you want to use already used tools but for this new language. However, sometimes you can use even better tools :)
Check-out this repository for an example (and other single-file examples):
https://github.com/wojtekmach/mix_install_examples/blob/main...
We're using Ecto, but I'm still looking for that easy tool that just seem wonderful to me. $.02