Current section
Files
Jump to
Current section
Files
lib/abacus_sql.ex
defmodule AbacusSql do
alias AbacusSql.Term
@type t :: binary | list | tuple
@spec select(Ecto.Query.t, atom | String.t, t) :: Ecto.Query.t
def select(query, key, term) do
params = case query.select do
%{params: params} -> params
_ -> []
end
with {:ok, query, expr, params} <- Term.to_ecto_term(query, term, params) do
select_item = {key, expr}
select_expr = {:%{}, [], [select_item]}
new_select = %Ecto.Query.SelectExpr{
expr: select_expr,
params: params,
take: %{}
}
Map.update(query, :select, new_select, fn
nil ->
new_select
%Ecto.Query.SelectExpr{
expr: {:%{}, ctx, list}
} = select ->
select
|> Map.put(:expr, {:%{}, ctx, list ++ [select_item]})
|> Map.put(:params, params)
%Ecto.Query.SelectExpr{
expr: {:merge, ctx, _} = merge
} = select ->
select
|> Map.put(:expr, {:merge, [], [merge, select_expr]})
|> Map.put(:params, params)
end)
else
_ -> query
end
end
@spec where(Ecto.Query.t, t) :: Ecto.Query.t
def where(query, term) do
with {:ok, query, expr, params} <- Term.to_ecto_term(query, term) do
where = %Ecto.Query.BooleanExpr{
expr: expr,
op: :and,
params: params
}
Map.update!(query, :wheres, &[where | &1])
else
_ -> query
end
end
@doc """
Adds the given filter to the havings list.
Make sure that the query has at least one group_by clause.
The advantage is that it can filter inside `has_many` assocs
using aggregation such as `count`, `min` or `max`.
For example: `having(BlogPost, "count(author.posts.id) > 10")` would
filter for blog posts whose authors have at least 10 posts.
"""
@spec having(Ecto.Query.t, t) :: Ecto.Query.t
def having(query, term) do
with {:ok, query, expr, params} <- Term.to_ecto_term(query, term) do
having = %Ecto.Query.BooleanExpr{
expr: expr,
op: :and,
params: params
}
Map.update!(query, :havings, &[having | &1])
else
_ -> query
end
end
@spec order_by(Ecto.Query.t, t, boolean) :: Ecto.Query.t
def order_by(query, term, ascending? \\ true) do
with {:ok, query, expr, params} <- Term.to_ecto_term(query, term) do
direction = if ascending?, do: :asc, else: :desc
Map.update!(query, :order_bys, fn order_bys ->
order_by = %Ecto.Query.QueryExpr{
expr: [{direction, expr}],
params: params
}
order_bys ++ [order_by]
end)
else
_ -> query
end
end
@spec group_by(Ecto.Query.t, t) :: Ecto.Query.t
def group_by(query, term) do
with {:ok, query, expr, params} <- Term.to_ecto_term(query, term) do
Map.update!(query, :group_bys, fn group_bys ->
group_by = %Ecto.Query.QueryExpr{
expr: [expr],
params: params
}
group_bys ++ [group_by]
end)
else
_ -> query
end
end
@spec scope(Ecto.Query.t(), atom(), term() | %{__struct__: module(), __meta__: Ecto.Schema.Metadata.t()}) :: Ecto.Query.t()
def scope(query, key, value) do
scope(query, [{key, value}])
end
@spec scope(Ecto.Query.t(), [{atom(), term() | %{__struct__: module(), __meta__: Ecto.Schema.Metadata.t()}}]) :: Ecto.Query.t()
def scope(query, scope) do
Enum.reduce(scope, query, fn {key, value}, query ->
join = AbacusSql.Scope.scope_join(query, key, value)
Map.update!(query, :joins, &(&1 ++ [join]))
end)
end
end