mfrasca

mfrasca

How to produce executable code using yecc?

maybe it’s not the way to go, but this was my initial guess.

I have been writing a parser for a reduced query dialect that I have partially inherited and much expanded, allowing users write things like:

plant where accession.code='2018.0047'

it’s not ready, but the missing intermediate steps are clear, except the final one: how do I have the result executed?

I am targeting as result the quote representation of the equivalent Ecto.Query.from query. for the above example the equivalent as far as I am concerned would be:
from(p in "plant", select: [:id], join: a in "accession", on: a.id==p.accession_id, where: a.code=="2018.0047")

I have been looking into the structures returned by the __schema__ functions, and all looks quite doable, I mean I know how to extract the table name from the modules, and owner and related modules and keys from the association given its name, so let’s assume that my parser does return this value:

{:from, [context: Elixir, import: Ecto.Query],
 [
   {:in, [context: Elixir, import: Kernel], [{:p, [], Elixir}, "plant"]},
   [
     select: [:id],
     join: {:in, [context: Elixir, import: Kernel],
      [{:a, [], Elixir}, "accession"]},
     on: {:==, [context: Elixir, import: Kernel],
      [
        {{:., [], [{:a, [], Elixir}, :id]}, [], []},
        {{:., [], [{:p, [], Elixir}, :accession_id]}, [], []}
      ]},
     where: {:==, [context: Elixir, import: Kernel],
      [{{:., [], [{:a, [], Elixir}, :code]}, [], []}, "2018.0047"]}
   ]
 ]}

how do I get Ecto to execute it?

Most Liked Responses

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Sure, so let’s do some practice. What I don’t recommend is trying to look at the ecto functions and see how they work, because they’re pretty complicated internally. Technically they’re all macros, so it’s just a bit of a pain to figure out what is going on. There’s a reason for this (compile time protection from sql injection, yay!) but it’s definitely complicated.

Fortunatelly, using the ecto query macros isn’t that bad, but there’s no substitute for practice. I highly recommend working through the programming ecto book, or any of the other Elixir books that uses ecto (programming phoenix, the graphql book, etc). If you want to go for it just based on docs that’s fine too, just build some simple exercises for yourself. Here’s an example of a function from the graphql book that takes this kind of input:

%{filter: %{category: "drinks", priced_below: 10.00}, order: :desc}

And then builds an ecto query that filters / orders an Item schema accordingly:

def list_items(args) do
  args
  |> items_query
  |> Repo.all
end

def items_query(args) do
  Enum.reduce(args, Item, fn
    {:order, order}, query ->
      query |> order_by({^order, :name})
    {:filter, filter}, query ->
      query |> filter_with(filter)
  end)
end

defp filter_with(query, filter) do
  Enum.reduce(filter, query, fn
    {:name, name}, query ->
      from q in query, where: ilike(q.name, ^"%#{name}%")
    {:priced_above, price}, query ->
      from q in query, where: q.price >= ^price
    {:priced_below, price}, query ->
      from q in query, where: q.price <= ^price
    {:added_after, date}, query ->
      from q in query, where: q.added_on >= ^date
    {:added_before, date}, query ->
      from q in query, where: q.added_on <= ^date
    {:category, category_name}, query ->
      from q in query,
        join: c in assoc(q, :category),
        where: ilike(c.name, ^"%#{category_name}%")
    {:tag, tag_name}, query ->
      from q in query,
        join: t in assoc(q, :tags),
        where: ilike(t.name, ^"%#{tag_name}%")
  end)
end

If we pass the example input into the items_query function we see that it returns an ecto query:

iex(2)> query = PlateSlate.Menu.items_query(%{filter: %{category: "drinks", priced_below: 10.00}, order: :desc})
#Ecto.Query<from i in PlateSlate.Menu.Item, join: c in assoc(i, :category),
 where: ilike(c.name, ^"%drinks%"), where: i.price <= ^10.0,
 order_by: [desc: i.name]>

This query can then be executed by our repo:

Repo.all(query)

I’m suggesting that you should build a function similar to my items_query function that recursively walks through your yecc output and matches on various sub parts, reducing on to the ecto query data structure.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Right, this is entirely possible in Elixir. Build a datastructure in Yecc that represents the input. Build a recursive function that builds an ecto query by walking the datastructure yecc returns. If this seems like something you aren’t sure how to do, break the problem down. Play around with the ecto functions so that you get familiar with how to use them. Play around with walking data structures with an accumulator so that you can do simple things like count how many parts to the input there are. Then combine.

We’re happy to help with these steps, but you need to break the problem down into pieces you can work on incrementally.

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

Elixir code is compiled not interpreted. Dynamically compiling code all the time is not going to perform well, and may lead to unbound memory growth since you’d be generating compilation artifacts all the time.

By an ecto query data structure I mean a %Ecto.Query{} struct, which is what you get when you call the functions:

SomeSchema |> where(foo: ^whatever)

Where Next?

Popular in Questions Top

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
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
vac
Hi, I’m quite new in Elixir and I’m trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and I...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
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
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
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
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New

Other popular topics Top

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
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
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
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53690 245
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
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
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement