improvement: add sum aggregate (#221)

This commit is contained in:
Michael St Clair 2021-04-04 14:00:53 -06:00 committed by GitHub
parent 7ecd72e61d
commit 6805d431ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 7 deletions

View file

@ -89,6 +89,8 @@ locals_without_parens = [
source_field: 1,
source_field_on_join_table: 1,
strategy: 1,
sum: 3,
sum: 4,
table: 1,
through: 1,
timestamps: 0,

View file

@ -13,7 +13,7 @@ defmodule Ash.Query.Aggregate do
:load
]
@kinds [:count, :first]
@kinds [:count, :first, :sum]
@type t :: %__MODULE__{}
@type kind :: unquote(Enum.reduce(@kinds, &{:|, [], [&1, &2]}))
@ -90,6 +90,7 @@ defmodule Ash.Query.Aggregate do
defp default_value(:count), do: 0
defp default_value(:first), do: nil
defp default_value(:sum), do: nil
defp validate_query(nil), do: {:ok, nil}
@ -114,8 +115,8 @@ defmodule Ash.Query.Aggregate do
@doc false
def kind_to_type(:count, _field_type), do: {:ok, Ash.Type.Integer}
def kind_to_type(:first, nil), do: {:error, "Must provide field type for :first"}
def kind_to_type(:first, field_type), do: {:ok, field_type}
def kind_to_type(kind, nil), do: {:error, "Must provide field type for #{kind}"}
def kind_to_type(kind, field_type) when kind in [:first, :sum], do: {:ok, field_type}
def kind_to_type(kind, _field_type), do: {:error, "Invalid aggregate kind: #{kind}"}
def requests(initial_query, can_be_in_query?, authorizing?) do

View file

@ -14,7 +14,7 @@ defmodule Ash.Resource.Aggregate do
required: true
],
kind: [
type: {:in, [:count, :first]},
type: {:in, [:count, :first, :sum]},
doc: "The kind of the aggregate",
required: true
],
@ -50,7 +50,7 @@ defmodule Ash.Resource.Aggregate do
relationship_path: {:ok, list(atom())} | {:error, String.t()},
filter: Keyword.t(),
field: atom,
kind: :count | :first,
kind: :count | :first | :sum,
description: String.t() | nil,
private?: boolean
}

View file

@ -743,6 +743,26 @@ defmodule Ash.Resource.Dsl do
auto_set_fields: [kind: :first]
}
@sum %Ash.Dsl.Entity{
name: :sum,
describe: """
Declares a named `sum` aggregate on the resource
Supports `filter`, but not `sort` (because that wouldn't affect the count)
""",
examples: [
"""
sum :assigned_ticket_price_sum, :assigned_tickets, :price do
filter [active: true]
end
"""
],
target: Ash.Resource.Aggregate,
args: [:name, :relationship_path, :field],
schema: Keyword.delete(Ash.Resource.Aggregate.schema(), :sort),
auto_set_fields: [kind: :sum]
}
@aggregates %Ash.Dsl.Section{
name: :aggregates,
describe: """
@ -762,7 +782,8 @@ defmodule Ash.Resource.Dsl do
],
entities: [
@count,
@first
@first,
@sum
]
}

View file

@ -35,6 +35,7 @@ defmodule Ash.Test.Resource.AggregatesTest do
aggregates do
count :count_of_comments, :comments
count :another_count_but_private, :comments, private?: true
sum :sum_of_comment_likes, :comments, :likes
end
relationships do
@ -45,17 +46,29 @@ defmodule Ash.Test.Resource.AggregatesTest do
assert [
%Aggregate{
name: :count_of_comments,
kind: :count,
relationship_path: [:comments],
private?: false
},
%Aggregate{
name: :another_count_but_private,
kind: :count,
relationship_path: [:comments],
private?: true
},
%Ash.Resource.Aggregate{
field: :likes,
kind: :sum,
name: :sum_of_comment_likes,
private?: false,
relationship_path: [:comments]
}
] = Ash.Resource.Info.aggregates(Post)
assert [%Aggregate{name: :count_of_comments}] = Ash.Resource.Info.public_aggregates(Post)
assert [
%Aggregate{name: :count_of_comments},
%Aggregate{name: :sum_of_comment_likes}
] = Ash.Resource.Info.public_aggregates(Post)
assert %Aggregate{name: :another_count_but_private} =
Ash.Resource.Info.aggregate(Post, :another_count_but_private)