# `Chronicle.Projections`
[🔗](https://github.com/Cratis/Chronicle.Elixir/blob/main/lib/chronicle/projections.ex#L4)

Idiomatic Elixir interface for ad-hoc projection querying and waiting for a registered
projection to catch up.

`query/2` sends a Projection Declaration Language (PDL) string straight to the kernel and
returns the projected entries, without registering a permanent projection or defining a read
model module. `preview/2` does the same for an already-registered projection's own
declaration. Mirrors the C# client's `IProjections.Query()`.

:::caution
Every call replays events from the beginning of the event sequence, up to an internal kernel
limit. Use this for tooling, diagnostics and tests over small, bounded event volumes - not in
a high-throughput code path.
:::

## Ad-hoc querying

    {:ok, result} =
      Chronicle.Projections.query("""
      projection OrderSummary
        from OrderPlaced
      """)

    result.instances      # decoded as plain maps unless `:read_model` is given
    result.total_count

Pass `:read_model` to decode the entries as a struct instead of plain maps:

    {:ok, result} = Chronicle.Projections.query(declaration, read_model: MyApp.ReadModels.Order)

A declaration with syntax or type errors returns
`{:error, {:invalid_declaration, [%{message: ..., line: ..., column: ...}]}}`.

## Previewing a registered projection

    {:ok, result} = Chronicle.Projections.preview(MyApp.ReadModels.Order)

Looks up the declaration already registered for the read model (or projection identifier) and
runs it the same way `query/2` does. Returns `{:error, {:unknown_projection, id}}` when no
declaration is registered under that identifier.

## Waiting for a projection

    :ok = Chronicle.Projections.wait_for(MyApp.ReadModels.Order, sequence_number)

Polls the projection's observer state until it has handled at least `sequence_number`, or
returns `{:error, :timeout}`.

## Common options

  * `:client` — the client name (default: `Chronicle.Client`)
  * `:namespace` — overrides the client's configured namespace
  * `:event_sequence_id` — event sequence identifier (default: `"event-log"`)

# `preview`

```elixir
@spec preview(module() | String.t(), keyword()) ::
  {:ok, Chronicle.ReadModels.QueryResult.t()}
  | {:error, {:invalid_declaration, [map()]}}
  | {:error, {:unknown_projection, module() | String.t()}}
  | {:error, term()}
```

Looks up the declaration already registered for a read model or projection identifier, and
runs it through `query/2`.

Returns `{:error, {:unknown_projection, module_or_id}}` when no declaration is registered under
that identifier.

## Options

Accepts `:read_model` like `query/2` (defaults to `module_or_id` when it is a module), plus the
common options.

# `query`

```elixir
@spec query(String.t(), keyword()) ::
  {:ok, Chronicle.ReadModels.QueryResult.t()}
  | {:error, {:invalid_declaration, [map()]}}
  | {:error, term()}
```

Queries a PDL declaration against the event log without registering it.

The declaration may omit `=> ReadModelType`, in which case the read model schema is inferred
from the events it uses; an inferred declaration can never be saved as a permanent projection.

Returns `{:ok, %Chronicle.ReadModels.QueryResult{}}`, or
`{:error, {:invalid_declaration, [%{message: String.t(), line: integer(), column: integer()}]}}`
when the declaration has syntax or type errors.

## Options

  * `:read_model` — decode each entry as this struct instead of a plain map
  * (see module documentation for the common options)

# `wait_for`

```elixir
@spec wait_for(module() | String.t(), non_neg_integer(), keyword()) ::
  :ok | {:error, :timeout}
```

Waits until the projection that maintains `module_or_id` has handled at least
`sequence_number`, polling `Chronicle.Observers.get_all/1`.

Returns `:ok`, or `{:error, :timeout}` once `:timeout` (default `5000` ms)
elapses.

## Options

  * `:timeout` — maximum time to wait in milliseconds (default `5000`)
  * `:interval` — delay between polls in milliseconds (default `50`)
  * (see module documentation for the common options)

---

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