Run this notebook

Use Livebook to open this notebook and explore new ideas.

It is easy to get started, on your machine or the cloud.

Click below to open and run it in your Livebook at .

(or change your Livebook location)

<!-- SPDX-FileCopyrightText: 2023 Wout De Puysseleir SPDX-License-Identifier: MIT --> # Ash: 8 - Aggregates ```elixir Application.put_env(:ash, :validate_domain_resource_inclusion?, false) Application.put_env(:ash, :validate_domain_config_inclusion?, false) Mix.install([{:ash, "~> 3.0"}], consolidate_protocols: false) ``` ## Aggregates <div class="flex items-center w-full flex-start justify-between rounded-xl p-4" style="background-color: #f0f5f9; color: #61758a;"> <div class="flex"> <i class="ri-arrow-left-fill"></i> <a class="flex ml-2" style="color: #61758a;" href="code_interfaces.livemd">Code Interfaces</a> </div> <div class="flex"> <i class="ri-home-fill"></i> <a class="flex ml-2" style="color: #61758a;" href="overview.livemd">Home</a> </div> <div class="flex"> <a class="flex mr-2" style="color: #61758a;" href="calculations.livemd">Calculations</a> <i class="ri-arrow-right-fill"></i> </div> </div> ### In this tutorial you will add an Aggregate on a Resource [Aggregates](https://hexdocs.pm/ash/aggregates.html) in Ash allow for retrieving summary information over groups of related data. Aggregates can be either _count_, _first_, _sum_ or _list_. Implement a count aggregate on the 'open tickets' a representative is assigned to. First explore the comments in the code below, to make this work properly you have to add the inverse relationship of belongs_to inside the Representative resource. To add an aggregate define an `aggregates do .. end` block inside the representative. Inside the `aggregates` block, define a `count :count_of_open_tickets, :tickets do .. end` block. Then inside this block, define a filter like so: `filter expr(status == :open)` Also, notice what happens when you don't define a filter. <details class="rounded-lg my-4" style="background-color: #96ef86; color: #040604;"> <summary class="cursor-pointer font-bold p-4"></i>Show Solution</summary> <div class="p-4"> ```elixir aggregates do count :count_of_open_tickets, :tickets do filter expr(status == :open) end end ``` </div> </details> ### Enter your solution ```elixir defmodule Tutorial.Support.Ticket do use Ash.Resource, domain: Tutorial.Support, data_layer: Ash.DataLayer.Ets actions do defaults [:read] # On creation set the representative by providing the id create :open do accept [:subject, :description, :representative_id] end update :close do accept [] change set_attribute(:status, :closed) end update :assign do accept [:representative_id] end end attributes do uuid_primary_key(:id) attribute :subject, :string, allow_nil?: false attribute :description, :string, allow_nil?: true attribute :status, :atom do constraints one_of: [:open, :closed] default :open allow_nil? false end create_timestamp :created_at update_timestamp :updated_at end relationships do belongs_to :representative, Tutorial.Support.Representative end code_interface do define :assign, args: [:representative_id] # <- added representative_id define :open, args: [:subject, :description, :representative_id] define :close, args: [] end end defmodule Tutorial.Support.Representative do use Ash.Resource, domain: Tutorial.Support, data_layer: Ash.DataLayer.Ets actions do defaults [:read] create :create do accept [:name] end end attributes do uuid_primary_key :id attribute :name, :string end # Added the inverse relationship of belongs_to in the ticket. # This way we can reference :tickets inside the aggregates. relationships do has_many :tickets, Tutorial.Support.Ticket end code_interface do define :create, args: [:name] end # <- Add the aggregates here end ``` ```elixir defmodule Tutorial.Support do use Ash.Domain resources do resource Tutorial.Support.Ticket resource Tutorial.Support.Representative end end ``` ## Query the Aggregate Use a [Bulk Create](https://hexdocs.pm/ash/create-actions.html#bulk-creates) to create 4 tickets, 3 of which are assigned to Joe. ```elixir # Create a representative joe = Tutorial.Support.Representative.create!("Joe Armstrong") # Bulk create 4 tickets, 3 of which are assigned to Joe [ %{subject: "I can't see my eyes!"}, %{subject: "I can't find my hand!", representative_id: joe.id}, %{subject: "My fridge is flying away!", representative_id: joe.id}, %{subject: "My bed is invisible!", representative_id: joe.id} ] |> Ash.bulk_create(Tutorial.Support.Ticket, :open, return_records?: true) ``` Close the last ticket assigned to Joe. ```elixir require Ash.Query # Retrieve the last ticket [last_ticket] = Tutorial.Support.Ticket |> Ash.Query.filter(representative_id == ^joe.id) |> Ash.Query.sort(created_at: :desc) |> Ash.Query.limit(1) |> Ash.read!() # Close the last ticket Tutorial.Support.Ticket.close!(last_ticket) ``` ```elixir joe = Ash.load!(joe, [:count_of_open_tickets]) joe.count_of_open_tickets ``` The result should be __2__, as you opened 4 tickets, 3 were assigned to Joe, but the last assigned ticket was closed. <!-- livebook:{"break_markdown":true} --> <div class="flex items-center w-full flex-start justify-between rounded-xl p-4" style="background-color: #f0f5f9; color: #61758a;"> <div class="flex"> <i class="ri-arrow-left-fill"></i> <a class="flex ml-2" style="color: #61758a;" href="code_interfaces.livemd">Code Interfaces</a> </div> <div class="flex"> <i class="ri-home-fill"></i> <a class="flex ml-2" style="color: #61758a;" href="overview.livemd">Home</a> </div> <div class="flex"> <a class="flex mr-2" style="color: #61758a;" href="calculations.livemd">Calculations</a> <i class="ri-arrow-right-fill"></i> </div> </div>
See source

Have you already installed Livebook?

If you already installed Livebook, you can configure the default Livebook location where you want to open notebooks.
Livebook up Checking status We can't reach this Livebook (but we saved your preference anyway)
Run notebook

Not yet? Install Livebook in just a minute

Livebook is open source, free, and ready to run anywhere.

Run in the cloud

on select platforms

To run on Linux, Docker, embedded devices, or Elixir’s Mix, check our README.

PLATINUM SPONSORS
SPONSORS
Code navigation with go to definition of modules and functions Read More