Skip to content

Instantly share code, notes, and snippets.

@moxley
Created March 3, 2025 22:27
Show Gist options
  • Save moxley/a43b8d6b3e9cade93f7defb956a8e161 to your computer and use it in GitHub Desktop.
Save moxley/a43b8d6b3e9cade93f7defb956a8e161 to your computer and use it in GitHub Desktop.
Debugging complex Ash aggregate using `parent()`

In AshSql: lib/expr.ex, in maybe_type_expr/6, I added the following debug statements:

        other ->
          dbg(query)
          dbg(other)
          dbg(bindings)
          dbg(embedded?)
          dbg(acc)
          dbg(type)
          {expr, acc} = do_dynamic_expr(query, other, bindings, embedded?, acc, type)
          type = bindings.sql_behaviour.parameterized_type(type, [])

          if type do
            {query.__ash_bindings__.sql_behaviour.type_expr(expr, type), acc}
          else
            {expr, acc}
          end

Then loaded the aggregate:

Ash.get!(Participant2, participant.id,
  authorize?: false,
  load: [:unread_messages_count2]
)
[(ash_sql 0.2.59) lib/expr.ex:2868: AshSql.Expr.maybe_type_expr/6]
query #=> #Ecto.Query<from p0 in GF.Messaging.Participant2, as: 0,
 select: struct(p0, [
  :id,
  :member_id,
  :last_read_message_id,
  :conversation_id,
  :last_notified_message_id,
  :silence_until
])>

[(ash_sql 0.2.59) lib/expr.ex:2869: AshSql.Expr.maybe_type_expr/6]
other #=> id

[(ash_sql 0.2.59) lib/expr.ex:2870: AshSql.Expr.maybe_type_expr/6]
bindings #=> %{
  sort: [],
  domain: GF.Domain,
  context: %{
    private: %{
      actor: #GF.Members.Member2<
        trial: #Ash.NotLoaded<:calculation, field: :trial>,
        left_group_on: #Ash.NotLoaded<:calculation, field: :left_group_on>,
        lower_email: #Ash.NotLoaded<:calculation, field: :lower_email>,
        account_confirmed: #Ash.NotLoaded<:calculation, field: :account_confirmed>,
        unread_topics_count: #Ash.NotLoaded<:aggregate, field: :unread_topics_count>,
        transactions: #Ash.NotLoaded<:relationship, field: :transactions>,
        team_memberships: #Ash.NotLoaded<:relationship, field: :team_memberships>,
        applications: #Ash.NotLoaded<:relationship, field: :applications>,
        signups: #Ash.NotLoaded<:relationship, field: :signups>,
        payment_methods: #Ash.NotLoaded<:relationship, field: :payment_methods>,
        participations: #Ash.NotLoaded<:relationship, field: :participations>,
        notes: #Ash.NotLoaded<:relationship, field: :notes>,
        notifications: #Ash.NotLoaded<:relationship, field: :notifications>,
        unread_topics: #Ash.NotLoaded<:relationship, field: :unread_topics>,
        conversation_participants: #Ash.NotLoaded<:relationship, field: :conversation_participants>,
        member_tags: #Ash.NotLoaded<:relationship, field: :member_tags>,
        notification_settings: #Ash.NotLoaded<:relationship, field: :notification_settings>,
        topic_view: #Ash.NotLoaded<:relationship, field: :topic_view>,
        profile: #Ash.NotLoaded<:relationship, field: :profile>,
        membership_plan: #Ash.NotLoaded<:relationship, field: :membership_plan>,
        linked_account: #Ash.NotLoaded<:relationship, field: :linked_account>,
        group: #Ash.NotLoaded<:relationship, field: :group>,
        __meta__: #Ecto.Schema.Metadata<:loaded, "public", "members">,
        account_confirmed_at: ~U[2025-03-03 22:21:04Z],
        auth_code: nil,
        auth_code_expires_at: nil,
        auto_renews: false,
        banned_datetime: nil,
        customer_id: nil,
        developer_access: false,
        dues_exempt: false,
        dues_variant_id: nil,
        email: "[email protected]",
        email_confirmed_at: nil,
        emails_enabled: true,
        emails_paused_until: nil,
        group_id: 617093,
        id: "OBdTzV7r2esI6r9",
        joined_group_on: ~D[2025-01-02],
        is_sample: false,
        linked_account_id: nil,
        meetup_member_id: 1741040464,
        member_type: :participant,
        membership_ends_on: ~D[2026-01-02],
        membership_plan_id: nil,
        ...
      >,
      authorize?: true,
      tenant: nil,
      in_before_action?: true,
      pre_flight_authorization?: false
    },
    action: %Ash.Resource.Actions.Read{
      arguments: [],
      description: nil,
      filter: nil,
      filters: [],
      get_by: [],
      get?: false,
      manual: nil,
      metadata: [],
      skip_unknown_inputs: [],
      modify_query: nil,
      multitenancy: :enforce,
      name: :read,
      pagination: %Ash.Resource.Actions.Read.Pagination{
        default_limit: nil,
        max_page_size: 250,
        countable: true,
        required?: false,
        keyset?: true,
        offset?: true
      },
      preparations: [],
      primary?: true,
      touches_resources: [],
      timeout: nil,
      transaction?: false,
      type: :read
    }
  },
  bindings: %{0 => %{type: :root, path: [], source: GF.Messaging.Participant2}},
  resource: GF.Messaging.Participant2,
  sql_behaviour: AshPostgres.SqlImplementation,
  calculations: %{},
  parent_resources: [],
  aggregate_names: %{},
  in_group?: false,
  expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
  current: 1,
  root_binding: 0,
  calculation_names: %{},
  current_calculation_name: :calculation_0,
  aggregate_defs: %{},
  current_aggregate_name: :aggregate_0,
  lateral_join?: false
}

[(ash_sql 0.2.59) lib/expr.ex:2871: AshSql.Expr.maybe_type_expr/6]
embedded? #=> false

[(ash_sql 0.2.59) lib/expr.ex:2872: AshSql.Expr.maybe_type_expr/6]
acc #=> %AshSql.Expr.ExprInfo{has_error?: false}

[(ash_sql 0.2.59) lib/expr.ex:2873: AshSql.Expr.maybe_type_expr/6]
type #=> {Ash.Type.Integer, []}

[(ash_sql 0.2.59) lib/expr.ex:2868: AshSql.Expr.maybe_type_expr/6]
query #=> #Ecto.Query<from p0 in GF.Messaging.Participant2, as: 0,
 select: struct(p0, [
  :id,
  :member_id,
  :last_read_message_id,
  :conversation_id,
  :last_notified_message_id,
  :silence_until
])>

[(ash_sql 0.2.59) lib/expr.ex:2869: AshSql.Expr.maybe_type_expr/6]
other #=> 115318

[(ash_sql 0.2.59) lib/expr.ex:2870: AshSql.Expr.maybe_type_expr/6]
bindings #=> %{
  sort: [],
  domain: GF.Domain,
  context: %{
    private: %{
      actor: #GF.Members.Member2<
        trial: #Ash.NotLoaded<:calculation, field: :trial>,
        left_group_on: #Ash.NotLoaded<:calculation, field: :left_group_on>,
        lower_email: #Ash.NotLoaded<:calculation, field: :lower_email>,
        account_confirmed: #Ash.NotLoaded<:calculation, field: :account_confirmed>,
        unread_topics_count: #Ash.NotLoaded<:aggregate, field: :unread_topics_count>,
        transactions: #Ash.NotLoaded<:relationship, field: :transactions>,
        team_memberships: #Ash.NotLoaded<:relationship, field: :team_memberships>,
        applications: #Ash.NotLoaded<:relationship, field: :applications>,
        signups: #Ash.NotLoaded<:relationship, field: :signups>,
        payment_methods: #Ash.NotLoaded<:relationship, field: :payment_methods>,
        participations: #Ash.NotLoaded<:relationship, field: :participations>,
        notes: #Ash.NotLoaded<:relationship, field: :notes>,
        notifications: #Ash.NotLoaded<:relationship, field: :notifications>,
        unread_topics: #Ash.NotLoaded<:relationship, field: :unread_topics>,
        conversation_participants: #Ash.NotLoaded<:relationship, field: :conversation_participants>,
        member_tags: #Ash.NotLoaded<:relationship, field: :member_tags>,
        notification_settings: #Ash.NotLoaded<:relationship, field: :notification_settings>,
        topic_view: #Ash.NotLoaded<:relationship, field: :topic_view>,
        profile: #Ash.NotLoaded<:relationship, field: :profile>,
        membership_plan: #Ash.NotLoaded<:relationship, field: :membership_plan>,
        linked_account: #Ash.NotLoaded<:relationship, field: :linked_account>,
        group: #Ash.NotLoaded<:relationship, field: :group>,
        __meta__: #Ecto.Schema.Metadata<:loaded, "public", "members">,
        account_confirmed_at: ~U[2025-03-03 22:21:04Z],
        auth_code: nil,
        auth_code_expires_at: nil,
        auto_renews: false,
        banned_datetime: nil,
        customer_id: nil,
        developer_access: false,
        dues_exempt: false,
        dues_variant_id: nil,
        email: "[email protected]",
        email_confirmed_at: nil,
        emails_enabled: true,
        emails_paused_until: nil,
        group_id: 617093,
        id: "OBdTzV7r2esI6r9",
        joined_group_on: ~D[2025-01-02],
        is_sample: false,
        linked_account_id: nil,
        meetup_member_id: 1741040464,
        member_type: :participant,
        membership_ends_on: ~D[2026-01-02],
        membership_plan_id: nil,
        ...
      >,
      authorize?: true,
      tenant: nil,
      in_before_action?: true,
      pre_flight_authorization?: false
    },
    action: %Ash.Resource.Actions.Read{
      arguments: [],
      description: nil,
      filter: nil,
      filters: [],
      get_by: [],
      get?: false,
      manual: nil,
      metadata: [],
      skip_unknown_inputs: [],
      modify_query: nil,
      multitenancy: :enforce,
      name: :read,
      pagination: %Ash.Resource.Actions.Read.Pagination{
        default_limit: nil,
        max_page_size: 250,
        countable: true,
        required?: false,
        keyset?: true,
        offset?: true
      },
      preparations: [],
      primary?: true,
      touches_resources: [],
      timeout: nil,
      transaction?: false,
      type: :read
    }
  },
  bindings: %{0 => %{type: :root, path: [], source: GF.Messaging.Participant2}},
  resource: GF.Messaging.Participant2,
  sql_behaviour: AshPostgres.SqlImplementation,
  calculations: %{},
  parent_resources: [],
  aggregate_names: %{},
  in_group?: false,
  expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
  current: 1,
  root_binding: 0,
  calculation_names: %{},
  current_calculation_name: :calculation_0,
  aggregate_defs: %{},
  current_aggregate_name: :aggregate_0,
  lateral_join?: false
}

[(ash_sql 0.2.59) lib/expr.ex:2871: AshSql.Expr.maybe_type_expr/6]
embedded? #=> false

[(ash_sql 0.2.59) lib/expr.ex:2872: AshSql.Expr.maybe_type_expr/6]
acc #=> %AshSql.Expr.ExprInfo{has_error?: false}

[(ash_sql 0.2.59) lib/expr.ex:2873: AshSql.Expr.maybe_type_expr/6]
type #=> {Ash.Type.Integer, []}

[(ash_sql 0.2.59) lib/expr.ex:2868: AshSql.Expr.maybe_type_expr/6]
query #=> #Ecto.Query<from p0 in GF.Messaging.Participant2, as: 0,
 select: struct(p0, [
  :id,
  :member_id,
  :last_read_message_id,
  :conversation_id,
  :last_notified_message_id,
  :silence_until
])>

[(ash_sql 0.2.59) lib/expr.ex:2869: AshSql.Expr.maybe_type_expr/6]
other #=> id

[(ash_sql 0.2.59) lib/expr.ex:2870: AshSql.Expr.maybe_type_expr/6]
bindings #=> %{
  sort: [],
  domain: GF.Domain,
  context: %{
    private: %{actor: nil, authorize?: false, tenant: nil},
    action: %Ash.Resource.Actions.Read{
      arguments: [],
      description: nil,
      filter: nil,
      filters: [],
      get_by: [],
      get?: false,
      manual: nil,
      metadata: [],
      skip_unknown_inputs: [],
      modify_query: nil,
      multitenancy: :enforce,
      name: :read,
      pagination: %Ash.Resource.Actions.Read.Pagination{
        default_limit: nil,
        max_page_size: 250,
        countable: true,
        required?: false,
        keyset?: true,
        offset?: true
      },
      preparations: [],
      primary?: true,
      touches_resources: [],
      timeout: nil,
      transaction?: false,
      type: :read
    },
    data_layer: %{
      data: %{
        GF.Messaging.Participant2 => [
          #GF.Messaging.Participant2<
            unread_messages_count: #Ash.NotLoaded<:calculation, field: :unread_messages_count>,
            unread_messages_count2: #Ash.NotLoaded<:aggregate, field: :unread_messages_count2>,
            last_read_message: #Ash.NotLoaded<:relationship, field: :last_read_message>,
            member: #Ash.NotLoaded<:relationship, field: :member>,
            conversation: #Ash.NotLoaded<:relationship, field: :conversation>,
            __meta__: #Ecto.Schema.Metadata<:loaded, "messaging", "participants">,
            id: 115318,
            conversation_id: "7K6ggMfHOMwvrMv",
            member_id: "OBdTzV7r2esI6r9",
            last_notified_message_id: nil,
            last_read_message_id: nil,
            silence_until: nil,
            aggregates: %{},
            calculations: %{},
            ...
          >
        ]
      }
    }
  },
  bindings: %{0 => %{type: :root, path: [], source: GF.Messaging.Participant2}},
  resource: GF.Messaging.Participant2,
  sql_behaviour: AshPostgres.SqlImplementation,
  calculations: %{},
  parent_resources: [],
  aggregate_names: %{},
  in_group?: false,
  expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
  current: 1,
  root_binding: 0,
  calculation_names: %{},
  current_calculation_name: :calculation_0,
  aggregate_defs: %{},
  current_aggregate_name: :aggregate_0,
  lateral_join?: false
}

[(ash_sql 0.2.59) lib/expr.ex:2871: AshSql.Expr.maybe_type_expr/6]
embedded? #=> false

[(ash_sql 0.2.59) lib/expr.ex:2872: AshSql.Expr.maybe_type_expr/6]
acc #=> %AshSql.Expr.ExprInfo{has_error?: false}

[(ash_sql 0.2.59) lib/expr.ex:2873: AshSql.Expr.maybe_type_expr/6]
type #=> {Ash.Type.Integer, []}

[(ash_sql 0.2.59) lib/expr.ex:2868: AshSql.Expr.maybe_type_expr/6]
query #=> #Ecto.Query<from p0 in GF.Messaging.Participant2, as: 0,
 select: struct(p0, [
  :id,
  :member_id,
  :last_read_message_id,
  :conversation_id,
  :last_notified_message_id,
  :silence_until
])>

[(ash_sql 0.2.59) lib/expr.ex:2869: AshSql.Expr.maybe_type_expr/6]
other #=> 115318

[(ash_sql 0.2.59) lib/expr.ex:2870: AshSql.Expr.maybe_type_expr/6]
bindings #=> %{
  sort: [],
  domain: GF.Domain,
  context: %{
    private: %{actor: nil, authorize?: false, tenant: nil},
    action: %Ash.Resource.Actions.Read{
      arguments: [],
      description: nil,
      filter: nil,
      filters: [],
      get_by: [],
      get?: false,
      manual: nil,
      metadata: [],
      skip_unknown_inputs: [],
      modify_query: nil,
      multitenancy: :enforce,
      name: :read,
      pagination: %Ash.Resource.Actions.Read.Pagination{
        default_limit: nil,
        max_page_size: 250,
        countable: true,
        required?: false,
        keyset?: true,
        offset?: true
      },
      preparations: [],
      primary?: true,
      touches_resources: [],
      timeout: nil,
      transaction?: false,
      type: :read
    },
    data_layer: %{
      data: %{
        GF.Messaging.Participant2 => [
          #GF.Messaging.Participant2<
            unread_messages_count: #Ash.NotLoaded<:calculation, field: :unread_messages_count>,
            unread_messages_count2: #Ash.NotLoaded<:aggregate, field: :unread_messages_count2>,
            last_read_message: #Ash.NotLoaded<:relationship, field: :last_read_message>,
            member: #Ash.NotLoaded<:relationship, field: :member>,
            conversation: #Ash.NotLoaded<:relationship, field: :conversation>,
            __meta__: #Ecto.Schema.Metadata<:loaded, "messaging", "participants">,
            id: 115318,
            conversation_id: "7K6ggMfHOMwvrMv",
            member_id: "OBdTzV7r2esI6r9",
            last_notified_message_id: nil,
            last_read_message_id: nil,
            silence_until: nil,
            aggregates: %{},
            calculations: %{},
            ...
          >
        ]
      }
    }
  },
  bindings: %{0 => %{type: :root, path: [], source: GF.Messaging.Participant2}},
  resource: GF.Messaging.Participant2,
  sql_behaviour: AshPostgres.SqlImplementation,
  calculations: %{},
  parent_resources: [],
  aggregate_names: %{},
  in_group?: false,
  expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
  current: 1,
  root_binding: 0,
  calculation_names: %{},
  current_calculation_name: :calculation_0,
  aggregate_defs: %{},
  current_aggregate_name: :aggregate_0,
  lateral_join?: false
}

[(ash_sql 0.2.59) lib/expr.ex:2871: AshSql.Expr.maybe_type_expr/6]
embedded? #=> false

[(ash_sql 0.2.59) lib/expr.ex:2872: AshSql.Expr.maybe_type_expr/6]
acc #=> %AshSql.Expr.ExprInfo{has_error?: false}

[(ash_sql 0.2.59) lib/expr.ex:2873: AshSql.Expr.maybe_type_expr/6]
type #=> {Ash.Type.Integer, []}

[(ash_sql 0.2.59) lib/expr.ex:2868: AshSql.Expr.maybe_type_expr/6]
query #=> #Ecto.Query<from m0 in GF.Messaging.Message2, as: 0, select: %{}>

[(ash_sql 0.2.59) lib/expr.ex:2869: AshSql.Expr.maybe_type_expr/6]
other #=> conversation_id

[(ash_sql 0.2.59) lib/expr.ex:2870: AshSql.Expr.maybe_type_expr/6]
bindings #=> %{
  sort: [],
  domain: GF.Domain,
  context: %{
    private: %{actor: nil, authorize?: false, tenant: nil},
    action: nil
  },
  bindings: %{0 => %{type: :root, path: [], source: GF.Messaging.Message2}},
  resource: GF.Messaging.Message2,
  sql_behaviour: AshPostgres.SqlImplementation,
  calculations: %{},
  parent_resources: [],
  aggregate_names: %{},
  in_group?: false,
  expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
  current: 1,
  root_binding: 0,
  calculation_names: %{},
  current_calculation_name: :calculation_0,
  aggregate_defs: %{},
  current_aggregate_name: :aggregate_0,
  lateral_join?: false,
  sort_applied?: true
}

[(ash_sql 0.2.59) lib/expr.ex:2871: AshSql.Expr.maybe_type_expr/6]
embedded? #=> false

[(ash_sql 0.2.59) lib/expr.ex:2872: AshSql.Expr.maybe_type_expr/6]
acc #=> %AshSql.Expr.ExprInfo{has_error?: false}

[(ash_sql 0.2.59) lib/expr.ex:2873: AshSql.Expr.maybe_type_expr/6]
type #=> {Ash.Type.String, [trim?: true, allow_empty?: false]}

[(ash_sql 0.2.59) lib/expr.ex:2868: AshSql.Expr.maybe_type_expr/6]
query #=> #Ecto.Query<from m0 in GF.Messaging.Message2, as: 0, select: %{}>

[(ash_sql 0.2.59) lib/expr.ex:2869: AshSql.Expr.maybe_type_expr/6]
other #=> "7K6ggMfHOMwvrMv"

[(ash_sql 0.2.59) lib/expr.ex:2870: AshSql.Expr.maybe_type_expr/6]
bindings #=> %{
  sort: [],
  domain: GF.Domain,
  context: %{
    private: %{actor: nil, authorize?: false, tenant: nil},
    action: nil
  },
  bindings: %{0 => %{type: :root, path: [], source: GF.Messaging.Message2}},
  resource: GF.Messaging.Message2,
  sql_behaviour: AshPostgres.SqlImplementation,
  calculations: %{},
  parent_resources: [],
  aggregate_names: %{},
  in_group?: false,
  expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
  current: 1,
  root_binding: 0,
  calculation_names: %{},
  current_calculation_name: :calculation_0,
  aggregate_defs: %{},
  current_aggregate_name: :aggregate_0,
  lateral_join?: false,
  sort_applied?: true
}

[(ash_sql 0.2.59) lib/expr.ex:2871: AshSql.Expr.maybe_type_expr/6]
embedded? #=> false

[(ash_sql 0.2.59) lib/expr.ex:2872: AshSql.Expr.maybe_type_expr/6]
acc #=> %AshSql.Expr.ExprInfo{has_error?: false}

[(ash_sql 0.2.59) lib/expr.ex:2873: AshSql.Expr.maybe_type_expr/6]
type #=> {Ash.Type.String, [trim?: true, allow_empty?: false]}

[(ash_sql 0.2.59) lib/expr.ex:2868: AshSql.Expr.maybe_type_expr/6]
query #=> #Ecto.Query<from c0 in GF.Messaging.Conversation2, as: 1,
 join: m1 in ^#Ecto.Query<from m0 in GF.Messaging.Message2, as: 0>, as: 2,
 on: as(1).id == m1.conversation_id,
 where: parent_as(0).conversation_id == as(1).id, group_by: [c0.id],
 select: %{id: c0.id}>

[(ash_sql 0.2.59) lib/expr.ex:2869: AshSql.Expr.maybe_type_expr/6]
other #=> messages.inserted_at

[(ash_sql 0.2.59) lib/expr.ex:2870: AshSql.Expr.maybe_type_expr/6]
bindings #=> %{
  sort: [],
  domain: GF.Domain,
  context: %{
    private: %{
      actor: nil,
      authorize?: false,
      tenant: nil,
      in_before_action?: true
    },
    action: %Ash.Resource.Actions.Read{
      arguments: [],
      description: nil,
      filter: nil,
      filters: [],
      get_by: [],
      get?: false,
      manual: nil,
      metadata: [],
      skip_unknown_inputs: [],
      modify_query: nil,
      multitenancy: :enforce,
      name: :read,
      pagination: %Ash.Resource.Actions.Read.Pagination{
        default_limit: nil,
        max_page_size: 250,
        countable: true,
        required?: false,
        keyset?: true,
        offset?: true
      },
      preparations: [],
      primary?: true,
      touches_resources: [],
      timeout: nil,
      transaction?: false,
      type: :read
    },
    data_layer: %{
      table: nil,
      parent_bindings: %{
        sort: [],
        domain: GF.Domain,
        context: %{
          private: %{
            actor: nil,
            authorize?: false,
            tenant: nil,
            in_before_action?: true
          },
          action: %Ash.Resource.Actions.Read{
            arguments: [],
            description: nil,
            filter: nil,
            filters: [],
            get_by: [],
            get?: false,
            manual: nil,
            metadata: [],
            skip_unknown_inputs: [],
            modify_query: nil,
            multitenancy: :enforce,
            name: :read,
            pagination: %Ash.Resource.Actions.Read.Pagination{
              default_limit: nil,
              max_page_size: 250,
              countable: true,
              required?: false,
              keyset?: true,
              offset?: true
            },
            preparations: [],
            primary?: true,
            touches_resources: [],
            timeout: nil,
            transaction?: false,
            type: :read
          }
        },
        bindings: %{
          0 => %{type: :root, path: [], source: GF.Messaging.Participant2}
        },
        resource: GF.Messaging.Participant2,
        sql_behaviour: AshPostgres.SqlImplementation,
        calculations: %{},
        parent_resources: [],
        aggregate_names: %{},
        in_group?: false,
        refs_at_path: [],
        expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
        current: 1,
        root_binding: 0,
        calculation_names: %{},
        current_calculation_name: :calculation_0,
        aggregate_defs: %{
          unread_messages_count2: #count<
            conversation.messages from #Ash.Query<resource: GF.Messaging.Message2,
             filter: #Ash.Filter<inserted_at == parent(last_read_message.inserted_at)>>
          >
        },
        current_aggregate_name: :aggregate_0,
        lateral_join?: false
      },
      start_bindings_at: 1,
      in_group?: false,
      has_parent_expr?: false
    }
  },
  bindings: %{
    1 => %{type: :root, path: [], source: GF.Messaging.Conversation2},
    2 => %{type: :inner, path: [:messages], source: GF.Messaging.Conversation2}
  },
  resource: GF.Messaging.Conversation2,
  sql_behaviour: AshPostgres.SqlImplementation,
  calculations: %{},
  parent_resources: [GF.Messaging.Participant2],
  aggregate_names: %{},
  parent_bindings: %{
    sort: [],
    domain: GF.Domain,
    context: %{
      private: %{
        actor: nil,
        authorize?: false,
        tenant: nil,
        in_before_action?: true
      },
      action: %Ash.Resource.Actions.Read{
        arguments: [],
        description: nil,
        filter: nil,
        filters: [],
        get_by: [],
        get?: false,
        manual: nil,
        metadata: [],
        skip_unknown_inputs: [],
        modify_query: nil,
        multitenancy: :enforce,
        name: :read,
        pagination: %Ash.Resource.Actions.Read.Pagination{
          default_limit: nil,
          max_page_size: 250,
          countable: true,
          required?: false,
          keyset?: true,
          offset?: true
        },
        preparations: [],
        primary?: true,
        touches_resources: [],
        timeout: nil,
        transaction?: false,
        type: :read
      }
    },
    bindings: %{
      0 => %{type: :root, path: [], source: GF.Messaging.Participant2}
    },
    resource: GF.Messaging.Participant2,
    sql_behaviour: AshPostgres.SqlImplementation,
    calculations: %{},
    parent_resources: [],
    aggregate_names: %{},
    parent?: true,
    in_group?: false,
    refs_at_path: [],
    expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
    current: 1,
    root_binding: 0,
    calculation_names: %{},
    current_calculation_name: :calculation_0,
    aggregate_defs: %{
      unread_messages_count2: #count<
        conversation.messages from #Ash.Query<resource: GF.Messaging.Message2,
         filter: #Ash.Filter<inserted_at == parent(last_read_message.inserted_at)>>
      >
    },
    current_aggregate_name: :aggregate_0,
    lateral_join?: false
  },
  in_group?: true,
  expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
  current: 3,
  root_binding: 1,
  calculation_names: %{},
  current_calculation_name: :calculation_0,
  aggregate_defs: %{},
  current_aggregate_name: :aggregate_0,
  lateral_join?: false,
  sort_applied?: true
}

[(ash_sql 0.2.59) lib/expr.ex:2871: AshSql.Expr.maybe_type_expr/6]
embedded? #=> false

[(ash_sql 0.2.59) lib/expr.ex:2872: AshSql.Expr.maybe_type_expr/6]
acc #=> %AshSql.Expr.ExprInfo{has_error?: false}

[(ash_sql 0.2.59) lib/expr.ex:2873: AshSql.Expr.maybe_type_expr/6]
type #=> {Ash.Type.UtcDatetimeUsec,
 [precision: :microsecond, cast_dates_as: :start_of_day, timezone: :utc]}

[(ash_sql 0.2.59) lib/expr.ex:2868: AshSql.Expr.maybe_type_expr/6]
query #=> #Ecto.Query<from c0 in GF.Messaging.Conversation2, as: 1,
 join: m1 in ^#Ecto.Query<from m0 in GF.Messaging.Message2, as: 0>, as: 2,
 on: as(1).id == m1.conversation_id,
 where: parent_as(0).conversation_id == as(1).id, group_by: [c0.id],
 select: %{id: c0.id}>

[(ash_sql 0.2.59) lib/expr.ex:2869: AshSql.Expr.maybe_type_expr/6]
other #=> parent(last_read_message.inserted_at)

[(ash_sql 0.2.59) lib/expr.ex:2870: AshSql.Expr.maybe_type_expr/6]
bindings #=> %{
  sort: [],
  domain: GF.Domain,
  context: %{
    private: %{
      actor: nil,
      authorize?: false,
      tenant: nil,
      in_before_action?: true
    },
    action: %Ash.Resource.Actions.Read{
      arguments: [],
      description: nil,
      filter: nil,
      filters: [],
      get_by: [],
      get?: false,
      manual: nil,
      metadata: [],
      skip_unknown_inputs: [],
      modify_query: nil,
      multitenancy: :enforce,
      name: :read,
      pagination: %Ash.Resource.Actions.Read.Pagination{
        default_limit: nil,
        max_page_size: 250,
        countable: true,
        required?: false,
        keyset?: true,
        offset?: true
      },
      preparations: [],
      primary?: true,
      touches_resources: [],
      timeout: nil,
      transaction?: false,
      type: :read
    },
    data_layer: %{
      table: nil,
      parent_bindings: %{
        sort: [],
        domain: GF.Domain,
        context: %{
          private: %{
            actor: nil,
            authorize?: false,
            tenant: nil,
            in_before_action?: true
          },
          action: %Ash.Resource.Actions.Read{
            arguments: [],
            description: nil,
            filter: nil,
            filters: [],
            get_by: [],
            get?: false,
            manual: nil,
            metadata: [],
            skip_unknown_inputs: [],
            modify_query: nil,
            multitenancy: :enforce,
            name: :read,
            pagination: %Ash.Resource.Actions.Read.Pagination{
              default_limit: nil,
              max_page_size: 250,
              countable: true,
              required?: false,
              keyset?: true,
              offset?: true
            },
            preparations: [],
            primary?: true,
            touches_resources: [],
            timeout: nil,
            transaction?: false,
            type: :read
          }
        },
        bindings: %{
          0 => %{type: :root, path: [], source: GF.Messaging.Participant2}
        },
        resource: GF.Messaging.Participant2,
        sql_behaviour: AshPostgres.SqlImplementation,
        calculations: %{},
        parent_resources: [],
        aggregate_names: %{},
        in_group?: false,
        refs_at_path: [],
        expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
        current: 1,
        root_binding: 0,
        calculation_names: %{},
        current_calculation_name: :calculation_0,
        aggregate_defs: %{
          unread_messages_count2: #count<
            conversation.messages from #Ash.Query<resource: GF.Messaging.Message2,
             filter: #Ash.Filter<inserted_at == parent(last_read_message.inserted_at)>>
          >
        },
        current_aggregate_name: :aggregate_0,
        lateral_join?: false
      },
      start_bindings_at: 1,
      in_group?: false,
      has_parent_expr?: false
    }
  },
  bindings: %{
    1 => %{type: :root, path: [], source: GF.Messaging.Conversation2},
    2 => %{type: :inner, path: [:messages], source: GF.Messaging.Conversation2}
  },
  resource: GF.Messaging.Conversation2,
  sql_behaviour: AshPostgres.SqlImplementation,
  calculations: %{},
  parent_resources: [GF.Messaging.Participant2],
  aggregate_names: %{},
  parent_bindings: %{
    sort: [],
    domain: GF.Domain,
    context: %{
      private: %{
        actor: nil,
        authorize?: false,
        tenant: nil,
        in_before_action?: true
      },
      action: %Ash.Resource.Actions.Read{
        arguments: [],
        description: nil,
        filter: nil,
        filters: [],
        get_by: [],
        get?: false,
        manual: nil,
        metadata: [],
        skip_unknown_inputs: [],
        modify_query: nil,
        multitenancy: :enforce,
        name: :read,
        pagination: %Ash.Resource.Actions.Read.Pagination{
          default_limit: nil,
          max_page_size: 250,
          countable: true,
          required?: false,
          keyset?: true,
          offset?: true
        },
        preparations: [],
        primary?: true,
        touches_resources: [],
        timeout: nil,
        transaction?: false,
        type: :read
      }
    },
    bindings: %{
      0 => %{type: :root, path: [], source: GF.Messaging.Participant2}
    },
    resource: GF.Messaging.Participant2,
    sql_behaviour: AshPostgres.SqlImplementation,
    calculations: %{},
    parent_resources: [],
    aggregate_names: %{},
    parent?: true,
    in_group?: false,
    refs_at_path: [],
    expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
    current: 1,
    root_binding: 0,
    calculation_names: %{},
    current_calculation_name: :calculation_0,
    aggregate_defs: %{
      unread_messages_count2: #count<
        conversation.messages from #Ash.Query<resource: GF.Messaging.Message2,
         filter: #Ash.Filter<inserted_at == parent(last_read_message.inserted_at)>>
      >
    },
    current_aggregate_name: :aggregate_0,
    lateral_join?: false
  },
  in_group?: true,
  expression_accumulator: %AshSql.Expr.ExprInfo{has_error?: false},
  current: 3,
  root_binding: 1,
  calculation_names: %{},
  current_calculation_name: :calculation_0,
  aggregate_defs: %{},
  current_aggregate_name: :aggregate_0,
  lateral_join?: false,
  sort_applied?: true
}

[(ash_sql 0.2.59) lib/expr.ex:2871: AshSql.Expr.maybe_type_expr/6]
embedded? #=> false

[(ash_sql 0.2.59) lib/expr.ex:2872: AshSql.Expr.maybe_type_expr/6]
acc #=> %AshSql.Expr.ExprInfo{has_error?: false}

[(ash_sql 0.2.59) lib/expr.ex:2873: AshSql.Expr.maybe_type_expr/6]
type #=> {Ash.Type.UtcDatetimeUsec,
 [precision: :microsecond, cast_dates_as: :start_of_day, timezone: :utc]}



  1) test messages_count/1 returns the correct count of messages for a participant (GF.Messaging.Participant2Test)
     test/gf/messaging/participant2_test.exs:13
     ** (Ash.Error.Unknown) 
     Bread Crumbs:
       > Exception raised in: GF.Messaging.Participant2.read

     Unknown Error

     * ** (RuntimeError) Error while building reference: last_read_message.inserted_at
       (ash_sql 0.2.59) lib/expr.ex:1915: AshSql.Expr.default_dynamic_expr/6
       (ash_sql 0.2.59) lib/expr.ex:2874: AshSql.Expr.maybe_type_expr/6
       (ash_sql 0.2.59) lib/expr.ex:1006: AshSql.Expr.default_dynamic_expr/6
       (ash_sql 0.2.59) lib/filter.ex:37: anonymous fn/2 in AshSql.Filter.add_filter_expression/2
       (elixir 1.17.2) lib/enum.ex:2531: Enum."-reduce/3-lists^foldl/2-0-"/3
       (ash_sql 0.2.59) lib/filter.ex:22: AshSql.Filter.filter/4
       (ash_sql 0.2.59) lib/aggregate.ex:887: anonymous fn/6 in AshSql.Aggregate.maybe_filter_subquery/6
       (elixir 1.17.2) lib/enum.ex:4858: Enumerable.List.reduce/3
       (elixir 1.17.2) lib/enum.ex:2585: Enum.reduce_while/3
       (ash_sql 0.2.59) lib/aggregate.ex:369: anonymous fn/10 in AshSql.Aggregate.add_aggregates/6
       (ash_sql 0.2.59) lib/join.ex:290: AshSql.Join.related_subquery/3
       (ash_sql 0.2.59) lib/aggregate.ex:217: anonymous fn/7 in AshSql.Aggregate.add_aggregates/6
       (elixir 1.17.2) lib/enum.ex:4858: Enumerable.List.reduce/3
       (elixir 1.17.2) lib/enum.ex:2585: Enum.reduce_while/3
       (ash_sql 0.2.59) lib/aggregate.ex:88: AshSql.Aggregate.add_aggregates/6
       (ash 3.4.66) lib/ash/query/query.ex:3157: Ash.Query.data_layer_query/2
       (ash 3.4.66) lib/ash/actions/read/read.ex:595: anonymous fn/8 in Ash.Actions.Read.do_read/5
       (ash 3.4.66) lib/ash/actions/read/read.ex:956: Ash.Actions.Read.maybe_in_transaction/3
       (ash 3.4.66) lib/ash/actions/read/read.ex:315: Ash.Actions.Read.do_run/3
       (ash 3.4.66) lib/ash/actions/read/read.ex:82: anonymous fn/3 in Ash.Actions.Read.run/3
     stacktrace:
       (ash_sql 0.2.59) lib/expr.ex:1915: AshSql.Expr.default_dynamic_expr/6
       (ash_sql 0.2.59) lib/expr.ex:2874: AshSql.Expr.maybe_type_expr/6
       (ash_sql 0.2.59) lib/expr.ex:1006: AshSql.Expr.default_dynamic_expr/6
       (ash_sql 0.2.59) lib/filter.ex:37: anonymous fn/2 in AshSql.Filter.add_filter_expression/2
       (elixir 1.17.2) lib/enum.ex:2531: Enum."-reduce/3-lists^foldl/2-0-"/3
       (ash_sql 0.2.59) lib/filter.ex:22: AshSql.Filter.filter/4
       (ash_sql 0.2.59) lib/aggregate.ex:887: anonymous fn/6 in AshSql.Aggregate.maybe_filter_subquery/6
       (elixir 1.17.2) lib/enum.ex:4858: Enumerable.List.reduce/3
       (elixir 1.17.2) lib/enum.ex:2585: Enum.reduce_while/3
       (ash_sql 0.2.59) lib/aggregate.ex:369: anonymous fn/10 in AshSql.Aggregate.add_aggregates/6
       (ash_sql 0.2.59) lib/join.ex:290: AshSql.Join.related_subquery/3
       (ash_sql 0.2.59) lib/aggregate.ex:217: anonymous fn/7 in AshSql.Aggregate.add_aggregates/6
       (elixir 1.17.2) lib/enum.ex:4858: Enumerable.List.reduce/3
       (elixir 1.17.2) lib/enum.ex:2585: Enum.reduce_while/3
       (ash_sql 0.2.59) lib/aggregate.ex:88: AshSql.Aggregate.add_aggregates/6
       (ash 3.4.66) lib/ash/query/query.ex:3157: Ash.Query.data_layer_query/2
       (ash 3.4.66) lib/ash/actions/read/read.ex:595: anonymous fn/8 in Ash.Actions.Read.do_read/5
       (ash 3.4.66) lib/ash/actions/read/read.ex:956: Ash.Actions.Read.maybe_in_transaction/3
       (ash 3.4.66) lib/ash/actions/read/read.ex:315: Ash.Actions.Read.do_run/3
       (ash 3.4.66) lib/ash/actions/read/read.ex:82: anonymous fn/3 in Ash.Actions.Read.run/3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment