iwatakeshi

iwatakeshi

Building a dynamic query using recursion

Hi :wave:, I’ve been playing around with Elixir and its ecosystem for a few weeks now and I really like it so far. Also, this is my first post here so please bear with me :bowing_man:t2:‍♂.

I’m trying to mimic how Amazon’s Amplify generates the filters for its GraphQL schemas using Absinthe.
So far I was able to get ne, eq, le, ge, gt, contains, and between to work. The hard part, however, is implementing and, or, and not (lines 88 - 90) since it involves recursion and Ecto.

I’ve created a small repl.it that shows what I’ve tried so far (maybe lots of code commented off as well :sweat_smile:). In the repl.it, I’m using strings to abstract things but the goal is to use Ecto’s dynamic query feature.

The input will look something like this:

input = %{
  and: [%{ and: [%{ b: %{ eq: "4"}}], c: %{ eq: "3"}}],
  or: [%{ d: %{ eq: "2"}}],
  a: %{ eq: "1"}
}

and the output will be expressed as something like:

# a and (b == 4 and c == 3) or (d == 2)

I think I’m on the right track. I know that I eventually have to reduce a tree-like structure to a single query, but I’m not sure how to deal with a binary operator such and and or within Enum.reduce so any help would be greatly appreciated.

Thank you.

Random thought:

I think it would be super neat if Absinthe could just build the tables and hook everything up including filtering, relay, etc with just a schema like Amplify… this would be a game changer.

Most Liked

al2o3cr

al2o3cr

I interpret that document as describing a different structure where both and and or take the terms they apply to in a list. For your example, start by writing the expression with explicit parentheses:

(a == 1 and (b == 4 and c == 3)) or (d == 2)

Rules:

  • %{variable_name: %{eq: value}} corresponds to variable_name == value
  • %{var1: %{eq: v1}, var2: %{eq: v2}} corresponds to (var1 == v1) and (var2 == v2)
  • %{and: [X, Y, Z]} corresponds to the expression (X and (Y and Z))
  • %{or: [X, Y, Z]} corresponds to the expression (X or (Y or Z))

so

%{
  a: %{eq: 1},
  b: %{eq: 4},
  c: %{eq: 3}
}

corresponds to (a == 1 and (b == 4 and c == 3)). So finally

%{
  or: [
    %{
      a: %{eq: 1},
      b: %{eq: 4},
      c: %{eq: 3}
    },
    %{
      d: %{eq: 2}
    }
  ]
}

corresponds to the original expression.

The recursion you’ll need to turn the map above into Ecto operations is the derivation in reverse. You’ll likely want to use explicit recursion for this, as it’s a depth-first traversal of the input.

Where Next?

Popular in Questions Top

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
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
New
Kagamiiiii
Student & New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New

Other popular topics Top

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
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
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
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 Postg...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

We're in Beta

About us Mission Statement