# `Selecto.DynamicJoin`

Handles dynamic join additions at runtime.

This module enables adding joins to a Selecto query that aren't defined
in the domain configuration. This is useful for:

- Ad-hoc queries that need joins beyond the preconfigured domain
- Programmatic query building where joins depend on runtime conditions
- Parameterized joins where the same association needs different filters

## Examples

    # Enable a predefined join from the domain
    selecto |> Selecto.join(:category)

    # Enable a join with specific type override
    selecto |> Selecto.join(:orders, type: :inner)

    # Add a parameterized instance of a join
    selecto |> Selecto.join_parameterize(:products, "electronics", category: "Electronics")

    # Add a custom join not in the domain
    selecto |> Selecto.join(:audit_log,
      source: "audit_logs",
      on: [%{left: "id", right: "record_id"}],
      type: :left
    )

# `join`

```elixir
@spec join(Selecto.t(), atom() | String.t(), keyword()) :: Selecto.t()
```

Enable a join from the domain configuration or add a custom join.

## Parameters

- `selecto` - The Selecto struct
- `join_id` - The join identifier (atom)
- `options` - Optional configuration overrides

## Options

- `:type` - Join type (:left, :inner, :right, :full). Default: :left
- `:source` - Source table name (required for custom joins)
- `:on` - Join conditions as list of maps with :left and :right keys
- `:owner_key` - The key on the parent table
- `:related_key` - The key on the joined table
- `:fields` - Map of field configurations to expose from the joined table

## Examples

    # Enable domain-configured join
    selecto |> Selecto.DynamicJoin.join(:category)

    # Custom join with explicit configuration
    selecto |> Selecto.DynamicJoin.join(:audit_log,
      source: "audit_logs",
      on: [%{left: "id", right: "record_id"}],
      type: :left,
      fields: %{
        action: %{type: :string},
        created_at: %{type: :naive_datetime}
      }
    )

# `join_parameterize`

```elixir
@spec join_parameterize(Selecto.t(), atom(), String.t() | atom(), keyword()) ::
  Selecto.t()
```

Create a parameterized instance of an existing join.

Parameterized joins allow the same association to be joined multiple times
with different filter conditions, useful for queries like:

- "Products in Electronics category" vs "Products in Clothing category"
- "Active orders" vs "Cancelled orders"
- "Primary contacts" vs "Billing contacts"

## Parameters

- `selecto` - The Selecto struct
- `join_id` - Base join identifier to parameterize
- `parameter` - Unique parameter value to identify this instance
- `options` - Filter conditions and options

## Options

- All options from `join/3` plus:
- Any key that matches a filter in the join's filter config will be applied

## Examples

    # Create parameterized join for electronics products
    selecto
    |> Selecto.DynamicJoin.join_parameterize(:products, "electronics",
        category_id: 1,
        active: true
      )
    |> Selecto.select(["products:electronics.product_name"])

    # Multiple parameterized instances
    selecto
    |> Selecto.DynamicJoin.join_parameterize(:orders, "active", status: "active")
    |> Selecto.DynamicJoin.join_parameterize(:orders, "completed", status: "completed")
    |> Selecto.select([
        "orders:active.total as active_total",
        "orders:completed.total as completed_total"
      ])

# `join_subquery`

```elixir
@spec join_subquery(Selecto.t(), atom(), Selecto.t(), keyword()) :: Selecto.t()
```

Join with another Selecto query as a subquery.

This creates a join using a separate Selecto query as the right side,
enabling complex subquery joins.

## Parameters

- `selecto` - The main Selecto struct
- `join_id` - Identifier for this join
- `join_selecto` - The Selecto struct to use as subquery
- `options` - Join configuration

## Options

- `:type` - Join type (:left, :inner, :right, :full). Default: :left
- `:on` - Join conditions referencing the subquery alias

## Examples

    # Create a subquery for aggregated data
    order_totals = Selecto.configure(order_domain, connection)
    |> Selecto.select(["customer_id", {:sum, "total", as: "total_spent"}])
    |> Selecto.group_by(["customer_id"])

    # Join to main query
    selecto
    |> Selecto.DynamicJoin.join_subquery(:customer_totals, order_totals,
        on: [%{left: "customer_id", right: "customer_id"}]
      )
    |> Selecto.select(["name", "customer_totals.total_spent"])

---

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