# `Sourced.EventStore.Query`
[🔗](https://gitlab.com/dimakula/sourced/-/blob/0.2.0/lib/sourced/event_store/query.ex#L1)

Describes which events a store should match.

A query is a plain list of items, as described by the
[DCB specification](https://dcb.events/topics/tags/). Each item is a map with
an optional `:types` and an optional `:tags` key. An event matches the query
when it matches **any** of the items (OR). Within an item, the event's type
must be one of the item's `types` (OR) **and** the event's tags must contain
**all** of the item's `tags` (AND). An omitted or empty list places no
constraint on that dimension, so an item with only `tags` matches them across
every event type, and one with only `types` matches those types whatever they
are tagged with.

An empty query (`[]`) matches every event.

Tags are what makes this work: rather than querying into the payload, events
carry `t:Sourced.StoredEvent.tag/0`s naming the domain concepts they concern,
and items match on those. See `Sourced.StoredEvent` for the tag conventions and
`Sourced.Middleware.Domain.Event` for deriving them from domain structs.

A query is a *matcher* and nothing else. Bounding a result to a range of
sequences, or to a number of events, is a property of a particular read rather
than of the set of events being described, so `:from`, `:to` and `:limit` are
options of `c:Sourced.EventStore.Behaviour.query/2` instead. The same matcher
can then be reused for the read, for the append that follows it, and for a
subscription, without one use silently inheriting another's bounds.

## Examples

    # Every OrderPlaced or OrderShipped tagged for order 1, plus every event
    # of any type tagged for customer 42.
    [
      %{types: ["OrderPlaced", "OrderShipped"], tags: ["order:1"]},
      %{tags: ["customer:42"]}
    ]

Note that the performance of any given query will be dependent on the event
store and its implementation details.

# `item`

```elixir
@type item() :: %{
  optional(:types) =&gt; [type()],
  optional(:tags) =&gt; [Sourced.StoredEvent.tag()]
}
```

A single item of a query, constraining event types, tags, or both.

# `t`

```elixir
@type t() :: [item()]
```

# `type`

```elixir
@type type() :: String.t() | atom() | module()
```

An event type criterion: a stored type string (`"order_placed"`), an atom
shorthand for one (`:order_placed`), or a domain event module
(`OrderPlaced`). Criteria are kept as given; atoms are resolved to their
stored type by the `Sourced.Middleware.Domain` middleware.

# `apply`

```elixir
@spec apply([Sourced.StoredEvent.t()], t() | nil) :: [Sourced.StoredEvent.t()]
```

Filters `events` down to the ones matching `query`.

Intended for event store implementations that load events into memory before
matching them. Bounding the result is separate; see
`c:Sourced.EventStore.Behaviour.query/2`.

# `empty?`

```elixir
@spec empty?(t() | nil) :: boolean()
```

Returns `true` if `query` has no items, i.e. it matches every event.

## Examples

    iex> Query.empty?([])
    true

    iex> Query.empty?(nil)
    true

    iex> Query.empty?([%{tags: ["order:1"]}])
    false

# `matches?`

```elixir
@spec matches?(t() | nil, Sourced.StoredEvent.t()) :: boolean()
```

Returns `true` if `event` matches the query.

An empty query matches every event; otherwise the event must match at least
one item.

Matching is exact: criteria are compared against `event.type` as-is, so an
item holding event modules matches events whose `type` is a module — which is
what `Sourced.Middleware.Domain` restores it to on the way out — while one
holding stored type strings matches raw events.

---

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