Rumamge.Ecto v1.3.0-rc.0 Rummage.Ecto.Hooks.Sort View Source

Rummage.Ecto.Hooks.Sort is the default sort hook that comes with Rummage.Ecto.

This module provides a operations that can add sorting functionality to a pipeline of Ecto queries. This module works by taking the field that should be used to order_by, order which can be asc or desc and assoc, which is a keyword list of assocations associated with those fields.

NOTE: This module doesn’t return a list of entries, but a Ecto.Query.t. This module uses Rummage.Ecto.Hook.


ABOUT:

Arguments:

This Hook expects a queryable (an Ecto.Queryable) and sort_params (a Map). The map should be in the format: %{field: :field_name, assoc: [], order: :asc}

Details:

  • field: The field name (atom) to sorted by.
  • assoc: List of associations in the sort.
  • order: Specifies the type of order asc or desc.
  • ci : Case Insensitivity. Defaults to false

For example, if we want to sort products with descending price, we would do the following:

Rummage.Ecto.Hooks.Sort.run(Product, %{field: :price,
  assoc: [], order: :desc})

Assoications:

Assocaitions can be given to this module’s run function as a key corresponding to params associated with a field. For example, if we want to sort products that belong to a category by ascending category_name, we would do the following:

params = %{field: :category_name, assoc: [inner: :category],
  order: :asc}

Rummage.Ecto.Hooks.Sort.run(Product, params)

The above operation will return an Ecto.Query.t struct which represents a query equivalent to:

from p in Product
|> join(:inner, :category)
|> order_by([p, c], {asc, c.category_name})

ASSUMPTIONS/NOTES:

  • This Hook has the default order of :asc.
  • This Hook has the default assoc of [].
  • This Hook assumes that the field passed is a field on the Ecto.Schema that corresponds to the last association in the assoc list or the Ecto.Schema that corresponds to the from in queryable, if assoc is an empty list.

NOTE: It is adviced to not use multiple associated sorts in one operation as assoc still has some minor bugs when used with multiple sorts. If you need to use two sorts with associations, I would pipe the call to another sort operation:

Sort.run(queryable, params1}
|> Sort.run(%{field2: params2}

USAGE:

For a regular sort:

This returns a queryable which upon running will give a list of Parent(s) sorted by ascending field_1

alias Rummage.Ecto.Hooks.Sort

sorted_queryable = Sort.run(Parent, %{assoc: [], field: :name, order: :asc}})

For a case-insensitive sort:

This returns a queryable which upon running will give a list of Parent(s) sorted by ascending case insensitive field_1.

Keep in mind that case_insensitive can only be called for text fields

alias Rummage.Ecto.Hooks.Sort

sorted_queryable = Sort.run(Parent, %{assoc: [], field: :name, order: :asc, ci: true}})

This module can be overridden with a custom module while using Rummage.Ecto in Ecto struct module.

In the Ecto module:

Rummage.Ecto.rummage(queryable, rummage, sort: CustomHook)

OR

Globally for all models in config.exs:

config :rummage_ecto,
  Rummage.Ecto,
 .sort: CustomHook

The CustomHook must use Rummage.Ecto.Hook. For examples of CustomHook, check out some custom_hooks that are shipped with Rummage.Ecto: Rummage.Ecto.CustomHooks.SimpleSearch, Rummage.Ecto.CustomHooks.SimpleSort, Rummage.Ecto.CustomHooks.SimplePaginate

Link to this section Summary

Functions

Callback implementation for Rummage.Ecto.Hook.format_params/2

This is the callback implementation of Rummage.Ecto.Hook.run/2

Link to this section Functions

Link to this function format_params(queryable, params, opts) View Source
format_params(Ecto.Query.t(), map(), keyword()) :: map()
format_params(Ecto.Query.t(), map(), keyword()) :: map()

Callback implementation for Rummage.Ecto.Hook.format_params/2.

This function ensures that params for each field have keys assoc, `order1 which are essential for running this hook module. ## Examples iex> alias Rummage.Ecto.Hooks.Sort iex> Sort.format_params(Parent, %{}, []) %{assoc: [], order: :asc}

Link to this function run(queryable, params) View Source
run(Ecto.Query.t(), map()) :: Ecto.Query.t()
run(Ecto.Query.t(), map()) :: Ecto.Query.t()

This is the callback implementation of Rummage.Ecto.Hook.run/2.

Builds a sort Ecto.Query.t on top of the given Ecto.Queryable variable using given params.

Besides an Ecto.Query.t an Ecto.Schema module can also be passed as it implements Ecto.Queryable

Params is a Map which is expected to have the keys field, order, assoc.

This funciton expects a field atom, order which can be asc or desc, ci which is a boolean indicating the case-insensitivity and assoc which is a list of associations with their join types.

Examples

When an empty map is passed as params:

iex> alias Rummage.Ecto.Hooks.Sort
iex> Sort.run(Parent, %{})
** (RuntimeError) Error in params, No values given for keys: field, order, assoc

When a non-empty map is passed as params, but with a missing key:

iex> alias Rummage.Ecto.Hooks.Sort
iex> Sort.run(Parent, %{field: :name})
** (RuntimeError) Error in params, No values given for keys: order, assoc

When a valid map of params is passed with an Ecto.Schema module:

iex> alias Rummage.Ecto.Hooks.Sort
iex> Sort.run(Rummage.Ecto.Product, %{field: :name, assoc: [], order: :asc})
#Ecto.Query<from p in subquery(from p in Rummage.Ecto.Product), order_by: [asc: p.name]>

When the queryable passed is an Ecto.Query variable:

iex> alias Rummage.Ecto.Hooks.Sort
iex> import Ecto.Query
iex> queryable = from u in "products"
#Ecto.Query<from p in "products">
iex> Sort.run(queryable, %{field: :name, assoc: [], order: :asc})
#Ecto.Query<from p in subquery(from p in "products"), order_by: [asc: p.name]>

When the queryable passed is an Ecto.Query variable, with desc order:

iex> alias Rummage.Ecto.Hooks.Sort
iex> import Ecto.Query
iex> queryable = from u in "products"
#Ecto.Query<from p in "products">
iex> Sort.run(queryable, %{field: :name, assoc: [], order: :desc})
#Ecto.Query<from p in subquery(from p in "products"), order_by: [desc: p.name]>

When the queryable passed is an Ecto.Query variable, with ci true:

iex> alias Rummage.Ecto.Hooks.Sort
iex> import Ecto.Query
iex> queryable = from u in "products"
#Ecto.Query<from p in "products">
iex> Sort.run(queryable, %{field: :name, assoc: [], order: :asc, ci: true})
#Ecto.Query<from p in subquery(from p in "products"), order_by: [asc: fragment("lower(?)", p.name)]>

When the queryable passed is an Ecto.Query variable, with associations:

iex> alias Rummage.Ecto.Hooks.Sort
iex> import Ecto.Query
iex> queryable = from u in "products"
#Ecto.Query<from p in "products">
iex> Sort.run(queryable, %{field: :name, assoc: [inner: :category, left: :category], order: :asc})
#Ecto.Query<from p in subquery(from p in "products"), join: c0 in assoc(p, :category), left_join: c1 in assoc(c0, :category), order_by: [asc: c1.name]>

When the queryable passed is an Ecto.Schema module with associations, desc order and ci true:

iex> alias Rummage.Ecto.Hooks.Sort
iex> queryable = Rummage.Ecto.Product
Rummage.Ecto.Product
iex> Sort.run(queryable, %{field: :name, assoc: [inner: :category], order: :desc, ci: true})
#Ecto.Query<from p in subquery(from p in Rummage.Ecto.Product), join: c in assoc(p, :category), order_by: [desc: fragment("lower(?)", c.name)]>