# `DrinkMeAlice.Graph`

Graph edges between records — SurrealDB's `RELATE` — with the same
honest write outcomes as `DrinkMeAlice.Tx`.

Edge tables are `TYPE RELATION` tables (DDL via
`DrinkMeAlice.Ash.Schema.edge_statements/4`, or by hand). A node is
`{table, key}` where `key` is a string or integer record key.

    {:committed, _} = Graph.relate(conn, "blocks", {"task", "a"}, {"task", "b"}, %{"why" => "needs api"})
    {:ok, ["b"]}    = Graph.outgoing(conn, {"task", "a"}, "blocks", "task")

Table and edge names must be identifiers (`DrinkMeAlice.identifier?/1`);
keys and field values travel as bound params. String keys and
top-level string field values travel exact (`DrinkMeAlice.exact_string/1` —
3.0.5 coerces date-, UUID- and record-shaped strings); nested field
values are canonical terms and are NOT protected from that coercion.
Field names must be identifiers.

# `node_ref`

```elixir
@type node_ref() :: {binary(), binary() | integer()}
```

# `incoming`

```elixir
@spec incoming(GenServer.server(), node_ref(), binary(), binary(), keyword()) ::
  {:ok, list()} | {:error, term()}
```

Keys of the `from_table` records with an `edge` INTO `to` (one incoming hop), bounded likewise.

# `outgoing`

```elixir
@spec outgoing(GenServer.server(), node_ref(), binary(), binary(), keyword()) ::
  {:ok, list()} | {:error, term()}
```

Keys of the `to_table` records reachable by one outgoing `edge` hop,
bounded at 1000 (more is `{:error, {:bounded_limit_exceeded, :neighbors}}`).

# `relate`

```elixir
@spec relate(GenServer.server(), binary(), node_ref(), node_ref(), map(), keyword()) ::
  DrinkMeAlice.Tx.outcome() | {:error, term()}
```

Create one edge `from -[edge]-> to`. Returns a `Tx.run/4` outcome;
`{:committed, %{"edge" => edge_row}}` on success.

# `unrelate`

```elixir
@spec unrelate(GenServer.server(), binary(), node_ref(), node_ref(), keyword()) ::
  DrinkMeAlice.Tx.outcome() | {:error, term()}
```

Delete every `from -[edge]-> to` edge. A `Tx.run/4` outcome.

---

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