valyukov
Dynamic fields in Ecto queries
Hi there,
How I can build queries with dynamic fields assignment in Ecto?
I try to do next thing:
import Ecto.Query
field = :id
value = 1
from(o in Order, where: o[field] != ^value)
** (Ecto.Query.CompileError) `o[field]` is not a valid query expression
(ecto) expanding macro: Ecto.Query.where/3
iex:6: (file)
(ecto) expanding macro: Ecto.Query.from/2
iex:6: (file)
how you can see it doesn’t work, but who cares? keyword syntax allow to do next things:
from(o in Order, where: ^[{field, value}])
and this works fine, but only for simple cases, it doesn’t allow neglect syntax (!=), like, is not, etc.
I don’t give up, and try to apply some metaprogramming magic:
(quote do: unquote(Order) |> where([q], q.unquote(field) == ^unquote(var!(value)))) |> Code.eval_quoted |> elem(0)
And it works perfect, however I need to make join with dynamic association, the next example throw exception and I have no idea how to solve it:
assoc = :transaction
(quote do: unquote(Order |> Ecto.Queryable.to_query) |> join(:inner, [r], l in assoc(r, unquote(var!(assoc)))) |> where([r, l], l.unquote(field) == ^unquote(var!(value))))
|> Code.eval_quoted
|> elem(0)
** (CompileError) nofile: invalid quoted expression: #Ecto.Query<from o in Store.Order>
(ecto) expanding macro: Ecto.Query.where/3
nofile:1: (file)
(elixir) expanding macro: Kernel.|>/2
nofile:1: (file)
but if I quote module all works fine
how we see there is only one problem that join macros throw error when I try to pass Ecto.Query struct to it. Next snippet works well, but in my app I would like to pass exactly Ecto.Query to join.
assoc = :transaction
(quote do: unquote(Order) |> join(:inner, [r], l in assoc(r, unquote(var!(assoc)))) |> where([r, l], l.unquote(field) == ^unquote(var!(value))))
|> Code.eval_quoted
|> elem(0)
Schemes:
defmodule Order do
use Ecto.Schema
schema "orders" do
has_one :transaction, Transaction
end
end
defmodule Transaction do
use Ecto.Schema
schema "transactions" do
belongs_to :order, Order
end
end
Data:
orders: [{id: 1}, {id: 2}]
transactions: [{id: 1, order_id: 1}, {id: 2, order_id: 2}]
Marked As Solved
Linuus
To use dynamic fields you can use the field macro.
thing = :points
from a in Foo, where: field(a, ^thing) > 8
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #javascript
- #code-sync
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








