No.
#{
'__struct__' => 'Elixir.Plug.Conn',
%% etc
}
No.
#{
'__struct__' => 'Elixir.Plug.Conn',
%% etc
}
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
.
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.
Take a look at https://hexdocs.pm/ecto/Ecto.Query.html#t:t/0, you are missing a lot of fields.
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.
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{}
…
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?
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.
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.
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).
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.
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”.
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.