Query Builder

View Source

The query builder provides composable, functional query construction. Build queries by chaining functions, then execute with kura_repo_worker:all/2 or kura_repo_worker:one/2.

Basic Queries

%% All users
Q = kura_query:from(my_user),
{ok, Users} = kura_repo_worker:all(my_repo, Q).

%% With conditions
Q = kura_query:from(my_user),
Q1 = kura_query:where(Q, {active, true}),
{ok, Users} = kura_repo_worker:all(my_repo, Q1).

WHERE Conditions

Equality

kura_query:where(Q, {name, ~"Alice"}).
kura_query:where(Q, {name, '=', ~"Alice"}).

Comparison Operators

kura_query:where(Q, {age, '>', 18}).
kura_query:where(Q, {age, '>=', 18}).
kura_query:where(Q, {age, '<', 65}).
kura_query:where(Q, {age, '<=', 65}).
kura_query:where(Q, {age, '!=', 0}).

IN / NOT IN

kura_query:where(Q, {role, in, [~"admin", ~"moderator"]}).
kura_query:where(Q, {status, not_in, [~"banned", ~"deleted"]}).

BETWEEN

kura_query:where(Q, {age, between, {18, 65}}).

LIKE / ILIKE

kura_query:where(Q, {name, like, ~"A%"}).
kura_query:where(Q, {email, ilike, ~"%@example.com"}).

NULL Checks

kura_query:where(Q, {deleted_at, is_nil}).
kura_query:where(Q, {email, is_not_nil}).

Boolean Combinators

%% AND (multiple where calls are implicitly AND'd)
Q1 = kura_query:where(Q, {active, true}),
Q2 = kura_query:where(Q1, {age, '>', 18}).

%% Explicit AND
kura_query:where(Q, {'and', [{active, true}, {age, '>', 18}]}).

%% OR
kura_query:where(Q, {'or', [{role, ~"admin"}, {role, ~"moderator"}]}).

%% NOT
kura_query:where(Q, {'not', {banned, true}}).

Fragments

Raw SQL fragments with ? placeholders:

kura_query:where(Q, {fragment, ~"lower(?) = ?", [~"Email", ~"alice@example.com"]}).

SELECT

%% Specific fields
kura_query:select(Q, [name, email]).

ORDER BY

kura_query:order_by(Q, [{name, asc}]).
kura_query:order_by(Q, [{inserted_at, desc}, {name, asc}]).

LIMIT / OFFSET

Q1 = kura_query:limit(Q, 10),
Q2 = kura_query:offset(Q1, 20).

GROUP BY / HAVING

Q1 = kura_query:group_by(Q, [role]),
Q2 = kura_query:having(Q1, {count, '>', 5}).

DISTINCT

%% Simple DISTINCT
kura_query:distinct(Q).

%% DISTINCT ON (PostgreSQL-specific)
kura_query:distinct(Q, [email]).

LOCK

kura_query:lock(Q, ~"FOR UPDATE").
kura_query:lock(Q, ~"FOR SHARE").

Schema Prefix

kura_query:prefix(Q, ~"my_schema").

JOINs

Q = kura_query:from(my_user),
Q1 = kura_query:join(Q, inner, posts, {user_id, id}).

%% With alias
Q1 = kura_query:join(Q, left, posts, {user_id, id}, p).

Join types: inner, left, right, full.

Aggregates

%% COUNT(*)
Q1 = kura_query:count(Q),
{ok, [#{count := Count}]} = kura_repo_worker:all(my_repo, Q1).

%% COUNT(field)
kura_query:count(Q, email).

%% SUM / AVG / MIN / MAX
kura_query:sum(Q, amount).
kura_query:avg(Q, score).
kura_query:min(Q, price).
kura_query:max(Q, price).

Preloading (in queries)

See the Associations guide for preloading details.

Q = kura_query:from(my_post),
Q1 = kura_query:preload(Q, [author]),
{ok, Posts} = kura_repo_worker:all(my_repo, Q1).