EctoTurbo

CI

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, "~> 1.1"}
]
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
# }

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
eq col = 'value' price_eq
not_eq col != 'value' price_not_eq
lt col < value price_lt
lteq col <= value price_lteq
gt col > value price_gt
gteq col >= value price_gteq
like col LIKE '%value%' name_like
not_like col NOT LIKE '%value%' name_not_like
ilike col ILIKE '%value%' name_ilike
not_ilike col NOT ILIKE '%value%' name_not_ilike
in col IN (values) price_in
not_in col NOT IN (values) price_not_in
start_with col ILIKE 'value%' name_start_with
not_start_with col NOT ILIKE 'value%' name_not_start_with
end_with col ILIKE '%value' name_end_with
not_end_with col NOT ILIKE '%value' name_not_end_with
between begin < col AND col < end price_between
is_true col = true available_is_true
is_false col = false available_is_false
is_null col IS NULL name_is_null
is_present col IS NOT NULL AND col != '' name_is_present
is_blank col 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.