# Calculations Calculations allow you to define derived values based on a resource's attributes or related data. Define calculations in the `calculations` block of a resource: ```elixir calculations do # Simple expression calculation calculate :full_name, :string, expr(first_name <> " " <> last_name) # Expression with conditions calculate :status_label, :string, expr( cond do status == :active -> "Active" status == :pending -> "Pending Review" true -> "Inactive" end ) # Using module calculations for more complex logic calculate :risk_score, :integer, {MyApp.Calculations.RiskScore, min: 0, max: 100} end ``` ## Expression Calculations Expression calculations use Ash expressions and can be pushed down to the data layer when possible: ```elixir calculations do # Simple string concatenation calculate :full_name, :string, expr(first_name <> " " <> last_name) # Math operations calculate :total_with_tax, :decimal, expr(amount * (1 + tax_rate)) # Date manipulation calculate :days_since_created, :integer, expr( date_diff(^now(), inserted_at, :day) ) end ``` ## Expressions In order to use expressions outside of resources, changes, preparations etc. you will need to use `Ash.Expr`. It provides both `expr/1` and template helpers like `actor/1` and `arg/1`. For example: ```elixir import Ash.Expr Author |> Ash.Query.aggregate(:count_of_my_favorited_posts, :count, [:posts], query: [ filter: expr(favorited_by(user_id: ^actor(:id))) ]) ``` See the expressions guide for more information on what is available in expresisons and how to use them. ## Module Calculations For complex calculations, create a module that implements `Ash.Resource.Calculation`: ```elixir defmodule MyApp.Calculations.FullName do use Ash.Resource.Calculation # Validate and transform options @impl true def init(opts) do {:ok, Map.put_new(opts, :separator, " ")} end # Specify what data needs to be loaded @impl true def load(_query, _opts, _context) do [:first_name, :last_name] end # Implement the calculation logic @impl true def calculate(records, opts, _context) do Enum.map(records, fn record -> [record.first_name, record.last_name] |> Enum.reject(&is_nil/1) |> Enum.join(opts.separator) end) end end # Usage in a resource calculations do calculate :full_name, :string, {MyApp.Calculations.FullName, separator: ", "} end ``` ## Calculations with Arguments You can define calculations that accept arguments: ```elixir calculations do calculate :full_name, :string, expr(first_name <> ^arg(:separator) <> last_name) do argument :separator, :string do allow_nil? false default " " constraints [allow_empty?: true, trim?: false] end end end ``` ## Using Calculations ```elixir # Using code interface options (preferred) users = MyDomain.list_users!(load: [full_name: [separator: ", "]]) # Filtering and sorting users = MyDomain.list_users!( query: [ filter: [full_name: [separator: " ", value: "John Doe"]], sort: [full_name: {[separator: " "], :asc}] ] ) # Manual query building (for complex cases) User |> Ash.Query.load(full_name: [separator: ", "]) |> Ash.read!() # Loading on existing records Ash.load!(users, :full_name) ``` ### Code Interface for Calculations Define calculation functions on your domain for standalone use: ```elixir # In your domain resource User do define_calculation :full_name, args: [:first_name, :last_name, {:optional, :separator}] end # Then call it directly MyDomain.full_name("John", "Doe", ", ") # Returns "John, Doe" ```