Skip to content

Instantly share code, notes, and snippets.

@diogobaracho
Last active August 23, 2019 00:39
Show Gist options
  • Save diogobaracho/e9b6b2fc88049a2460f5a72134dde7f8 to your computer and use it in GitHub Desktop.
Save diogobaracho/e9b6b2fc88049a2460f5a72134dde7f8 to your computer and use it in GitHub Desktop.
[elixir] ordenação natural + absinthe graphql
defmodule GraphqlAPI.Middleware.OrderBy do
@moduledoc """
Absinthe middleware that sort results
"""
alias Helpers.NaturalSort
@behaviour Absinthe.Middleware
def call(resolution, _config) do
%{resolution | value: sort_results(resolution)}
end
defp sort_results(%{value: results, arguments: arguments}) do
sort_results_by(results, Map.take(arguments, [:order_by]))
end
# sort by custom order by args
defp sort_results_by(results, %{order_by: args}) do
args
# priority order top to bottom
|> Enum.reverse()
|> Enum.reduce(results, fn
%{column: :task_id, sort: direction}, results ->
sort_by(results, :id, direction)
%{column: :task_title, sort: direction}, results ->
sort_by(results, :name, direction)
%{column: :task_type, sort: direction}, results ->
sort_by(results, :type, direction)
%{column: :discipline_id, sort: direction}, results ->
sort_by(results, [:discipline, :id], direction)
%{column: :discipline_name, sort: direction}, results ->
sort_by(results, [:discipline, :name], direction)
%{column: :task_workflow_id, sort: direction}, results ->
sort_by(results, [:task_workflow, :id], direction)
%{column: :task_workflow_title, sort: direction}, results ->
sort_by(results, [:task_workflow, :name], direction)
%{column: :task_summary_respondents_rate, sort: direction}, results ->
sort_by(results, [:task_summary, :respondents_rate], direction)
%{column: :task_summary_respondents_rate_pretty, sort: direction}, results ->
sort_by(results, [:task_summary, :respondents_rate_pretty], direction)
_, results ->
results
end)
end
# default sort
defp sort_results_by(results, _order_by) do
results
|> NaturalSort.asc_by([:class, :name])
|> NaturalSort.asc_by(:name)
|> NaturalSort.asc_by([:activity, :name])
|> NaturalSort.asc_by([:discipline, :name])
|> NaturalSort.asc_by([:discipline, :discipline_name])
|> NaturalSort.asc_by([:discipline, :sector_name])
end
defp sort_by(data, key, direction) when direction == :asc, do: NaturalSort.asc_by(data, key)
defp sort_by(data, key, direction) when direction == :desc, do: NaturalSort.desc_by(data, key)
end
defmodule GraphqlAPI.Schema.Tasks.Queries do
@moduledoc false
use Absinthe.Schema.Notation
import Absinthe.Resolution.Helpers
import PlurallReportsWeb.Schema.FilterArgs
alias PlurallReportsWeb.Schema.{
Middleware
}
object :tasks_queries do
field :tasks, non_null(list_of(non_null(:tasks))) do
middleware(Middleware.Authorize, :biruleibe)
filter_args()
arg(:order_by, list_of(:task_order_by))
resolve(&GraphqlAPI.Resolvers.Tasks.all/3)
middleware(GraphqlAPI.Middleware.OrderByTasks)
end
end
input_object :task_order_by do
field(:column, :task_fields)
field(:sort, :sort_direction)
end
enum :sort_direction do
value(:asc)
value(:desc)
end
enum :task_fields do
value(:task_id)
value(:task_title)
value(:task_type)
value(:discipline_id)
value(:discipline_name)
value(:task_workflow_id)
value(:task_workflow_title)
value(:task_summary_respondents_rate)
value(:task_summary_respondents_rate_pretty)
end
end
variables = %{
catalogIds: [63_584],
basketIds: [100_638],
orderBy: [
%{column: "TASK_WORKFLOW_TITLE", sort: "DESC"},
%{column: "TASK_TITLE", sort: "ASC"}
]
}
response =
conn
|> login_user(%{somos_id: 3_650_821})
|> get("/api", query: GraphqlQueries.query_tasks(), variables: variables)
defmodule Helpers.NaturalSort do
@moduledoc """
Natural sorting is the sorting of text that does more than rely on the order of individual characters
codes to make the finding of individual strings easier for a human reader.
https://rosettacode.org/wiki/Natural_sorting
"""
@doc """
Sort list values asc
## Examples
iex> Helpers.NaturalSort.sort(["lista 10", "lista 01", "lista 03", "lista 02", "lista 2", "lista 1 e 2"])
["lista 01", "lista 1 e 2", "lista 02", "lista 2", "lista 03", "lista 10"]
iex> Helpers.NaturalSort.sort(["lista 10", "lista 01", "lista 03", "lista 02", "lista 2", "lista 1 e 2"], :asc)
["lista 01", "lista 1 e 2", "lista 02", "lista 2", "lista 03", "lista 10"]
"""
def sort(enumerable) when is_list(enumerable), do: sort(enumerable, :asc)
def sort(enumerable, sort) when is_list(enumerable) and sort == :asc do
Enum.sort_by(enumerable, &compare_value/1, sorter(:asc))
end
@doc """
Sort list values desc
## Examples
iex> Helpers.NaturalSort.sort(["lista 10", "lista 01", "lista 03", "lista 02", "lista 2", "lista 1 e 2"], :desc)
["lista 10", "lista 03", "lista 02", "lista 2", "lista 1 e 2", "lista 01"]
"""
def sort(enumerable, sort) when is_list(enumerable) and sort == :desc do
Enum.sort_by(enumerable, &compare_value/1, sorter(:desc))
end
@doc """
Sort map list by field value in ASC order
## Examples
iex> Helpers.NaturalSort.asc_by([
...> %{task_id: 11, task_title: "Task Título 10", task_workflow: %{id: 1, title: "Workflow Título 11"}},
...> %{task_id: 13, task_title: "Task Título 01", task_workflow: %{id: 3, title: "Workflow Título 13"}},
...> %{task_id: 12, task_title: "Task Título 2", task_workflow: %{id: 2, title: "Workflow Título 2"}}
...> ], :task_title)
[
%{
task_id: 13,
task_title: "Task Título 01",
task_workflow: %{id: 3, title: "Workflow Título 13"}
},
%{
task_id: 12,
task_title: "Task Título 2",
task_workflow: %{id: 2, title: "Workflow Título 2"}
},
%{
task_id: 11,
task_title: "Task Título 10",
task_workflow: %{id: 1, title: "Workflow Título 11"}
}
]
iex> Helpers.NaturalSort.asc_by([
...> %{task_id: 11, task_title: "Task Título 10", task_workflow: %{id: 1, title: "Workflow Título 1"}},
...> %{task_id: 13, task_title: "Task Título 01", task_workflow: %{id: 3, title: "Workflow Título 10"}},
...> %{task_id: 12, task_title: "Task Título 1", task_workflow: %{id: 2, title: "Workflow Título 01"}}
...> ], [:task_workflow, :title])
[
%{
task_id: 11,
task_title: "Task Título 10",
task_workflow: %{id: 1, title: "Workflow Título 1"}
},
%{
task_id: 12,
task_title: "Task Título 1",
task_workflow: %{id: 2, title: "Workflow Título 01"}
},
%{
task_id: 13,
task_title: "Task Título 01",
task_workflow: %{id: 3, title: "Workflow Título 10"}
}
]
iex> Helpers.NaturalSort.asc_by([%{key: ""}], :noexist)
[%{key: ""}]
iex> Helpers.NaturalSort.asc_by([%{key: ""}], [:noexist, :noexistil])
[%{key: ""}]
"""
def asc_by(enumerable, key), do: sort_by(enumerable, key, sorter(:asc))
@doc """
Sort map list by field value in DESC order
## Examples
iex> Helpers.NaturalSort.desc_by([
...> %{task_id: 11, task_title: "Atom desc Título 10", task_workflow: %{id: 1, title: "Workflow Título 10"}},
...> %{task_id: 13, task_title: "Atom desc Título 01", task_workflow: %{id: 3, title: "Workflow Título 01"}},
...> %{task_id: 12, task_title: "Atom desc Título 09", task_workflow: %{id: 2, title: "Workflow Título 09"}}
...> ], :task_title)
[
%{
task_id: 11,
task_title: "Atom desc Título 10",
task_workflow: %{id: 1, title: "Workflow Título 10"}
},
%{
task_id: 12,
task_title: "Atom desc Título 09",
task_workflow: %{id: 2, title: "Workflow Título 09"}
},
%{
task_id: 13,
task_title: "Atom desc Título 01",
task_workflow: %{id: 3, title: "Workflow Título 01"}
}
]
iex> Helpers.NaturalSort.desc_by([
...> %{task_id: 11, task_title: "Task Título 10", task_workflow: %{id: 1, title: "Workflow Título 1"}},
...> %{task_id: 13, task_title: "Task Título 01", task_workflow: %{id: 3, title: "Workflow Título 10"}},
...> %{task_id: 12, task_title: "Task Título 1", task_workflow: %{id: 2, title: "Workflow Título 01"}}
...> ], [:task_workflow, :title])
[
%{
task_id: 13,
task_title: "Task Título 01",
task_workflow: %{id: 3, title: "Workflow Título 10"}
},
%{
task_id: 11,
task_title: "Task Título 10",
task_workflow: %{id: 1, title: "Workflow Título 1"}
},
%{
task_id: 12,
task_title: "Task Título 1",
task_workflow: %{id: 2, title: "Workflow Título 01"}
}
]
iex> Helpers.NaturalSort.desc_by([%{key: ""}], :noexist)
[%{key: ""}]
iex> Helpers.NaturalSort.desc_by([%{key: ""}], [:noexist, :noexistil])
[%{key: ""}]
"""
def desc_by(enumerable, key), do: sort_by(enumerable, key, sorter(:desc))
defp sort_by(enumerable, key, sorter), do: Enum.sort_by(enumerable, &mapper(&1, key), sorter)
defp sorter(sort) when sort == :desc, do: &>=/2
defp sorter(sort) when sort == :asc, do: &<=/2
defp sorter, do: &<=/2
defp mapper(data, keys) when is_map(data) and is_list(keys), do: compare_value(get_in(data, keys))
defp mapper(data, key) when is_map(data), do: compare_value(Map.fetch(data, key))
defp compare_value({:ok, param}), do: compare_value(param)
defp compare_value(param) when is_binary(param) do
param
|> String.downcase()
|> String.replace(~r/\A(a |an |the )/, "")
|> String.split()
|> Enum.map(fn word -> scan_word(word) end)
end
defp compare_value(param), do: param
defp scan_word(word) do
Enum.map(Regex.scan(~r/\d+|\D+/, word), fn [part] ->
case Integer.parse(part) do
{num, ""} -> num
_ -> part
end
end)
end
end
{
"orderBy": [
{
"column": "TASK_TITLE",
"sort": "DESC"
},
{
"column": "TASK_WORKFLOW_TITLE",
"sort": "DESC"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment