# `Selecto.Subselect`

Subselect functionality for array-based data aggregation from related tables.

The Subselect feature enables returning related data as arrays or JSON objects,
preventing result set denormalization while maintaining relational context.

## Examples

    # Basic subselect - get orders as JSON array for each attendee
    selecto
    |> Selecto.select(["attendee.name"])
    |> Selecto.subselect([
         "order.product_name", 
         "order.quantity"
       ])
    |> Selecto.filter([{"event_id", 123}])

    # This generates SQL like:
    # SELECT 
    #   a.name,
    #   (SELECT json_agg(json_build_object(
    #     'product_name', o.product_name,
    #     'quantity', o.quantity
    #   )) FROM orders o WHERE o.attendee_id = a.attendee_id) as orders
    # FROM attendees a
    # WHERE a.event_id = 123

## Aggregation Formats

- `:json_agg` - Returns JSON array of objects (default)
- `:array_agg` - Returns PostgreSQL array
- `:string_agg` - Returns delimited string
- `:count` - Returns count of related records

# `clear_subselects`

```elixir
@spec clear_subselects(Selecto.Types.t()) :: Selecto.Types.t()
```

Clear all subselect configurations from a Selecto query.

# `get_subselect_configs`

```elixir
@spec get_subselect_configs(Selecto.Types.t()) :: [Selecto.Types.subselect_selector()]
```

Get all subselect configurations from a Selecto query.

# `group_subselects_by_table`

```elixir
@spec group_subselects_by_table(Selecto.Types.t()) :: %{
  required(atom()) =&gt; [Selecto.Types.subselect_selector()]
}
```

Group subselects by their target table for efficient SQL generation.

# `has_subselects?`

```elixir
@spec has_subselects?(Selecto.Types.t()) :: boolean()
```

Check if a Selecto query has subselect configuration applied.

# `resolve_join_path`

```elixir
@spec resolve_join_path(Selecto.Types.t(), atom()) ::
  {:ok, [atom()]} | {:error, String.t()}
```

Resolve the join path needed to reach a target schema from the current context.

If we're in a retargeted context, the path is calculated from the retarget target.
Otherwise, the path is calculated from the source.

# `subselect`

```elixir
@spec subselect(
  Selecto.Types.t(),
  [String.t() | Selecto.Types.subselect_selector()],
  keyword()
) ::
  Selecto.Types.t()
```

Add subselect fields to return related data as aggregated arrays.

## Parameters

- `selecto` - The Selecto struct
- `field_specs` - List of field specifications with optional configuration
- `opts` - Global options for subselects

## Field Specification Formats

    # Simple field list (uses defaults)
    ["order.product_name", "order.quantity"]

    # With custom configuration
    [
      %{
        fields: ["product_name", "quantity"],
        target_schema: :order,
        format: :json_agg,
        alias: "order_items"
      }
    ]

## Options

- `:format` - Default aggregation format (`:json_agg`, `:array_agg`, `:string_agg`, `:count`)
- `:alias_prefix` - Prefix for generated field aliases
- `:order_by` - Default ordering for aggregated results

## Returns

Updated Selecto struct with subselect configuration applied.

# `validate_subselect_config`

```elixir
@spec validate_subselect_config(Selecto.Types.t(), Selecto.Types.subselect_selector()) ::
  :ok | {:error, String.t()}
```

Validate that a subselect configuration is valid for the given domain.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
