Packages
ash_sql
0.1.1-rc.2
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, implementation, resource)
{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: ^0, 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: ^0, 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(^0), ^key) == field(row, ^key)
)
else
Ecto.Query.dynamic(
[row],
field(parent_as(^0), ^key) == field(row, ^key)
)
end
end)
in_query =
from(row in in_query, where: ^dynamic)
from(row in query.from.source, as: ^0, 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: ^0, 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