spapas

spapas

Create dynamic bindings for where clause

Hello,

the where macro (among others) gets an array of query positions as an argument ie query |> where([c0, c1, c2], c2.name = 32). How could I create the array of named bindings [c0, c1, c2] and the actual binding (c2) dynamically ? For example I lets say I have something like {name: 2} and I’d like to get both [c0, c1, c2] and c2.name=32 (or field(c2, ^“name”) in order to create a dynamic filter. Is this even possible? Do I need to use macros? Can somebody point me to the correct direction.

Thank you

Most Liked

spapas

spapas

After some clarifications on the elixir-lang IRC channel from @josevalim it seems that @peerreynders suggestion was correct. Using named bindings can solve this problem!

So, if you have a query with proper named bindings you can pass them to your filter specification in order to be used when querying. Here’s a small snippet on how you can such dynamic filters:

  query =  from(w in whs_query, as: :withholding,
             join: b in Beneficiary, as: :beneficiary,
             on: [id: w.beneficiary_id])

  binding = :withholding
  field = :number
  value = "1"
​
  query |> where(
            [{^binding, t}],
            field(t, ^field) == ^value
          )

Thank you very much for helping with this !

peerreynders

peerreynders

While you’ve explained how you want to do it - it’s not clear why you would want to do it in this particular fashion.

I’m just mentioning this because in many use cases it makes sense to switch away from positional bindings and use named bindings instead - that is, any binding that may need to be referenced later in a composition is named so that it can be referenced by name rather than position later.

spapas

spapas

@peerreynders yes I guess I fell into the trap of asking of a way to implement the solution I had in my mind than describing the actual problem :slight_smile:

I more or less want to create a dynamic query filtering mechanism. For example, I’ve got the following query:

    whs_query =
      from(w in Withholding,
        join: b in Beneficiary,
        on: [id: w.beneficiary_id],
        join: a in Authority,
        on: [id: w.authority_id],
        join: ak in AuthorityKind,
        on: [id: a.authority_kind_id],
        preload: [beneficiary: b, authority: {a, authority_kind: ak}],
        order_by: [asc: w.pay_date, asc: w.id]
      )

and I want to filter on various fields from all relations (w, b, a, ak).

Now in non-dynamic case, I’ll create a schemaless changeset using something like

    types = %{
      status: :string,
      afm: :string,
      # year: :integer,
      authority: :string,
      authority_kind: :string
    }

     {%{}, types}
     |> Ecto.Changeset.cast(params, Map.keys(types))

and finally retrieve the changes from the changeset and filter on the initial query:

changes = Map.fetch!(changeset, :changes) |> IO.inspect()

    whs_query =
      case changes[:status] do
        nil -> whs_query
        v -> whs_query |> where([w], w.status == ^v)
      end

  whs_query =
      case changes[:afm] do
        nil -> whs_query
        v -> whs_query |> where([w, b], b.afm == ^v)
      end


# etc

This works fine however it is a lot of work to do all this again and again for all my filters.

So instead I thought of modeling the filters I’d need for each query using a simple structure like:

filters= [
      %{name: :status, type: :string, pos: 0},
      %{name: :afm, type: :string, pos: 1}
    ] 

– please notice that the pos defines the position in the named query i.e status is for Withholding (pos 0) while afm is for Beneficiary (pos 1) –

and then do something like:

    changeset = QueryFilterEx.make_filter_changeset(filters, filter_params)
    
    whs_query =
      from(w in whs_query,
        join: b in Beneficiary,
        on: [id: w.beneficiary_id],
        join: a in Authority,
        on: [id: w.authority_id],
        join: ak in AuthorityKind,
        on: [id: a.authority_kind_id],
        preload: [beneficiary: b, authority: {a, authority_kind: ak}],
        order_by: [asc: w.pay_date, asc: w.id]
      ) |>  QueryFilterEx.filter(filters, changeset)

So now the problem is that I have the initial query which is passed to QueryFilterEx.filter and I want to dynamically filter it using the filters struct I defined before to add where clauses to it. This leads me to the problem I described in my 1st and 2nd post :frowning:

Also, concerning named bindings i really don’t think that it would solve my problem because I still wouldn’t be able to dynamically construct the aliases of the bindings in the where clauses.

BR,
Serafeim

Where Next?

Popular in Questions Top

Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&query=perfume&page=2, I would like to get: ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New

We're in Beta

About us Mission Statement