Interest check in query functions

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.

11 Likes

I really like all these libs that make devs lives easier. I have some concerns with this approach though:

  1. 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
  2. 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) :smiley:

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.

1 Like

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.