Chronicle.Projections (cratis_chronicle v3.16.1)

Copy Markdown View Source

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")

Summary

Functions

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

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

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

Functions

preview(module_or_id, opts \\ [])

@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(declaration, opts \\ [])

@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(module_or_id, sequence_number, opts \\ [])

@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)