How do i produce an elixir struct from erlang

OPs problem is no different from receiving form data and building a dynamic query. The first step is to structure the data, then you traverse the data building dynamic queries: https://hexdocs.pm/ecto/dynamic-queries.html

The fact it is a macro does not mean it has to happen at compile time. After all, the macro can simply choose to do all of the work at runtime (which is what dynamic queries do).

1 Like

thank you for the hint, but I don’t think that I again need to learn how to implement a calculator using yecc, I think I still remember what I studied in the early '90s. :wink:

I do think I need to understand how to combine clauses (field operator value) with bool-operators, with the goal to get a single Ecto.Query.BooleanExpr which I would then put in a one-element list, and feed to a Ecto.Query as its wheres value.

let’s agree that it’s not viable, building an Elixir struct form Erlang, but the impression at the moment is that we’re postponing the action without mentioning the tools needed to perform it.

I need to construct an Ecto.Query value.
this contains one Ecto.Query.FromExpr value, one Ecto.Query.SelectExpr (a list of fields), a list of Ecto.Query.JoinExpr, each associated to a Ecto.Query.QueryExpr, and one Ecto.Query.BooleanExpr, all of which I need to construct, none of which I know how to construct with a proper exposed API. my interpretation is detailed in another thread, and I did not understand whether I can do that, or which otherwise which would be the way to go.

I don’t mind constructing values in Erlang, nor limiting Erlang to building a structure that I would later translate to the Elixir value, but I still do not know how (for example) given this query string:

location.code = "GH2" or accession.code = "2018.0044"

which I have no trouble at all translating into:

or == . location code "GH2" == . accession code "2018.0044"
(or (== (. location code) "GH2") (== (. accession code) "2018.0044"))

it’s still not clear how I get to my target value:

%Ecto.Query.BooleanExpr{
  op: :and, 
  expr:
    {:or, [], [
      {:==, [], [
        {{:., [], [{:&, [], [2]}, :code]}, [], []}, 
        "GH2"]}, 
      {:==, [], [
        {{:., [], [{:&, [], [1]}, :code]}, [], []}, 
        "2018.0044"]}]}}]}

as said: “for example”.

1 Like

It is not the yecc part that would be important in the calculator exercise. It was clear from previous posts you can handle that part just fine. The important bit about the calculator is how to traverse and evaluate the calculator AST once it has been built. That plus Ecto.Query.dynamic (linked here) is what is missing for you to go from your AST to an Ecto query. You don’t need to access any of Ecto.Query.* private structs to do it.

EDIT: here is an example of how to achieve what you want with the pieces above:

So assuming you have the following text:

location.code = "GH2" or accession.code = "2018.0044"

Which I am choosing to be parsed into the following format by yecc:

{:or, {:==, :location, :code, "GH2"}, {:==, :accession, :code, "2018.0044"}}

You can build a query from it like this:

    import Ecto.Query

    # Set the binding as seen in the ast (i.e. :location / :accession)
    from Location, as: :location,
      join: Accession, as: :accession,
      where: ^where_from_ast(ast)

    defp where_from_ast({:or, left, right}) do
      left = where_from_ast(left)
      right = where_from_ast(right)
      dynamic(^left or ^right)
    end

    defp where_from_ast({:==, binding, field, value}) do
      dynamic([{^binding, t}], field(t, ^field) == ^value)
    end

The query above has two joins and a boolean expression, all built without changing the underlying structures. :slight_smile:

8 Likes