# `Gralkor.Search`
[🔗](https://github.com/elimydlarz/jido_gralkor/blob/main/lib/gralkor/search.ex#L1)

A search across registered memory Destinations.

With empty `destinations` and `lenses`, search reads episodes from every
accessible registered Destination and from direct capture and every Lens and Reflection writer.
The packaged `personal` Destination resolves only to the current
`operator_id`'s graph.

`destinations` selects graphs and `lenses` filters episode writers. Names are
ORed within either list and the two dimensions are ANDed together. Lens
filtering is valid for episode and fact results. Direct episodes expose
`content`, `source_description`, `source_kind`, and `writer: :direct` without
Lens or Reflection authorship. Historical unmarked episodes remain available
without invented authorship; historical `operator` Lens provenance is preserved
but never matched by a `personal-chat` Lens selector. A Lens-written episode exposes
`content`, `source_description`, and `lens`; a Reflection-written episode
exposes `artefact: %{id: id, payload: payload}`, `source_description`, and
its writer as `reflection`. Reflection episodes have no encoded `content`.
Naturally textual Lens content remains text, including JSON-looking source
records. Facts retain their text, timestamps, and structured `sources` with named Lens or
Reflection provenance where available. Lens selection filters matching edges before
the result limit and retains only selected Lens sources. Use
`Gralkor.Format.format_fact/1` explicitly for readable fact presentation.

Episodes are the default result type. Facts, nodes, and Reflection artefacts
remain available by explicitly setting `result_type` to `:facts`, `:nodes`,
or `:artefacts`.

# `direct_episode`

```elixir
@type direct_episode() :: %{
  :content =&gt; String.t(),
  :source_description =&gt; String.t(),
  optional(:source_kind) =&gt; String.t(),
  optional(:writer) =&gt; :direct
}
```

# `lens_episode`

```elixir
@type lens_episode() :: %{
  content: String.t(),
  source_description: String.t(),
  lens: String.t()
}
```

# `reflection_episode`

```elixir
@type reflection_episode() :: %{
  artefact: %{id: String.t(), payload: map()},
  source_description: String.t(),
  reflection: String.t()
}
```

# `result`

```elixir
@type result() ::
  %{
    destination: String.t(),
    episode: direct_episode() | lens_episode() | reflection_episode()
  }
  | %{destination: String.t(), fact: map()}
  | %{destination: String.t(), node: map()}
  | %{destination: String.t(), artefact: Gralkor.Artefact.t()}
```

# `result_type`

```elixir
@type result_type() :: :facts | :nodes | :episodes | :artefacts
```

The requested representation; episodes are the public default.

# `t`

```elixir
@type t() :: %Gralkor.Search{
  artefact_id: String.t() | nil,
  destinations: [String.t()],
  edge_types: [String.t()],
  entity_types: [String.t()],
  lenses: [String.t()],
  max_results: pos_integer(),
  operator_id: String.t(),
  query: String.t(),
  result_type: result_type()
}
```

One search request.

Empty selector lists mean all accessible Destinations and all episode
writers. `entity_types`, `edge_types`, and `artefact_id` apply only to their
corresponding explicit advanced result types. `max_results` applies
independently to each selected Destination.

---

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