defmodule Trubo.Ecto do @moduledoc """ Elixir Search compoment, support single table search, sort and paginate. """ alias Trubo.Ecto.Hooks.{Paginate, Sort, Search} alias Trubo.Ecto.Config, as: TConfig @doc """ Returns a result and pageinate info. ## Example iex> params = %{"q" => %{"name_like" => "q"}, "s" => "updated_at+asc", "per_page" => 5, "page" => 1} iex> Trubo.Ecto.trubo(Trubo.Ecto.Product, params) %{ paginate: %{current_page: 1, per_page: 5, next_page: nil, prev_page: nil, total_count: 0, total_pages: 0}, datas: [] } """ @spec trubo(Ecto.Query.t(), Map.t(), Keyword.t()) :: Map.t() def trubo(queryable, params, opts \\ []) do queryable = truboq(queryable, params) %{ datas: handle_query(queryable, opts), paginate: get_paginate(queryable, params, opts) } end @doc """ Returns processed queryable. ## Example iex> params = %{"q" => %{"name_like" => "name", "body_like" => "body"}, "s" => "updated_at+asc", "per_page" => 5, "page" => 1} iex> Trubo.Ecto.truboq(Trubo.Ecto.Product, params) #Ecto.Query """ @spec truboq(Ecto.Query.t(), Map.t()) :: Ecto.Query.t() def truboq(queryable, params) do [Search, Sort, Paginate] |> Enum.reduce(queryable, &run_hook(&1, &2, params)) end @doc """ Returns searching result. ## Example iex> params = %{"q" => %{"name_like" => "q"}} iex> Trubo.Ecto.search(Trubo.Ecto.Product, params) [] """ @spec search(Ecto.Query.t(), Map.t(), Keyword.t()) :: any def search(queryable, params, opts \\ []) do queryable |> searchq(params) |> handle_query(opts) end @doc """ Returns searching queryable. ## Example iex> Trubo.Ecto.searchq(Trubo.Ecto.Product, %{"q" => %{"name_like" => "q"}}) #Ecto.Query """ @spec search(Ecto.Query.t(), Map.t()) :: Ecto.Query.t() def searchq(queryable, params), do: Search.run(queryable, params) @doc """ Returns sorting result. ## Example iex> params = %{"s" => "updated_at+desc"} iex> Trubo.Ecto.sort(Trubo.Ecto.Product, params) [] """ @spec sort(Ecto.Query.t(), Map.t(), Keyword.t()) :: any def sort(queryable, params, opts \\ []) do queryable |> sortq(params) |> handle_query(opts) end @doc """ Returns sorting queryable. ## Example iex> params = %{"s" => "updated_at+desc"} iex> Trubo.Ecto.sortq(Trubo.Ecto.Product, params) #Ecto.Query """ @spec sortq(Ecto.Query.t(), Map.t()) :: Ecto.Query.t() def sortq(queryable, params), do: Sort.run(queryable, params) @doc """ Returns Paginate result. ## Example iex> params = %{"per_page" => 5, "page" => 2} iex> Trubo.Ecto.paginate(Trubo.Ecto.Product, params) %{dates: [], paginate: %{current_page: 2, next_page: nil, per_page: 5, prev_page: nil, total_count: 0, total_pages: 0}} """ @spec paginate(Ecto.Query.t(), Map.t(), Keyword.t()) :: Map.t() def paginate(queryable, params, opts \\ []) do queryable = paginateq(queryable, params) %{ dates: handle_query(queryable, opts), paginate: get_paginate(queryable, params, opts) } end @doc """ Returns Paginate queryable. ## Example iex> params = %{"per_page" => 5, "page" => 2} iex> Trubo.Ecto.paginateq(Trubo.Ecto.Product, params) #Ecto.Query """ @spec paginateq(Ecto.Query.t(), Map.t()) :: Ecto.Query.t() def paginateq(queryable, params), do: Paginate.run(queryable, params) @doc """ Gets paginate info. ## Example iex> params = %{"per_page" => 5, "page" => 2} iex> Trubo.Ecto.get_paginate(Trubo.Ecto.Product, params) %{per_page: 5, current_page: 2, next_page: nil, prev_page: nil, total_count: 0, total_pages: 0} """ # @spec get_paginate(Ecto.Query.t(), Map.t(), Keyword.t()) :: Map.t() def get_paginate(queryable, params, opts \\ []) do build_opts = Keyword.put_new(opts, :repo, TConfig.repo) Paginate.get_paginate(queryable, params, build_opts) end # Invoke hooks run method. defp run_hook(hook, queryable, params), do: apply(hook, :run, [queryable, params]) defp handle_query(queryable, opts) do build_opts = Keyword.put_new(opts, :repo, TConfig.repo) case Keyword.get(build_opts, :repo) do nil -> raise "Expected key `repo` in `opts`, got #{inspect(opts)}" repo -> apply(repo, :all, [queryable]) end end defmacro __using__(opts) do quote do alias Trubo.Ecto.Config, as: TConfig @doc """ Returns result ## Example iex> params = %{"q" => %{"name_like" => "q"}, "s" => "updated_at+asc", "per_page" => 5, "page" => 1} iex> Trubo.Ecto.trubo(Trubo.Ecto.Product, params) %{ paginate: %{current_page: 1, per_page: 5, next_page: nil, prev_page: nil, total_count: 0, total_pages: 0}, datas: [] } """ @spec trubo(Ecto.Query.t(), map(), Keyword.t()) :: any() def trubo(queryable, params, opts \\ []) do Trubo.Ecto.trubo(queryable, params, uniq_merge(opts, defaults())) end @doc """ Returns queryable ## Example iex> Trubo.Ecto.searchq(Trubo.Ecto.Product, %{"q" => %{"name_like" => "q"}}) #Ecto.Query """ @spec truboq(Ecto.Query.t(), map(), Keyword.t()) :: Ecto.Query.t() def truboq(queryable, params, opts \\ []) do Trubo.Ecto.truboq(queryable, params) end @doc """ Returns search result. ## Example iex> params = %{"q" => %{"name_like" => "q"} iex> Trubo.Ecto.search(Trubo.Ecto.Product, params}) [] """ @spec search(Ecto.Query.t(), map(), Keyword.t()) :: any() def search(queryable, params, opts \\ []) do Trubo.Ecto.search(queryable, params, uniq_merge(opts, defaults())) end @doc """ Returns searching queryable. ## Example iex> Trubo.Ecto.searchq(Trubo.Ecto.Product, %{"q" => %{"name_like" => "q"}}) #Ecto.Query """ @spec searchq(Ecto.Query.t(), map(), Keyword.t()) :: Ecto.Query.t() def searchq(queryable, params, opts \\ []) do Trubo.Ecto.searchq(queryable, params) end @doc """ Returns sort data result. ## Example iex> params = %{"s" => "updated_at+desc"} iex> Trubo.Ecto.sort(Trubo.Ecto.Product, params) [] """ @spec sort(Ecto.Query.t(), map(), Keyword.t()) :: any() def sort(queryable, params, opts \\ []) do Trubo.Ecto.sort(queryable, params, uniq_merge(opts, defaults())) end @doc """ Returns sort queryable. ## Example iex> params = %{"s" => "updated_at+desc"} iex> Trubo.Ecto.sortq(Trubo.Ecto.Product, params) #Ecto.Query """ @spec sortq(Ecto.Query.t(), map(), Keyword.t()) :: Ecto.Query.t() def sortq(queryable, params, opts \\ []) do Trubo.Ecto.sortq(queryable, params) end @doc """ Return paginate result. ## Example iex> params = %{"per_page" => 5, "page" => 2} iex> Trubo.Ecto.paginate(Trubo.Ecto.Product, params) %{dates: [], paginate: %{current_page: 2, next_page: nil, per_page: 5, prev_page: nil, total_count: 0, total_pages: 0}} """ @spec paginate(Ecto.Query.t(), map(), Keyword.t()) :: any() def paginate(queryable, params, opts \\ []) do Trubo.Ecto.paginate(queryable, params, uniq_merge(opts, defaults())) end @doc """ Return paginate queryable. ## Example iex> params = %{"per_page" => 5, "page" => 2} iex> Trubo.Ecto.paginateq(Trubo.Ecto.Product, params) #Ecto.Query """ @spec paginateq(Ecto.Query.t(), map(), Keyword.t()) :: Ecto.Query.t() def paginateq(queryable, params, opts \\ []) do Trubo.Ecto.paginateq(queryable, params) end defp defaults() do keys = ~w(repo per_page)a Enum.map(keys, &get_defs/1) end defp get_defs(key) do app = Application.get_application(__MODULE__) {key, Keyword.get(unquote(opts), key, apply(TConfig, key, [app]))} end defp uniq_merge(keyword1, keyword2) do keyword2 |> Keyword.merge(keyword1) |> Keyword.new() end end end end