Current section
Files
Jump to
Current section
Files
src/cquill@query@builder.erl
-module(cquill@query@builder).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/cquill/query/builder.gleam").
-export([filter/1, filter_eq_int/2, filter_eq_string/2, filter_eq_bool/2, filter_not_null/1, filter_null/1, filter_gt_int/2, compose/1, and_then/2, identity/0, 'when'/2, when_some/2, with_limit/1, with_offset/1, with_pagination/2, order_asc/1, order_desc/1, with_select/1, with_distinct/0, scope/2, apply_scope/2, apply_scopes/2, merge_conditions/2, merge_order_bys/2, merge_pagination/2, not_deleted/0, published/0, active/0, recent_first/0, oldest_first/0, by_user/1, clone/1, clone_without_conditions/1, clone_without_order/1, clone_without_pagination/1, conditions_equivalent/2, count_conditions_deep/1, get_condition_fields/1]).
-export_type([scope/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type scope(TDO) :: {scope,
binary(),
fun((cquill@query@ast:'query'(TDO)) -> cquill@query@ast:'query'(TDO))}.
-file("src/cquill/query/builder.gleam", 51).
?DOC(
" Create a filter modifier from a condition.\n"
" Returns a function that adds the condition to any query.\n"
"\n"
" ## Example\n"
" ```gleam\n"
" let active_filter = builder.filter(query.eq_bool(\"active\", True))\n"
" user_query |> active_filter\n"
" ```\n"
).
-spec filter(cquill@query@ast:condition()) -> fun((cquill@query@ast:'query'(TDS)) -> cquill@query@ast:'query'(TDS)).
filter(Condition) ->
fun(Q) -> cquill@query:where(Q, Condition) end.
-file("src/cquill/query/builder.gleam", 56).
?DOC(" Create a filter that requires a field to equal a specific integer.\n").
-spec filter_eq_int(binary(), integer()) -> fun((cquill@query@ast:'query'(TDU)) -> cquill@query@ast:'query'(TDU)).
filter_eq_int(Field, Value) ->
filter(cquill@query:eq_int(Field, Value)).
-file("src/cquill/query/builder.gleam", 61).
?DOC(" Create a filter that requires a field to equal a specific string.\n").
-spec filter_eq_string(binary(), binary()) -> fun((cquill@query@ast:'query'(TDW)) -> cquill@query@ast:'query'(TDW)).
filter_eq_string(Field, Value) ->
filter(cquill@query:eq_string(Field, Value)).
-file("src/cquill/query/builder.gleam", 66).
?DOC(" Create a filter that requires a field to equal a specific boolean.\n").
-spec filter_eq_bool(binary(), boolean()) -> fun((cquill@query@ast:'query'(TDY)) -> cquill@query@ast:'query'(TDY)).
filter_eq_bool(Field, Value) ->
filter(cquill@query:eq_bool(Field, Value)).
-file("src/cquill/query/builder.gleam", 71).
?DOC(" Create a filter for non-null values.\n").
-spec filter_not_null(binary()) -> fun((cquill@query@ast:'query'(TEA)) -> cquill@query@ast:'query'(TEA)).
filter_not_null(Field) ->
filter(cquill@query:is_not_null(Field)).
-file("src/cquill/query/builder.gleam", 76).
?DOC(" Create a filter for null values.\n").
-spec filter_null(binary()) -> fun((cquill@query@ast:'query'(TEC)) -> cquill@query@ast:'query'(TEC)).
filter_null(Field) ->
filter(cquill@query:is_null(Field)).
-file("src/cquill/query/builder.gleam", 81).
?DOC(" Create a filter for values greater than a threshold.\n").
-spec filter_gt_int(binary(), integer()) -> fun((cquill@query@ast:'query'(TEE)) -> cquill@query@ast:'query'(TEE)).
filter_gt_int(Field, Value) ->
filter(cquill@query:gt_int(Field, Value)).
-file("src/cquill/query/builder.gleam", 101).
?DOC(
" Compose multiple modifiers into a single modifier.\n"
" Modifiers are applied left to right.\n"
"\n"
" ## Example\n"
" ```gleam\n"
" let active_admins = builder.compose([\n"
" builder.filter(query.eq_bool(\"active\", True)),\n"
" builder.filter(query.eq_string(\"role\", \"admin\")),\n"
" builder.with_limit(10),\n"
" ])\n"
" user_query |> active_admins\n"
" ```\n"
).
-spec compose(
list(fun((cquill@query@ast:'query'(TEG)) -> cquill@query@ast:'query'(TEG)))
) -> fun((cquill@query@ast:'query'(TEG)) -> cquill@query@ast:'query'(TEG)).
compose(Modifiers) ->
fun(Query) ->
gleam@list:fold(Modifiers, Query, fun(Q, Modifier) -> Modifier(Q) end)
end.
-file("src/cquill/query/builder.gleam", 108).
?DOC(" Compose two modifiers into a single modifier.\n").
-spec and_then(
fun((cquill@query@ast:'query'(TEK)) -> cquill@query@ast:'query'(TEK)),
fun((cquill@query@ast:'query'(TEK)) -> cquill@query@ast:'query'(TEK))
) -> fun((cquill@query@ast:'query'(TEK)) -> cquill@query@ast:'query'(TEK)).
and_then(First, Second) ->
fun(Query) -> _pipe = Query,
_pipe@1 = First(_pipe),
Second(_pipe@1) end.
-file("src/cquill/query/builder.gleam", 116).
?DOC(" Create an identity modifier (does nothing).\n").
-spec identity() -> fun((cquill@query@ast:'query'(TEO)) -> cquill@query@ast:'query'(TEO)).
identity() ->
fun(Query) -> Query end.
-file("src/cquill/query/builder.gleam", 122).
?DOC(
" Conditionally apply a modifier.\n"
" If the condition is true, apply the modifier; otherwise, return unchanged.\n"
).
-spec 'when'(
boolean(),
fun((cquill@query@ast:'query'(TEQ)) -> cquill@query@ast:'query'(TEQ))
) -> fun((cquill@query@ast:'query'(TEQ)) -> cquill@query@ast:'query'(TEQ)).
'when'(Condition, Modifier) ->
case Condition of
true ->
Modifier;
false ->
identity()
end.
-file("src/cquill/query/builder.gleam", 131).
?DOC(
" Apply a modifier based on an optional value.\n"
" If the value is Some, apply the modifier factory with that value.\n"
).
-spec when_some(
gleam@option:option(TET),
fun((TET) -> fun((cquill@query@ast:'query'(TEV)) -> cquill@query@ast:'query'(TEV)))
) -> fun((cquill@query@ast:'query'(TEV)) -> cquill@query@ast:'query'(TEV)).
when_some(Maybe_value, Modifier_factory) ->
case Maybe_value of
{some, Value} ->
Modifier_factory(Value);
none ->
identity()
end.
-file("src/cquill/query/builder.gleam", 146).
?DOC(" Create a modifier that sets the limit.\n").
-spec with_limit(integer()) -> fun((cquill@query@ast:'query'(TEY)) -> cquill@query@ast:'query'(TEY)).
with_limit(Count) ->
fun(Q) -> cquill@query:limit(Q, Count) end.
-file("src/cquill/query/builder.gleam", 151).
?DOC(" Create a modifier that sets the offset.\n").
-spec with_offset(integer()) -> fun((cquill@query@ast:'query'(TFA)) -> cquill@query@ast:'query'(TFA)).
with_offset(Count) ->
fun(Q) -> cquill@query:offset(Q, Count) end.
-file("src/cquill/query/builder.gleam", 156).
?DOC(" Create a modifier that sets pagination.\n").
-spec with_pagination(integer(), integer()) -> fun((cquill@query@ast:'query'(TFC)) -> cquill@query@ast:'query'(TFC)).
with_pagination(Page, Per_page) ->
fun(Q) -> cquill@query:paginate(Q, Page, Per_page) end.
-file("src/cquill/query/builder.gleam", 161).
?DOC(" Create a modifier that adds ORDER BY ASC.\n").
-spec order_asc(binary()) -> fun((cquill@query@ast:'query'(TFE)) -> cquill@query@ast:'query'(TFE)).
order_asc(Field) ->
fun(Q) -> cquill@query:order_by_asc(Q, Field) end.
-file("src/cquill/query/builder.gleam", 166).
?DOC(" Create a modifier that adds ORDER BY DESC.\n").
-spec order_desc(binary()) -> fun((cquill@query@ast:'query'(TFG)) -> cquill@query@ast:'query'(TFG)).
order_desc(Field) ->
fun(Q) -> cquill@query:order_by_desc(Q, Field) end.
-file("src/cquill/query/builder.gleam", 171).
?DOC(" Create a modifier that sets specific fields to select.\n").
-spec with_select(list(binary())) -> fun((cquill@query@ast:'query'(TFJ)) -> cquill@query@ast:'query'(TFJ)).
with_select(Fields) ->
fun(Q) -> cquill@query:select(Q, Fields) end.
-file("src/cquill/query/builder.gleam", 176).
?DOC(" Create a modifier that makes the query distinct.\n").
-spec with_distinct() -> fun((cquill@query@ast:'query'(TFL)) -> cquill@query@ast:'query'(TFL)).
with_distinct() ->
fun(Q) -> cquill@query:distinct(Q) end.
-file("src/cquill/query/builder.gleam", 191).
?DOC(" Create a named scope.\n").
-spec scope(
binary(),
fun((cquill@query@ast:'query'(TFN)) -> cquill@query@ast:'query'(TFN))
) -> scope(TFN).
scope(Name, Modifier) ->
{scope, Name, Modifier}.
-file("src/cquill/query/builder.gleam", 196).
?DOC(" Apply a scope to a query.\n").
-spec apply_scope(cquill@query@ast:'query'(TFQ), scope(TFQ)) -> cquill@query@ast:'query'(TFQ).
apply_scope(Query, Scope) ->
(erlang:element(3, Scope))(Query).
-file("src/cquill/query/builder.gleam", 201).
?DOC(" Apply multiple scopes to a query.\n").
-spec apply_scopes(cquill@query@ast:'query'(TFU), list(scope(TFU))) -> cquill@query@ast:'query'(TFU).
apply_scopes(Query, Scopes) ->
gleam@list:fold(Scopes, Query, fun(Q, S) -> apply_scope(Q, S) end).
-file("src/cquill/query/builder.gleam", 211).
?DOC(
" Merge conditions from source query into target query.\n"
" Preserves target's source, select, and other settings.\n"
).
-spec merge_conditions(
cquill@query@ast:'query'(TFZ),
cquill@query@ast:'query'(any())
) -> cquill@query@ast:'query'(TFZ).
merge_conditions(Target, Source) ->
Source_conditions = cquill@query:get_conditions(Source),
gleam@list:fold(
Source_conditions,
Target,
fun(Q, Cond) -> cquill@query:where(Q, Cond) end
).
-file("src/cquill/query/builder.gleam", 217).
?DOC(" Merge order_bys from source query into target query.\n").
-spec merge_order_bys(
cquill@query@ast:'query'(TGE),
cquill@query@ast:'query'(any())
) -> cquill@query@ast:'query'(TGE).
merge_order_bys(Target, Source) ->
{'query', _, _, _, Target_orders, _, _, _, _, _, _} = Target,
Source_orders = cquill@query:get_order_bys(Source),
{'query',
erlang:element(2, Target),
erlang:element(3, Target),
erlang:element(4, Target),
lists:append(Target_orders, Source_orders),
erlang:element(6, Target),
erlang:element(7, Target),
erlang:element(8, Target),
erlang:element(9, Target),
erlang:element(10, Target),
erlang:element(11, Target)}.
-file("src/cquill/query/builder.gleam", 225).
?DOC(
" Merge pagination (limit/offset) from source to target.\n"
" Only applies if target doesn't already have pagination.\n"
).
-spec merge_pagination(
cquill@query@ast:'query'(TGJ),
cquill@query@ast:'query'(any())
) -> cquill@query@ast:'query'(TGJ).
merge_pagination(Target, Source) ->
Target_limit = cquill@query:get_limit(Target),
Target_offset = cquill@query:get_offset(Target),
Source_limit = cquill@query:get_limit(Source),
Source_offset = cquill@query:get_offset(Source),
New_limit = case Target_limit of
{some, _} ->
Target_limit;
none ->
Source_limit
end,
New_offset = case Target_offset of
{some, _} ->
Target_offset;
none ->
Source_offset
end,
{'query',
erlang:element(2, Target),
erlang:element(3, Target),
erlang:element(4, Target),
erlang:element(5, Target),
New_limit,
New_offset,
erlang:element(8, Target),
erlang:element(9, Target),
erlang:element(10, Target),
erlang:element(11, Target)}.
-file("src/cquill/query/builder.gleam", 250).
?DOC(
" Create a \"soft delete\" filter that excludes soft-deleted records.\n"
" Assumes a `deleted_at` field that is NULL for non-deleted records.\n"
).
-spec not_deleted() -> fun((cquill@query@ast:'query'(TGO)) -> cquill@query@ast:'query'(TGO)).
not_deleted() ->
filter_null(<<"deleted_at"/utf8>>).
-file("src/cquill/query/builder.gleam", 256).
?DOC(
" Create a \"published\" filter for content models.\n"
" Assumes a `published` boolean field.\n"
).
-spec published() -> fun((cquill@query@ast:'query'(TGQ)) -> cquill@query@ast:'query'(TGQ)).
published() ->
filter_eq_bool(<<"published"/utf8>>, true).
-file("src/cquill/query/builder.gleam", 262).
?DOC(
" Create an \"active\" filter.\n"
" Assumes an `active` boolean field.\n"
).
-spec active() -> fun((cquill@query@ast:'query'(TGS)) -> cquill@query@ast:'query'(TGS)).
active() ->
filter_eq_bool(<<"active"/utf8>>, true).
-file("src/cquill/query/builder.gleam", 268).
?DOC(
" Create a \"recent first\" ordering modifier.\n"
" Assumes a `created_at` timestamp field.\n"
).
-spec recent_first() -> fun((cquill@query@ast:'query'(TGU)) -> cquill@query@ast:'query'(TGU)).
recent_first() ->
order_desc(<<"created_at"/utf8>>).
-file("src/cquill/query/builder.gleam", 274).
?DOC(
" Create an \"oldest first\" ordering modifier.\n"
" Assumes a `created_at` timestamp field.\n"
).
-spec oldest_first() -> fun((cquill@query@ast:'query'(TGW)) -> cquill@query@ast:'query'(TGW)).
oldest_first() ->
order_asc(<<"created_at"/utf8>>).
-file("src/cquill/query/builder.gleam", 280).
?DOC(
" Create a modifier for \"by user\" filtering.\n"
" Common pattern for multi-tenant queries.\n"
).
-spec by_user(integer()) -> fun((cquill@query@ast:'query'(TGY)) -> cquill@query@ast:'query'(TGY)).
by_user(User_id) ->
filter_eq_int(<<"user_id"/utf8>>, User_id).
-file("src/cquill/query/builder.gleam", 289).
?DOC(" Clone a query, optionally resetting certain parts.\n").
-spec clone(cquill@query@ast:'query'(THA)) -> cquill@query@ast:'query'(THA).
clone(Q) ->
Q.
-file("src/cquill/query/builder.gleam", 294).
?DOC(" Clone a query but clear all WHERE conditions.\n").
-spec clone_without_conditions(cquill@query@ast:'query'(THD)) -> cquill@query@ast:'query'(THD).
clone_without_conditions(Q) ->
cquill@query:where_clear(Q).
-file("src/cquill/query/builder.gleam", 299).
?DOC(" Clone a query but clear all ORDER BY.\n").
-spec clone_without_order(cquill@query@ast:'query'(THG)) -> cquill@query@ast:'query'(THG).
clone_without_order(Q) ->
cquill@query:order_by_clear(Q).
-file("src/cquill/query/builder.gleam", 304).
?DOC(" Clone a query but clear pagination.\n").
-spec clone_without_pagination(cquill@query@ast:'query'(THJ)) -> cquill@query@ast:'query'(THJ).
clone_without_pagination(Q) ->
cquill@query:no_pagination(Q).
-file("src/cquill/query/builder.gleam", 313).
?DOC(" Check if two queries have equivalent conditions (order-independent).\n").
-spec conditions_equivalent(
cquill@query@ast:'query'(any()),
cquill@query@ast:'query'(any())
) -> boolean().
conditions_equivalent(Query_a, Query_b) ->
Conds_a = cquill@query:get_conditions(Query_a),
Conds_b = cquill@query:get_conditions(Query_b),
((erlang:length(Conds_a) =:= erlang:length(Conds_b)) andalso gleam@list:all(
Conds_a,
fun(C) -> gleam@list:contains(Conds_b, C) end
))
andalso gleam@list:all(
Conds_b,
fun(C@1) -> gleam@list:contains(Conds_a, C@1) end
).
-file("src/cquill/query/builder.gleam", 328).
-spec count_condition_nodes(cquill@query@ast:condition()) -> integer().
count_condition_nodes(Condition) ->
case Condition of
{'and', Conditions} ->
1 + gleam@list:fold(
Conditions,
0,
fun(Acc, C) -> Acc + count_condition_nodes(C) end
);
{'or', Conditions@1} ->
1 + gleam@list:fold(
Conditions@1,
0,
fun(Acc@1, C@1) -> Acc@1 + count_condition_nodes(C@1) end
);
{'not', Inner} ->
1 + count_condition_nodes(Inner);
_ ->
1
end.
-file("src/cquill/query/builder.gleam", 323).
?DOC(" Count total conditions including nested AND/OR.\n").
-spec count_conditions_deep(cquill@query@ast:'query'(any())) -> integer().
count_conditions_deep(Q) ->
Conditions = cquill@query:get_conditions(Q),
gleam@list:fold(
Conditions,
0,
fun(Acc, C) -> Acc + count_condition_nodes(C) end
).
-file("src/cquill/query/builder.gleam", 349).
-spec extract_fields_from_condition(cquill@query@ast:condition()) -> list(binary()).
extract_fields_from_condition(Condition) ->
case Condition of
{eq, Field, _} ->
[Field];
{not_eq, Field@1, _} ->
[Field@1];
{gt, Field@2, _} ->
[Field@2];
{gte, Field@3, _} ->
[Field@3];
{lt, Field@4, _} ->
[Field@4];
{lte, Field@5, _} ->
[Field@5];
{in, Field@6, _} ->
[Field@6];
{not_in, Field@7, _} ->
[Field@7];
{like, Field@8, _} ->
[Field@8];
{not_like, Field@9, _} ->
[Field@9];
{i_like, Field@10, _} ->
[Field@10];
{not_i_like, Field@11, _} ->
[Field@11];
{is_null, Field@12} ->
[Field@12];
{is_not_null, Field@13} ->
[Field@13];
{between, Field@14, _, _} ->
[Field@14];
{'and', Conditions} ->
gleam@list:flat_map(Conditions, fun extract_fields_from_condition/1);
{'or', Conditions@1} ->
gleam@list:flat_map(
Conditions@1,
fun extract_fields_from_condition/1
);
{'not', Inner} ->
extract_fields_from_condition(Inner);
{raw, _, _} ->
[]
end.
-file("src/cquill/query/builder.gleam", 342).
?DOC(" Extract all field names referenced in conditions.\n").
-spec get_condition_fields(cquill@query@ast:'query'(any())) -> list(binary()).
get_condition_fields(Q) ->
Conditions = cquill@query:get_conditions(Q),
_pipe = Conditions,
_pipe@1 = gleam@list:flat_map(_pipe, fun extract_fields_from_condition/1),
gleam@list:unique(_pipe@1).