Currently I have an Ecto query which is sorted by an order_by
argument like so:
query =
Ecto.Queryable.to_query(queryable_passed_in_as_an_argument)
|> Ecto.Query.order_by(^order_by)
order_by
is a keyword list of directions and field names, like [asc: :name, desc: :id]
. This order_by
arg is constructed by a different module from where the query is actually running.
However, in a more advanced case I would like to sort by an arbitrary fragment on the model, such as my_jsonb_column->>'Name'
. This would be simple to do if I were running the entire query from the same place where the order_by
was being constructed, like so:
from(i in Item, order_by: fragment("my_jsonb_column->>'Name' ASC"))
But that is not how this code is structured, and I can’t really restructure it like that. (If you want to know why, the real code where this is happening is in this GitHub comment. The order_by
basically gets constructed when the arguments to the GraphQL API call are parsed, which is much earlier than when the eventual query is run.)
It appears that fragment/1
is not usable outside of a query function like from
, and if I attempt to call fragment(^order_by_string)
with order_by_string = "my_jsonb_column->>'Name' ASC"
, Ecto refuses as it thinks I’m opening myself to SQL injection:
** (ArgumentError) to prevent SQL injection attacks, fragment(...) does not allow strings to be interpolated as the first argument via the `^` operator, got: `["payload->>'Name' ASC"]`
Is there any way to order an Ecto query by an arbitrary string fragment when that string is in a variable and not available at compile time?