Packages
ash_sql
0.2.36
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.93
0.2.92
0.2.91
0.2.90
0.2.89
0.2.88
0.2.87
0.2.86
0.2.85
0.2.84
0.2.83
0.2.82
0.2.81
0.2.80
0.2.79
0.2.78
0.2.77
0.2.76
0.2.75
0.2.74
0.2.73
0.2.72
0.2.71
0.2.70
0.2.69
0.2.68
0.2.67
0.2.66
0.2.65
0.2.64
0.2.63
0.2.62
0.2.61
0.2.60
0.2.59
0.2.58
0.2.57
0.2.56
0.2.55
0.2.54
0.2.53
0.2.52
0.2.51
0.2.50
0.2.49
0.2.48
0.2.47
0.2.46
0.2.45
0.2.44
0.2.43
0.2.42
0.2.41
0.2.40
0.2.39
0.2.38
0.2.37
0.2.36
0.2.35
0.2.34
0.2.33
0.2.32
0.2.31
0.2.30
0.2.29
0.2.28
0.2.27
0.2.26
0.2.25
0.2.24
0.2.23
0.2.22
0.2.21
0.2.20
0.2.19
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1-rc.20
0.1.1-rc.19
0.1.1-rc.18
0.1.1-rc.17
0.1.1-rc.16
0.1.1-rc.15
0.1.1-rc.14
0.1.1-rc.13
0.1.1-rc.12
0.1.1-rc.11
0.1.1-rc.10
0.1.1-rc.9
0.1.1-rc.8
0.1.1-rc.7
0.1.1-rc.6
0.1.1-rc.5
0.1.1-rc.4
0.1.1-rc.3
0.1.1-rc.2
0.1.1-rc.1
0.1.1-rc.0
Shared utilities for ecto-based sql data layers.
Current section
Files
Jump to
Current section
Files
lib/aggregate_query.ex
defmodule AshSql.AggregateQuery do
@moduledoc false
import Ecto.Query, only: [from: 2, subquery: 1]
def run_aggregate_query(original_query, aggregates, resource, implementation) do
original_query =
AshSql.Bindings.default_bindings(original_query, resource, implementation)
{can_group, cant_group} =
aggregates
|> Enum.split_with(&AshSql.Aggregate.can_group?(resource, &1, original_query))
|> case do
{[one], cant_group} -> {[], [one | cant_group]}
{can_group, cant_group} -> {can_group, cant_group}
end
{global_filter, can_group} =
AshSql.Aggregate.extract_shared_filters(can_group)
query =
case global_filter do
{:ok, global_filter} ->
AshSql.Filter.filter(original_query, global_filter, resource)
:error ->
{:ok, original_query}
end
case query do
{:error, error} ->
{:error, error}
{:ok, query} ->
query =
if query.distinct || query.limit do
query =
query
|> Ecto.Query.exclude(:select)
|> Ecto.Query.exclude(:order_by)
|> Map.put(:windows, [])
from(row in subquery(query), as: ^query.__ash_bindings__.root_binding, select: %{})
else
query
|> Ecto.Query.exclude(:select)
|> Ecto.Query.exclude(:order_by)
|> Map.put(:windows, [])
|> Ecto.Query.select(%{})
end
query =
Enum.reduce(
can_group,
query,
fn agg, query ->
first_relationship =
Ash.Resource.Info.relationship(resource, agg.relationship_path |> Enum.at(0))
AshSql.Aggregate.add_subquery_aggregate_select(
query,
agg.relationship_path |> Enum.drop(1),
agg,
resource,
false,
first_relationship
)
end
)
result =
case can_group do
[] ->
%{}
_ ->
repo = AshSql.dynamic_repo(resource, implementation, query)
repo.one(query, AshSql.repo_opts(repo, implementation, nil, nil, resource))
end
{:ok, add_single_aggs(result, resource, original_query, cant_group, implementation)}
end
end
def add_single_aggs(result, resource, query, cant_group, implementation) do
Enum.reduce(cant_group, result, fn
%{kind: :exists} = agg, result ->
{:ok, filtered} =
case agg do
%{query: %{filter: filter}} when not is_nil(filter) ->
AshSql.Filter.filter(query, filter, resource)
_ ->
{:ok, query}
end
filtered =
if filtered.distinct || filtered.limit do
filtered =
filtered
|> Ecto.Query.exclude(:select)
|> Ecto.Query.exclude(:order_by)
|> Map.put(:windows, [])
from(row in subquery(filtered), as: ^query.__ash_bindings__.root_binding, select: %{})
else
filtered
|> Ecto.Query.exclude(:select)
|> Ecto.Query.exclude(:order_by)
|> Map.put(:windows, [])
|> Ecto.Query.select(%{})
end
repo = AshSql.dynamic_repo(resource, implementation, filtered)
Map.put(
result || %{},
agg.name,
repo.exists?(filtered, AshSql.repo_opts(repo, implementation, nil, nil, resource))
)
agg, result ->
{:ok, filtered} =
case agg do
%{query: %{filter: filter}} when not is_nil(filter) ->
AshSql.Filter.filter(query, filter, resource)
_ ->
{:ok, query}
end
filtered =
if filtered.distinct do
in_query = filtered |> Ecto.Query.exclude(:distinct) |> Ecto.Query.exclude(:select)
dynamic =
Enum.reduce(Ash.Resource.Info.primary_key(resource), nil, fn key, dynamic ->
if dynamic do
Ecto.Query.dynamic(
[row],
^dynamic and
field(parent_as(^query.__ash_bindings__.root_binding), ^key) ==
field(row, ^key)
)
else
Ecto.Query.dynamic(
[row],
field(parent_as(^query.__ash_bindings__.root_binding), ^key) ==
field(row, ^key)
)
end
end)
in_query =
from(row in in_query, where: ^dynamic)
in_query = Ecto.Query.exclude(in_query, :distinct)
from(row in query.from.source,
as: ^query.__ash_bindings__.root_binding,
where: exists(in_query)
)
else
filtered
end
filtered =
if filtered.limit do
filtered =
filtered
|> Ecto.Query.exclude(:select)
|> Ecto.Query.exclude(:order_by)
|> Map.put(:windows, [])
from(row in subquery(filtered), as: ^query.__ash_bindings__.root_binding, select: %{})
else
filtered
|> Ecto.Query.exclude(:select)
|> Ecto.Query.exclude(:order_by)
|> Map.put(:windows, [])
|> Ecto.Query.select(%{})
end
first_relationship =
Ash.Resource.Info.relationship(resource, agg.relationship_path |> Enum.at(0))
query =
AshSql.Aggregate.add_subquery_aggregate_select(
AshSql.Bindings.default_bindings(filtered, resource, implementation),
agg.relationship_path |> Enum.drop(1),
%{agg | query: %{agg.query | filter: nil}},
resource,
true,
first_relationship
)
repo = AshSql.dynamic_repo(resource, implementation, query)
Map.merge(
result || %{},
repo.one(
query,
AshSql.repo_opts(repo, query.__ash_bindings__.sql_behaviour, nil, nil, resource)
)
)
end)
end
end