Current section

Files

Jump to
sorcery lib services store_adapter ecto query.ex
Raw

lib/services/store_adapter/ecto/query.ex

defmodule Sorcery.StoreAdapter.Ecto.Query do
@moduledoc false
import Ecto.Query
alias Sorcery.ReturnedEntities, as: RE
alias Sorcery.StoreAdapter.Ecto.Thread
def run_query(inner_state, wheres, finds) do
repo = inner_state.args.repo_module
config = inner_state.config_module.config()
tk_map = config.schemas
threads = Thread.all(wheres)
ctx = %{repo: repo, config: config, tk_map: tk_map, finds: finds}
# Possibly replace this with a Task.await_many
results = Enum.map(threads, fn t -> run_thread(t, ctx) end)
|> RE.merge()
{:ok, results}
end
def run_thread(%Thread{order: order, where_groups: wgs} = thread, %{tk_map: tk_map} = ctx) do
[lvar | tl_order] = order
wheres = wgs[lvar]
[%{tk: tk} | _] = wheres
mod = tk_map[tk]
finds = Map.filter(ctx.finds, fn {k, _} -> k in order end)
q = from(x in mod, as: ^lvar)
q = Enum.reduce(wheres, q, fn wc, q ->
add_where(q, wc, tk_map)
end)
ctx = Map.put(ctx, :q, q)
thread = Map.put(thread, :order, tl_order)
ctx = continue_thread(thread, ctx)
|> add_select(finds, thread)
ctx.repo.all(ctx.q)
|> convert_to_returned_entities()
|> assign_tks(wgs)
end
def continue_thread(%{order: []}, ctx), do: ctx
def continue_thread(%{order: [lvar | tl_order], where_groups: wgs} = thread, ctx) do
wheres = wgs[lvar]
ctx = add_join(ctx, lvar, wheres)
thread = Map.put(thread, :order, tl_order)
continue_thread(thread, ctx)
end
# {{{ add_join
def add_join(%{tk_map: tk_map} = ctx, lvar, wheres) do
[%{tk: tk} | _] = wheres
mod = tk_map[tk]
d_on = Enum.reduce(wheres, dynamic(true), fn
%{other_lvar: nil, op: :==, right: nil} = wc, d -> dynamic([{^lvar, child}], ^d and is_nil(field(child, ^wc.attr)) )
%{other_lvar: nil, op: :!=, right: nil} = wc, d -> dynamic([{^lvar, child}], ^d and not is_nil(field(child, ^wc.attr)) )
%{other_lvar: nil, op: :==} = wc, d -> dynamic([{^lvar, child}], ^d and field(child, ^wc.attr) == ^wc.right)
%{other_lvar: nil, op: :!=} = wc, d -> dynamic([{^lvar, child}], ^d and field(child, ^wc.attr) != ^wc.right)
%{other_lvar: nil, op: :in} = wc, d -> dynamic([{^lvar, child}], ^d and field(child, ^wc.attr) in ^wc.right)
%{other_lvar: nil, op: :<} = wc, d -> dynamic([{^lvar, child}], ^d and field(child, ^wc.attr) < ^wc.right)
%{other_lvar: nil, op: :<=} = wc, d -> dynamic([{^lvar, child}], ^d and field(child, ^wc.attr) <= ^wc.right)
%{other_lvar: nil, op: :>} = wc, d -> dynamic([{^lvar, child}], ^d and field(child, ^wc.attr) > ^wc.right)
%{other_lvar: nil, op: :>=} = wc, d -> dynamic([{^lvar, child}], ^d and field(child, ^wc.attr) >= ^wc.right)
%{op: :==, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = wc, d ->
dynamic([{^parent_lvar, parent}, {^lvar, child}], ^d and field(child, ^wc.attr) == field(parent, ^parent_attr))
%{op: :!=, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = wc, d ->
dynamic([{^parent_lvar, parent}, {^lvar, child}], ^d and field(child, ^wc.attr) != field(parent, ^parent_attr))
%{op: :in, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = wc, d ->
dynamic([{^parent_lvar, parent}, {^lvar, child}], ^d and field(child, ^wc.attr) in field(parent, ^parent_attr))
%{op: :<, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = wc, d ->
dynamic([{^parent_lvar, parent}, {^lvar, child}], ^d and field(child, ^wc.attr) < field(parent, ^parent_attr))
%{op: :<=, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = wc, d ->
dynamic([{^parent_lvar, parent}, {^lvar, child}], ^d and field(child, ^wc.attr) <= field(parent, ^parent_attr))
%{op: :>, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = wc, d ->
dynamic([{^parent_lvar, parent}, {^lvar, child}], ^d and field(child, ^wc.attr) > field(parent, ^parent_attr))
%{op: :>=, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = wc, d ->
dynamic([{^parent_lvar, parent}, {^lvar, child}], ^d and field(child, ^wc.attr) >= field(parent, ^parent_attr))
end)
q = join(ctx.q, :left, [], child in ^mod, on: ^d_on, as: ^lvar)
Map.put(ctx, :q, q)
end
# }}}
def initial_from([wc | _], tk_map) do
mod = tk_map[wc.tk]
lvar = wc.lvar
from(x in mod, as: ^lvar)
end
# {{{ WHERE
def add_where(ecto_query, where_clause, tk_map) do
case where_clause.right_type do
:literal -> add_literal_clause(ecto_query, where_clause)
:lvar -> add_lvar_clause(ecto_query, where_clause, tk_map)
end
end
def add_literal_clause(q, where_clause) do
%{lvar: lvar, attr: attr, right: value} = where_clause
case {where_clause.op, value} do
{:==, nil} -> where(q, [{^lvar, x}], is_nil(field(x, ^attr)))
{:==, _} -> where(q, [{^lvar, x}], field(x, ^attr) == ^value)
{:in, _} -> where(q, [{^lvar, x}], field(x, ^attr) in ^value)
{:!=, nil} -> where(q, [{^lvar, x}], not is_nil(field(x, ^attr)))
{:!=, _} -> where(q, [{^lvar, x}], field(x, ^attr))
{:>, _} -> where(q, [{^lvar, x}], field(x, ^attr) > ^value)
{:>=, _} -> where(q, [{^lvar, x}], field(x, ^attr) >= ^value)
{:<, _} -> where(q, [{^lvar, x}], field(x, ^attr) < ^value)
{:<=, _} -> where(q, [{^lvar, x}], field(x, ^attr) <= ^value)
end
end
def add_lvar_clause(q, where_clause, tk_map) do
%{tk: tk, lvar: child_lvar, attr: child_attr, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = where_clause
mod = tk_map[tk]
case where_clause.op do
:== -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) == field(parent, ^parent_attr), as: ^child_lvar)
:in -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) in field(parent, ^parent_attr), as: ^child_lvar)
:!= -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) != field(parent, ^parent_attr), as: ^child_lvar)
:> -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) > field(parent, ^parent_attr), as: ^child_lvar)
:>= -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) >= field(parent, ^parent_attr), as: ^child_lvar)
:< -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) < field(parent, ^parent_attr), as: ^child_lvar)
:<= -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) <= field(parent, ^parent_attr), as: ^child_lvar)
end
end
# }}}
# {{{ SELECT/FIND
def add_select(ctx, finds, _thread) do
lvars = Map.keys(finds)
[lvar | lvars] = lvars
q =
ctx.q
|> add_first_select(finds, lvar)
|> add_next_select(finds, lvars)
Map.put(ctx, :q, q)
end
def add_first_select(q, finds, lvar) do
lvarstr = "#{lvar}"
case Map.get(finds, lvar) do
nil -> q
_ -> select(q, [{^lvar, x}], %{^lvarstr => x})
end
end
def add_next_select(q, _finds, []), do: q
def add_next_select(q, finds, [lvar | lvars]) do
lvarstr = "#{lvar}"
case Map.get(finds, lvar) do
nil -> add_next_select(q, finds, lvars)
_ -> select_merge(q, [{^lvar, x}], %{^lvarstr => x})
end
|> add_next_select(finds, lvars)
end
# }}}
# {{{ ReturnedEntities formatting
def convert_to_returned_entities(li), do: convert_to_returned_entities(li, RE.new())
def convert_to_returned_entities([], re), do: re
def convert_to_returned_entities([items | li], re) do
#convert_to_returned_entities(li, re)
re = Enum.reduce(items, re, fn {lvarstr, entity}, re ->
RE.put_entities(re, lvarstr, [entity])
end)
convert_to_returned_entities(li, re)
end
def assign_tks(re, wgs) do
Enum.reduce(wgs, re, fn {lvar, [%{tk: tk} | _]}, acc ->
lvar_str = "#{lvar}"
RE.assign_lvar_tk(acc, lvar_str, tk)
end)
end
# }}}
end
#defmodule Sorcery.StoreAdapter.Ecto.Query do
# @moduledoc false
# import Ecto.Query
# alias Sorcery.ReturnedEntities, as: RE
#
# _comment = ~s"""
# Do not do this:
#
# from p0, where p0...
# join l1 on(...), where(l1)
#
# Instead, do this
# from p0, where p0...
# join l1 on( ... and l1)
#
# """
#
#
# def run_query(inner_state, wheres, finds) do
# repo = inner_state.args.repo_module
#
# config = inner_state.config_module.config()
# tk_map = config.schemas
#
# q = initial_from(wheres, tk_map)
# results = Enum.reduce(wheres, q, fn wc, q -> add_where(q, wc, tk_map) end)
# |> add_select(finds)
# |> repo.all()
# |> convert_to_returned_entities()
# |> assign_tks(wheres)
# {:ok, results}
# end
#
# def initial_from([wc | _], tk_map) do
# mod = tk_map[wc.tk]
# lvar = wc.lvar
# from(x in mod, as: ^lvar)
# end
#
# # {{{ WHERE
# def add_where(ecto_query, where_clause, tk_map) do
# case where_clause.right_type do
# :literal -> add_literal_clause(ecto_query, where_clause)
# :lvar -> add_lvar_clause(ecto_query, where_clause, tk_map)
# end
# end
#
# def add_literal_clause(q, where_clause) do
# %{lvar: lvar, attr: attr, right: value} = where_clause
# case {where_clause.op, value} do
# {:==, nil} -> where(q, [{^lvar, x}], is_nil(field(x, ^attr)))
# {:==, _} -> where(q, [{^lvar, x}], field(x, ^attr) == ^value)
# {:in, _} -> where(q, [{^lvar, x}], field(x, ^attr) in ^value)
# {:!=, nil} -> where(q, [{^lvar, x}], not is_nil(field(x, ^attr)))
# {:!=, _} -> where(q, [{^lvar, x}], field(x, ^attr))
# {:>, _} -> where(q, [{^lvar, x}], field(x, ^attr) > ^value)
# {:>=, _} -> where(q, [{^lvar, x}], field(x, ^attr) >= ^value)
# {:<, _} -> where(q, [{^lvar, x}], field(x, ^attr) < ^value)
# {:<=, _} -> where(q, [{^lvar, x}], field(x, ^attr) <= ^value)
# end
# end
#
# def add_lvar_clause(q, where_clause, tk_map) do
# %{tk: tk, lvar: child_lvar, attr: child_attr, other_lvar: parent_lvar, other_lvar_attr: parent_attr} = where_clause
# mod = tk_map[tk]
# join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) == field(parent, ^parent_attr), as: ^child_lvar)
# case where_clause.op do
# :== -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) == field(parent, ^parent_attr), as: ^child_lvar)
# :in -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) in field(parent, ^parent_attr), as: ^child_lvar)
# :!= -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) != field(parent, ^parent_attr), as: ^child_lvar)
# :> -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) > field(parent, ^parent_attr), as: ^child_lvar)
# :>= -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) >= field(parent, ^parent_attr), as: ^child_lvar)
# :< -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) < field(parent, ^parent_attr), as: ^child_lvar)
# :<= -> join(q, :left, [{^parent_lvar, parent}], child in ^mod, on: field(child, ^child_attr) <= field(parent, ^parent_attr), as: ^child_lvar)
# end
# end
# # }}}
#
#
# # {{{ SELECT/FIND
# def add_select(q, finds) do
# lvars = Map.keys(finds)
# [lvar | lvars] = lvars
# q
# |> add_first_select(finds, lvar)
# |> add_next_select(finds, lvars)
# end
#
# def add_first_select(q, finds, lvar) do
# lvarstr = "#{lvar}"
# case Map.get(finds, lvar) do
# nil -> q
# :* -> select(q, [{^lvar, x}], %{^lvarstr => x})
# find -> select(q, [{^lvar, x}], %{^lvarstr => map(x, ^find)})
# end
# end
# def add_next_select(q, _finds, []), do: q
# def add_next_select(q, finds, [lvar | lvars]) do
# lvarstr = "#{lvar}"
# case Map.get(finds, lvar) do
# nil -> add_next_select(q, finds, lvars)
# :* -> select_merge(q, [{^lvar, x}], %{^lvarstr => x})
# find -> select_merge(q, [{^lvar, x}], %{^lvarstr => map(x, ^find)})
# end
# |> add_next_select(finds, lvars)
#
# end
# # }}}
#
#
# # {{{ ReturnedEntities formatting
# def convert_to_returned_entities(li), do: convert_to_returned_entities(li, RE.new())
# def convert_to_returned_entities([], re), do: re
# def convert_to_returned_entities([items | li], re) do
# #convert_to_returned_entities(li, re)
# re = Enum.reduce(items, re, fn {lvarstr, entity}, re ->
# RE.put_entities(re, lvarstr, [entity])
# end)
# convert_to_returned_entities(li, re)
# end
#
# def assign_tks(re, clauses) do
# Enum.reduce(clauses, re, fn %{lvar: lvar, tk: tk}, acc ->
# lvar_str = "#{lvar}"
# RE.assign_lvar_tk(acc, lvar_str, tk)
# end)
# end
# # }}}
#
#end