-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(SGO) :: {scope, binary(), fun((cquill@query@ast:'query'(SGO)) -> cquill@query@ast:'query'(SGO))}. -file("src/cquill/query/builder.gleam", 56). ?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'(SGS)) -> cquill@query@ast:'query'(SGS)). filter(Condition) -> fun(Q) -> cquill@query:where(Q, Condition) end. -file("src/cquill/query/builder.gleam", 61). ?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'(SGU)) -> cquill@query@ast:'query'(SGU)). filter_eq_int(Field, Value) -> filter(cquill@query:eq_int(Field, Value)). -file("src/cquill/query/builder.gleam", 66). ?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'(SGW)) -> cquill@query@ast:'query'(SGW)). filter_eq_string(Field, Value) -> filter(cquill@query:eq_string(Field, Value)). -file("src/cquill/query/builder.gleam", 71). ?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'(SGY)) -> cquill@query@ast:'query'(SGY)). filter_eq_bool(Field, Value) -> filter(cquill@query:eq_bool(Field, Value)). -file("src/cquill/query/builder.gleam", 76). ?DOC(" Create a filter for non-null values.\n"). -spec filter_not_null(binary()) -> fun((cquill@query@ast:'query'(SHA)) -> cquill@query@ast:'query'(SHA)). filter_not_null(Field) -> filter(cquill@query:is_not_null(Field)). -file("src/cquill/query/builder.gleam", 81). ?DOC(" Create a filter for null values.\n"). -spec filter_null(binary()) -> fun((cquill@query@ast:'query'(SHC)) -> cquill@query@ast:'query'(SHC)). filter_null(Field) -> filter(cquill@query:is_null(Field)). -file("src/cquill/query/builder.gleam", 86). ?DOC(" Create a filter for values greater than a threshold.\n"). -spec filter_gt_int(binary(), integer()) -> fun((cquill@query@ast:'query'(SHE)) -> cquill@query@ast:'query'(SHE)). filter_gt_int(Field, Value) -> filter(cquill@query:gt_int(Field, Value)). -file("src/cquill/query/builder.gleam", 106). ?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'(SHG)) -> cquill@query@ast:'query'(SHG))) ) -> fun((cquill@query@ast:'query'(SHG)) -> cquill@query@ast:'query'(SHG)). compose(Modifiers) -> fun(Query) -> gleam@list:fold(Modifiers, Query, fun(Q, Modifier) -> Modifier(Q) end) end. -file("src/cquill/query/builder.gleam", 113). ?DOC(" Compose two modifiers into a single modifier.\n"). -spec and_then( fun((cquill@query@ast:'query'(SHK)) -> cquill@query@ast:'query'(SHK)), fun((cquill@query@ast:'query'(SHK)) -> cquill@query@ast:'query'(SHK)) ) -> fun((cquill@query@ast:'query'(SHK)) -> cquill@query@ast:'query'(SHK)). and_then(First, Second) -> fun(Query) -> _pipe = Query, _pipe@1 = First(_pipe), Second(_pipe@1) end. -file("src/cquill/query/builder.gleam", 121). ?DOC(" Create an identity modifier (does nothing).\n"). -spec identity() -> fun((cquill@query@ast:'query'(SHO)) -> cquill@query@ast:'query'(SHO)). identity() -> fun(Query) -> Query end. -file("src/cquill/query/builder.gleam", 127). ?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'(SHQ)) -> cquill@query@ast:'query'(SHQ)) ) -> fun((cquill@query@ast:'query'(SHQ)) -> cquill@query@ast:'query'(SHQ)). 'when'(Condition, Modifier) -> case Condition of true -> Modifier; false -> identity() end. -file("src/cquill/query/builder.gleam", 136). ?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(SHT), fun((SHT) -> fun((cquill@query@ast:'query'(SHV)) -> cquill@query@ast:'query'(SHV))) ) -> fun((cquill@query@ast:'query'(SHV)) -> cquill@query@ast:'query'(SHV)). when_some(Maybe_value, Modifier_factory) -> case Maybe_value of {some, Value} -> Modifier_factory(Value); none -> identity() end. -file("src/cquill/query/builder.gleam", 151). ?DOC(" Create a modifier that sets the limit.\n"). -spec with_limit(integer()) -> fun((cquill@query@ast:'query'(SHY)) -> cquill@query@ast:'query'(SHY)). with_limit(Count) -> fun(Q) -> cquill@query:limit(Q, Count) end. -file("src/cquill/query/builder.gleam", 156). ?DOC(" Create a modifier that sets the offset.\n"). -spec with_offset(integer()) -> fun((cquill@query@ast:'query'(SIA)) -> cquill@query@ast:'query'(SIA)). with_offset(Count) -> fun(Q) -> cquill@query:offset(Q, Count) end. -file("src/cquill/query/builder.gleam", 161). ?DOC(" Create a modifier that sets pagination.\n"). -spec with_pagination(integer(), integer()) -> fun((cquill@query@ast:'query'(SIC)) -> cquill@query@ast:'query'(SIC)). with_pagination(Page, Per_page) -> fun(Q) -> cquill@query:paginate(Q, Page, Per_page) end. -file("src/cquill/query/builder.gleam", 166). ?DOC(" Create a modifier that adds ORDER BY ASC.\n"). -spec order_asc(binary()) -> fun((cquill@query@ast:'query'(SIE)) -> cquill@query@ast:'query'(SIE)). order_asc(Field) -> fun(Q) -> cquill@query:order_by_asc(Q, Field) end. -file("src/cquill/query/builder.gleam", 171). ?DOC(" Create a modifier that adds ORDER BY DESC.\n"). -spec order_desc(binary()) -> fun((cquill@query@ast:'query'(SIG)) -> cquill@query@ast:'query'(SIG)). order_desc(Field) -> fun(Q) -> cquill@query:order_by_desc(Q, Field) end. -file("src/cquill/query/builder.gleam", 176). ?DOC(" Create a modifier that sets specific fields to select.\n"). -spec with_select(list(binary())) -> fun((cquill@query@ast:'query'(SIJ)) -> cquill@query@ast:'query'(SIJ)). with_select(Fields) -> fun(Q) -> cquill@query:select(Q, Fields) end. -file("src/cquill/query/builder.gleam", 181). ?DOC(" Create a modifier that makes the query distinct.\n"). -spec with_distinct() -> fun((cquill@query@ast:'query'(SIL)) -> cquill@query@ast:'query'(SIL)). with_distinct() -> fun(Q) -> cquill@query:distinct(Q) end. -file("src/cquill/query/builder.gleam", 196). ?DOC(" Create a named scope.\n"). -spec scope( binary(), fun((cquill@query@ast:'query'(SIN)) -> cquill@query@ast:'query'(SIN)) ) -> scope(SIN). scope(Name, Modifier) -> {scope, Name, Modifier}. -file("src/cquill/query/builder.gleam", 201). ?DOC(" Apply a scope to a query.\n"). -spec apply_scope(cquill@query@ast:'query'(SIQ), scope(SIQ)) -> cquill@query@ast:'query'(SIQ). apply_scope(Query, Scope) -> (erlang:element(3, Scope))(Query). -file("src/cquill/query/builder.gleam", 206). ?DOC(" Apply multiple scopes to a query.\n"). -spec apply_scopes(cquill@query@ast:'query'(SIU), list(scope(SIU))) -> cquill@query@ast:'query'(SIU). apply_scopes(Query, Scopes) -> gleam@list:fold(Scopes, Query, fun(Q, S) -> apply_scope(Q, S) end). -file("src/cquill/query/builder.gleam", 216). ?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'(SIZ), cquill@query@ast:'query'(any()) ) -> cquill@query@ast:'query'(SIZ). 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", 222). ?DOC(" Merge order_bys from source query into target query.\n"). -spec merge_order_bys( cquill@query@ast:'query'(SJE), cquill@query@ast:'query'(any()) ) -> cquill@query@ast:'query'(SJE). 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", 230). ?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'(SJJ), cquill@query@ast:'query'(any()) ) -> cquill@query@ast:'query'(SJJ). 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", 255). ?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'(SJO)) -> cquill@query@ast:'query'(SJO)). not_deleted() -> filter_null(<<"deleted_at"/utf8>>). -file("src/cquill/query/builder.gleam", 261). ?DOC( " Create a \"published\" filter for content models.\n" " Assumes a `published` boolean field.\n" ). -spec published() -> fun((cquill@query@ast:'query'(SJQ)) -> cquill@query@ast:'query'(SJQ)). published() -> filter_eq_bool(<<"published"/utf8>>, true). -file("src/cquill/query/builder.gleam", 267). ?DOC( " Create an \"active\" filter.\n" " Assumes an `active` boolean field.\n" ). -spec active() -> fun((cquill@query@ast:'query'(SJS)) -> cquill@query@ast:'query'(SJS)). active() -> filter_eq_bool(<<"active"/utf8>>, true). -file("src/cquill/query/builder.gleam", 273). ?DOC( " Create a \"recent first\" ordering modifier.\n" " Assumes a `created_at` timestamp field.\n" ). -spec recent_first() -> fun((cquill@query@ast:'query'(SJU)) -> cquill@query@ast:'query'(SJU)). recent_first() -> order_desc(<<"created_at"/utf8>>). -file("src/cquill/query/builder.gleam", 279). ?DOC( " Create an \"oldest first\" ordering modifier.\n" " Assumes a `created_at` timestamp field.\n" ). -spec oldest_first() -> fun((cquill@query@ast:'query'(SJW)) -> cquill@query@ast:'query'(SJW)). oldest_first() -> order_asc(<<"created_at"/utf8>>). -file("src/cquill/query/builder.gleam", 285). ?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'(SJY)) -> cquill@query@ast:'query'(SJY)). by_user(User_id) -> filter_eq_int(<<"user_id"/utf8>>, User_id). -file("src/cquill/query/builder.gleam", 294). ?DOC(" Clone a query, optionally resetting certain parts.\n"). -spec clone(cquill@query@ast:'query'(SKA)) -> cquill@query@ast:'query'(SKA). clone(Q) -> Q. -file("src/cquill/query/builder.gleam", 299). ?DOC(" Clone a query but clear all WHERE conditions.\n"). -spec clone_without_conditions(cquill@query@ast:'query'(SKD)) -> cquill@query@ast:'query'(SKD). clone_without_conditions(Q) -> cquill@query:where_clear(Q). -file("src/cquill/query/builder.gleam", 304). ?DOC(" Clone a query but clear all ORDER BY.\n"). -spec clone_without_order(cquill@query@ast:'query'(SKG)) -> cquill@query@ast:'query'(SKG). clone_without_order(Q) -> cquill@query:order_by_clear(Q). -file("src/cquill/query/builder.gleam", 309). ?DOC(" Clone a query but clear pagination.\n"). -spec clone_without_pagination(cquill@query@ast:'query'(SKJ)) -> cquill@query@ast:'query'(SKJ). clone_without_pagination(Q) -> cquill@query:no_pagination(Q). -file("src/cquill/query/builder.gleam", 318). ?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", 333). -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", 328). ?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", 354). -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", 347). ?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).