How do i produce an elixir struct from erlang

No.

#{
  '__struct__' => 'Elixir.Plug.Conn',
  %% etc
}
1 Like

do you think you can help me with this error message?

message: "got FunctionClauseError with message \"no function clause matching in Inspect.Ecto.Query.from_sources/1\" while inspecting %{__struct__: Ecto.Query, from: %{__struct__: Ecto.Query.FromExpr, source: {\"plant\", nil}}, joins: {:==, [{{:., [], [{:domain, [], Elixir}, :id]}, [], []}, 3]}, select: [expr: [{{:., [], [{:&, [], [0]}, :id]}, [], []}]], wheres: {:==, [{{:., [], [{:domain, [], Elixir}, :id]}, [], []}, 3]}}"

please do not focus on the wheres and joins parts, I have not yet rewritten them.

but why do I see %{__struct__: Ecto.Query.FromExpr, instead of %Ecto.Query.FromExpr{

All elixir modules, which includes structs, have an Elixir. prefix. You don’t see this when you are writing literal modules in Elixir, but you’ll need to specify it from Erlang.

You need Elixir.Ecto.Query instead.

That indicates that there are either to much or to few fields compared to the defstruct.

1 Like
query -> domain where expression : #{'__struct__' => 'Elixir.Ecto.Query', from => '$1', select => [{expr, [{{'.', [], [{'&', [], [0]}, id]}, [], []}]}], wheres => '$3', joins => '$3'}.
domain -> word : #{'__struct__' => 'Elixir.Ecto.Query.FromExpr', source => {list_to_binary(extract_value('$1')), nil}}.

The posted snippet was alixir syntax, so the :Elixir prefix was there but implicit.

2 Likes

Take a look at https://hexdocs.pm/ecto/Ecto.Query.html#t:t/0, you are missing a lot of fields.

1 Like

I know I am not defining all fields. if I do this from Elixir, it works fine, as I wrote here.

I’m now trying to reproduce that, from the parser, in erlang.

the error message explicitly mentions from_sources, which I would like to understand.

Struct syntax in elixir does a lot of magic, you need to do that magic on your own when writing Erlang.

Especially you need to manually and explicitly specify each key with its default value.

2 Likes

well, I’m afraid I just managed to break that magic, but in a direction opposite as what I was aiming at.

within this project this Ecto.Query.FromExpr{source: {"plant", nil}} does not work any more:


iex(1)> w = %Ecto.Query{}
#Ecto.Query<from q in query>
iex(2)> w = %{ w | from: %Ecto.Query.FromExpr{source: {"plant", nil}}}
** (CompileError) iex:2: Ecto.Query.FromExpr.__struct__/1 is undefined, cannot expand struct Ecto.Query.FromExpr
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
    (stdlib) lists.erl:1355: :lists.mapfoldl/3
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
    (stdlib) lists.erl:1355: :lists.mapfoldl/3
iex(2)> %Ecto.Query.FromExpr{source: {"plant", nil}}
** (CompileError) iex:2: Ecto.Query.FromExpr.__struct__/1 is undefined, cannot expand struct Ecto.Query.FromExpr

iex(2)> 

It should exist and is defined here:

Anyway, @moduledoc false clearly states that this module and anything defined by it, is not meant to be used by anything outside of ecto.

It is considered internal use only. You need to find another way to create your %Ecto.Query{}

2 Likes

what can I say. I’m short of options. this is why I’m asking here, and on stackoverflow.

Have you considered asking the ecto team for a function based API to build queries at runtime rather than build time?

1 Like

you lost me. you mean that asking here is the way to go?

I mean that you should file a bug at the ecto repository, asking for a API that can be used to create queries at runtime (function based) rather the current macro-based API which does construction only during compile time.

1 Like

The API for query creation is exactly what we have today. You are thinking about using the internals but it is not necessary to fiddle with them all. Our query api supports both interpolation and dynamic values. For example, imagine you have to parse “foo:43” and convert it to a query. You can do this:

query = "table" # or Schema
[field, value] = String.split("foo:43", ":")
field = String.to_existing_atom(field)
query = from q in querry, field(q, ^field) == ^value

And so on. Ben mentioned the correct way to solve this in the forum here: How to produce executable code using yecc? - In a nutshell, you shouldn’t build the query inside the parser because the parser goal is to return an AST. Then, in Elixir code, you traverse the AST, building the Ecto query.

For example, for “foo:43”, your parser should return something like:

{:where, :foo, "43"}

Then you can traverse this AST and convert it into an Ecto query.

As a good exercise, I would suggest for you to implement a calculator using yecc. Do not have yecc perform the computations. Instead you should have yecc return an AST (so 1 + 2 returns {:+, 1, 2}) and then you traverse this AST in Elixir computing the final result.

4 Likes

But the OP wants to create an ecto query at runtime from user input. How shall he continue after his parser returned the AST?

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