I am working on big update to my ecto_function library. It is inspired by Nx support for defn functions, and my thought was “well, why not, check if it will be possible to do something similar for DB queries”. It seems that it is more or less possible:
defq clamp(val) do
cond do
val < 0 -> 0
val > 1 -> 1
true -> val
end
end
Which when used like:
from e in "entires",
select: clamp(e.value)
Will be translated to:
SELECT
CASE
WHEN e.value < 0 THEN 0
WHEN e.value > 1 THEN 1
ELSE e.value
END
FROM entries e
This may be useful when one would like to move more computation to the DB where sometimes it may make more sense (move your computation to your data instead of moving data to your computation).
Question is whether there will be will for such constructs and whether community will find something like that useful.
There is already working PoC on the GitHub
But I would live to get some more insights before I dig more into it.
I really like all these libs that make devs lives easier. I have some concerns with this approach though:
It’s not very explicit. I mean, having just the letter q by the end of def does not make it very clear that this is a macro that is going to magically turn your elixir code into an Ecto.Query
Magic is normally hard to customize. I can see myself using your library for simple queries and falling back to Ecto’s default query builder for more complex stuff. But some people really like to push the limits of what you initially intended for the lib (specially people that do not have much experience with magic), so that can backfire to a lot of unexpected results.
Anyway, I liked your idea and I might even give it a try on one of my livestream side projects in the future (if it ever makes to a final version of your lib)
Actually it doesn’t change it into Ecto.Query, it changes it into macro that produces fragment/1.
defq clamp(val) do
cond do
val < 0 -> 0
val > 1 -> 1
true -> val
end
end
Will expand to:
defmacro clamp(val) do
quote do
fragment(
"CASE WHEN ? THEN ? WHEN ? THEN ? ELSE ? END",
unquote(val) < 0, 0,
unquote(val) > 1, 1,
unquote(val)
)
end
end
As shown above it is meant to be used within regular Ecto.Query queries, it will just provide a way to define more complex queries without manually writing fragments.
I see. My concern still applies though. By looking at the code, it only feels like that is going to run a condition and return a plain value, but instead, the code is magically turned into a fragment.
I’m not saying that no one should use it because of that though, just saying that one might want to be careful with the amount of logic put on these functions, as something might not work exactly as expected.
Also, have you ever checked dynamic/2? Not sure if it wouldn’t actually make sense to use that one instead of fragments.