Using Aggregates and Calculations in read actions, possibly using Cachex

Hello friends!

So I’ve been ticking away at a project to showcase the possible use case for using Ash and Phoenix at my place of work.

I feel like I’m missing a few things about Ash (or maybe it’s just Elixir) to fully flesh out this example project.

For starters, here’s a more “”“real world”“” example of an Ash Resource that outlines what I’m trying to do:

defmodule MyProject.Entities.State do
  @moduledoc """
  """
  use Ash.Resource,
    data_layer: AshPostgres.DataLayer,
    extensions: AshJsonApi.Resource

  attributes do
    uuid_primary_key :id

    attribute :name, :string do
      allow_nil? false
    end

    attribute :opt_out, :boolean do
      allow_nil? false
      default false
    end
  end

  actions do
    # Exposes default built in actions to manage the resource
    defaults [:create, :read, :update, :destroy]

    # Defines custom read action which fetches post by id.
    read :by_id do
      # This action has one argument :id of type :uuid
      argument :id, :uuid, allow_nil?: false
      # Tells us we expect this action to return a single result
      get? true
      filter expr(id == ^arg(:id))
    end
      
  end

  code_interface do
    define_for MyProject.Entities
    define :create, action: :create
    define :read_all, action: :read
    define :update, action: :update
    define :destroy, action: :destroy
    define :get_by_id, args: [:id], action: :by_id
  end

  relationships do
    belongs_to :country, MyProject.Entities.Country
    has_many :counties, MyProject.Entities.County
    has_many :cities, MyProject.Entities.City
  end

  aggregates do
    count :number_of_counties, :counties do
      description "Nubmer of counties in a state."
      filter expr(opt_out == false)
    end

    list :counties_in_state, :counties, :id do
      description "List of counties in the given state."
      filter expr(opt_out == false)
    end

    count :number_of_cities, :cities do
      description "Number of cities in a state"
      filter expr(opt_out == false)
    end

    list :cities_in_state, :cities, :id do
      description "List of cities in the given state."
      filter expr(opt_out == false)
    end
  end

  postgres do
    table "states"
    repo MyProject.Repo
  end

  json_api do
    type "states"

    routes do
      base "/states"

      get :by_id, route: "/:id", action: :by_id
      index :read, route: "/all"
      post :create
    end
  end
end

So a couple of questions:

  1. How do I call an aggregate or calculation in a read action?

    list is very useful in a lot of situations that I’d be facing.

  2. Is it possible or even recommended to cache the results of read actions via Cachex?

    They will be used in the json_api section and the result of the some read actions can be quite large, some results might even be from the list type of aggregate or even more advanced calculations. Of course abusing MATERIALIZED VIEW is an option, but is that always recommended first before any advanced caching mechanisms?