EctoTurbo

A rich Ecto component for searching, sorting, and paginating queries.

EctoTurbo is a consolidated fork of turbo_ecto, with production-tested bug fixes, security improvements, and modern Elixir compatibility.

Installation

Add ecto_turbo to your list of dependencies in mix.exs:

def deps do
  [
    {:ecto_turbo, "~> 0.1.0"}
  ]
end

Configuration

# config/config.exs
config :ecto_turbo, EctoTurbo,
  repo: MyApp.Repo,
  per_page: 10,
  entry_name: "data",
  paginate_name: "pagination"

Usage

Basic Query with Pagination

# Returns %{data: [...], pagination: %{...}}
EctoTurbo.turbo(Post, %{"page" => 1, "per_page" => 20})

# Pagination payload includes:
# %{
#   current_page: 1,
#   current_pages: [1, 2, 3, "...", 20],
#   per_page: 20,
#   total_count: 100,
#   total_pages: 5,
#   next_page: 2,
#   prev_page: nil
# }

Search

Use the q or filter parameter to search:

# Exact match
EctoTurbo.turbo(Post, %{"q" => %{"price_eq" => 100}})

# Like search
EctoTurbo.turbo(Post, %{"q" => %{"name_like" => "elixir"}})

# Association search
EctoTurbo.turbo(Post, %{"q" => %{"category_name_like" => "tech"}})

# Combined OR search
EctoTurbo.turbo(Post, %{"q" => %{"name_or_body_like" => "elixir"}})

Supported Search Types

Type SQL Example
eqcol = 'value'price_eq
not_eqcol != 'value'price_not_eq
ltcol < valueprice_lt
lteqcol <= valueprice_lteq
gtcol > valueprice_gt
gteqcol >= valueprice_gteq
likecol LIKE '%value%'name_like
not_likecol NOT LIKE '%value%'name_not_like
ilikecol ILIKE '%value%'name_ilike
not_ilikecol NOT ILIKE '%value%'name_not_ilike
incol IN (values)price_in
not_incol NOT IN (values)price_not_in
start_withcol ILIKE 'value%'name_start_with
not_start_withcol NOT ILIKE 'value%'name_not_start_with
end_withcol ILIKE '%value'name_end_with
not_end_withcol NOT ILIKE '%value'name_not_end_with
betweenbegin < col AND col < endprice_between
is_truecol = trueavailable_is_true
is_falsecol = falseavailable_is_false
is_nullcol IS NULLname_is_null
is_presentcol IS NOT NULL AND col != ''name_is_present
is_blankcol IS NULL OR col = ''name_is_blank

Sort

# Single sort
EctoTurbo.turbo(Post, %{"s" => "updated_at+desc"})

# Or using "sort" key
EctoTurbo.turbo(Post, %{"sort" => "price+asc"})

# Multiple sorts
EctoTurbo.turbo(Post, %{"s" => ["updated_at+desc", "name+asc"]})

Options

EctoTurbo.turbo(Post, params,
  repo: MyApp.Repo,          # Override configured repo
  per_page: 25,               # Override per_page
  entry_name: "entries",       # Custom key for results
  paginate_name: "meta",       # Custom key for pagination
  with_paginate: false,        # Return flat list without pagination
  prefix: "my_schema",         # Ecto query prefix
  callback: fn q -> q end      # Transform query before execution
)

Query Builder Only

Use turboq/2 to build the query without executing it:

query = EctoTurbo.turboq(Post, %{"q" => %{"name_like" => "elixir"}, "s" => "name+asc"})
# Returns an Ecto.Query struct

Improvements over turbo_ecto

License

MIT - See LICENSE.md for details.

Originally created by Zven Wang.