Skip to content

Commit

Permalink
Merge pull request #392 from woylie/selected-as-variable
Browse files Browse the repository at this point in the history
selected as variable
  • Loading branch information
woylie authored Nov 14, 2023
2 parents a084e65 + 888312f commit f4d44ac
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 89 deletions.
16 changes: 0 additions & 16 deletions lib/flop/adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,4 @@ defmodule Flop.Adapter do
@callback list(queryable, opts) :: [any]

@callback get_field(any, atom, Flop.FieldInfo.t()) :: any

@doc """
Returns a quoted function to be compiled in the schema protocol
implementation.
Takes the schema options (with the nested adapter options) as argument.
This is a hacky workaround that is necessary because
`Ecto.Query.API.selected_as/1` does not accept variables, which means that we
need to compile a function that builds the `order_by` clause for configured
alias fields.
This callback will be removed as soon as a better solution is found or made
possible.
"""
@callback custom_func_builder(opts) :: Macro.t() when opts: keyword
end
103 changes: 41 additions & 62 deletions lib/flop/adapter/ecto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,51 @@ defmodule Flop.Adapter.Ecto do
module ->
struct = struct(module)

Enum.reduce(directions, query, fn expr, acc_query ->
Flop.Schema.custom(struct, {:apply_order_by, acc_query, expr})
Enum.reduce(directions, query, fn {_, field} = expr, acc_query ->
field_info = Flop.Schema.field_info(struct, field)
apply_order_by_field(acc_query, expr, field_info, struct)
end)
end
end

defp apply_order_by_field(
q,
{direction, _},
%FieldInfo{
extra: %{type: :join, binding: binding, field: field}
},
_
) do
order_by(q, [{^binding, r}], [{^direction, field(r, ^field)}])
end

defp apply_order_by_field(
q,
{direction, _},
%FieldInfo{
extra: %{type: :compound, fields: fields}
},
struct
) do
Enum.reduce(fields, q, fn field, acc_query ->
field_info = Flop.Schema.field_info(struct, field)
apply_order_by_field(acc_query, {direction, field}, field_info, struct)
end)
end

defp apply_order_by_field(
q,
{direction, field},
%FieldInfo{extra: %{type: :alias}},
_
) do
order_by(q, [{^direction, selected_as(^field)}])
end

defp apply_order_by_field(q, order_expr, _, _) do
order_by(q, ^order_expr)
end

@impl Flop.Adapter
def apply_limit_offset(query, limit, offset, _opts) do
query
Expand Down Expand Up @@ -460,66 +499,6 @@ defmodule Flop.Adapter.Ecto do
Keyword.merge(default_opts, Keyword.get(opts, :query_opts, []))
end

## Compile time shenanigans

@impl Flop.Adapter
def custom_func_builder(opts) do
adapter_opts = Keyword.fetch!(opts, :adapter_opts)
compound_fields = Map.fetch!(adapter_opts, :compound_fields)
join_fields = Map.fetch!(adapter_opts, :join_fields)
alias_fields = Map.fetch!(adapter_opts, :alias_fields)

compound_field_funcs =
for {name, fields} <- compound_fields do
quote do
def custom(struct, {:apply_order_by, q, {direction, unquote(name)}}) do
Enum.reduce(unquote(fields), q, fn field, acc_q ->
Flop.Schema.custom(
struct,
{:apply_order_by, acc_q, {direction, field}}
)
end)
end
end
end

join_field_funcs =
for {join_field, %{binding: binding, field: field}} <- join_fields do
quote do
def custom(_, {:apply_order_by, q, {direction, unquote(join_field)}}) do
order_by(
q,
[{^unquote(binding), r}],
[{^direction, field(r, unquote(field))}]
)
end
end
end

alias_field_func =
for name <- alias_fields do
quote do
def custom(_, {:apply_order_by, q, {direction, unquote(name)}}) do
order_by(q, [{^direction, selected_as(unquote(name))}])
end
end
end

schema_field_func =
quote do
def custom(_, {:apply_order_by, q, direction}) do
order_by(q, ^direction)
end
end

[
compound_field_funcs,
join_field_funcs,
alias_field_func,
schema_field_func
]
end

## Filter query builder

for op <- [:like_and, :like_or, :ilike_and, :ilike_or] do
Expand Down
7 changes: 0 additions & 7 deletions lib/flop/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -844,10 +844,6 @@ defprotocol Flop.Schema do
@doc since: "0.2.0"
@spec max_limit(any) :: pos_integer | nil
def max_limit(data)

@doc false
@spec custom(any, any) :: any
def custom(data, arg)
end

defimpl Flop.Schema, for: Any do
Expand Down Expand Up @@ -926,7 +922,6 @@ defimpl Flop.Schema, for: Any do

field_info_func = build_field_info_func(adapter, adapter_opts, struct)
get_field_func = build_get_field_func(struct, adapter, adapter_opts)
custom_func = adapter.custom_func_builder(options)

quote do
defimpl Flop.Schema, for: unquote(module) do
Expand Down Expand Up @@ -965,8 +960,6 @@ defimpl Flop.Schema, for: Any do
def sortable(_) do
unquote(sortable_fields)
end

unquote(custom_func)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ defmodule Flop.MixProject do
[
{:credo, "~> 1.7.0", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.4.0", only: [:dev, :test], runtime: false},
{:ecto, "~> 3.10.3"},
{:ecto_sql, "~> 3.9", only: :test},
{:ecto, "~> 3.11"},
{:ecto_sql, "~> 3.11", only: :test},
{:ex_doc, "~> 0.21", only: :dev, runtime: false},
{:ex_machina, "~> 2.4", only: :test},
{:makeup_diff, "~> 0.1.0", only: :dev, runtime: false},
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.2", "764a6e8e7a354f0ba95d58418178d486065ead1f69ad89782817c296d0d746a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "516603d8067b2fd585319e4b13d3674ad4f314a5902ba8130cd97dc902ce6bbd"},
"earmark_parser": {:hex, :earmark_parser, "1.4.38", "b42252eddf63bda05554ba8be93a1262dc0920c721f1aaf989f5de0f73a2e367", [:mix], [], "hexpm", "2cd0907795aaef0c7e8442e376633c5b3bd6edc8dbbdc539b22f095501c1cdb6"},
"ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"},
"ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"},
"ecto": {:hex, :ecto, "3.11.0", "ff8614b4e70a774f9d39af809c426def80852048440e8785d93a6e91f48fec00", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7769dad267ef967310d6e988e92d772659b11b09a0c015f101ce0fff81ce1f81"},
"ecto_sql": {:hex, :ecto_sql, "3.11.0", "c787b24b224942b69c9ff7ab9107f258ecdc68326be04815c6cce2941b6fad1c", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "77aa3677169f55c2714dda7352d563002d180eb33c0dc29cd36d39c0a1a971f5"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.30.9", "d691453495c47434c0f2052b08dd91cc32bc4e1a218f86884563448ee2502dd2", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "d7aaaf21e95dc5cddabf89063327e96867d00013963eadf2c6ad135506a8bc10"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
Expand Down

0 comments on commit f4d44ac

Please sign in to comment.