Last active
August 23, 2019 00:39
-
-
Save diogobaracho/e9b6b2fc88049a2460f5a72134dde7f8 to your computer and use it in GitHub Desktop.
[elixir] ordenação natural + absinthe graphql
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"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