# `BoxTurtle.Schema.Sort`
[🔗](https://github.com/twajjo/box_turtle/blob/v0.1.0/lib/schema/sort.ex#L1)

Default sorting for `BoxTurtle.Schema.Query`, and a base for schema-specific sort modules.

A sort specification is a list whose elements are any of:

  * `:column` - ascending order on the column
  * `{direction, :column}` - e.g. `{:desc_nulls_last, :reported_at}`
  * `{direction, [:column, ...]}` - the same direction for several columns

where `direction` is one of `[:asc, :asc_nulls_first, :asc_nulls_last, :desc, :desc_nulls_first, :desc_nulls_last]`
(see `Ecto.Query.order_by/3`). Keyword lists such as `[desc: :inserted_at, asc: :id]` therefore work as-is.

## Custom sorts

Use this module and add `sort_by/2` clauses for named sorts that aren't plain columns. Each clause
handles the element at the head of the list and must pass the rest back to `sort_by/2`. Elements
your clauses don't match fall through to the default behaviour, one element at a time.

For a `MyApp.Blog.Post` schema with `likes` and `shares` fields:

    defmodule MyApp.Blog.Post.Sort do
      use BoxTurtle.Schema.Sort
      import Ecto.Query

      # Most engagement first; the primary key breaks ties so pages are stable.
      def sort_by(query, [:popularity | rest]) do
        query
        |> order_by([p], desc: p.likes + p.shares, asc: p.id)
        |> sort_by(rest)
      end
    end

    defmodule MyApp.Blog.Post.Query do
      use BoxTurtle.Schema.Query,
        repo: MyApp.Repo,
        sorting_module: MyApp.Blog.Post.Sort,
        default_sort: [:popularity]
    end

    MyApp.Blog.Post.Query.list!()                                  # most popular first
    MyApp.Blog.Post.Query.list!(sort: [desc: :inserted_at, asc: :id]) # plain columns still work

`:default_sort` goes through your module too, so it may use your named sorts. Sorts on joined
columns and closed modules for `?sort=-likes,title` params are shown in the repository's
`test/support/examples/schema.ex`.

# `apply_sort`

```elixir
@spec apply_sort(Ecto.Queryable.t(), term()) :: Ecto.Queryable.t()
```

Applies a single sort element (see the module documentation) to the query.

Raises `ArgumentError` for anything else.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
