# `Clarity.Graph`
[🔗](https://github.com/team-alembic/clarity/blob/v0.5.1/lib/clarity/graph.ex#L1)

Manages the graph structure.

# `error`

```elixir
@type error() :: :subgraphs_are_readonly | :not_owner | :file.posix()
```

# `query`

```elixir
@type query() ::
  {:and, query(), query()}
  | {:or, query(), query()}
  | {:not, query()}
  | {:==, query_subject(), term()}
  | {:!=, query_subject(), term()}
  | {:in, query_subject(), [term()]}
  | boolean()
```

# `query_subject`

```elixir
@type query_subject() :: :vertex_type | :vertex_id | {:field, atom()}
```

# `result`

```elixir
@type result() :: :ok | {:error, error()}
```

# `result`

```elixir
@type result(inner) :: {:ok, inner} | {:error, error()}
```

# `t`

```elixir
@opaque t()
```

The Graph structure.

It is opaque and should be manipulated only via the provided functions.

# `add_edge`

```elixir
@spec add_edge(t(), Clarity.Vertex.t(), Clarity.Vertex.t(), :digraph.label()) ::
  result()
```

Adds an edge between two vertices.

# `add_vertex`

```elixir
@spec add_vertex(t(), Clarity.Vertex.t(), Clarity.Vertex.t()) :: result()
```

Adds a vertex.

# `available_vertex_types`

```elixir
@spec available_vertex_types(t()) :: [module()]
```

Gets all unique vertex types present in the graph.

Returns a list of modules representing the types of vertices in the graph.

# `breadcrumbs`

```elixir
@spec breadcrumbs(t(), Clarity.Vertex.t()) :: [Clarity.Vertex.t()] | false
```

Gets the shortest path between the root and the vertex.
Returns false if no path exists.

# `clear`

```elixir
@spec clear(t()) :: result()
```

Clears all vertices and edges from the graph.

Resets graphs to empty state with root vertex.

# `delete`

```elixir
@spec delete(t()) :: result()
```

Deletes Graph

# `edge`

```elixir
@spec edge(t(), :digraph.edge()) ::
  {:digraph.edge(), Clarity.Vertex.t() | nil, Clarity.Vertex.t() | nil,
   :digraph.label()}
  | false
```

Gets edge information for a given edge ID.

# `edges`

```elixir
@spec edges(t()) :: [:digraph.edge()]
```

Gets all edges IDs.

# `filter`

```elixir
@spec filter(t(), Clarity.Graph.Filter.filter() | [Clarity.Graph.Filter.filter()]) ::
  t()
```

Creates a filtered subgraph using a composable filter.
Returns a new Clarity.Graph instance with the filtered vertices and edges.

> #### Graph Memory Management {: .warning}
>
> Creating a subgraph will create multiple `:digraph` instances and `:ets`
> tables. While the main graph is managed by `Clarity`, any subgraphs
> created via this function must be explicitly deleted using `delete/1`
> when no longer needed to free up memory.

## Examples

    # Single filter
    subgraph = Graph.filter(graph, Filter.within_steps(vertex, 2, 1))

    # Multiple composed filters
    filter = Filter.all([
      Filter.within_steps(vertex, 2, 1),
      Filter.reachable_from([root_vertex])
    ])
    subgraph = Graph.filter(graph, filter)

# `get_short_path`

```elixir
@spec get_short_path(t(), Clarity.Vertex.t(), Clarity.Vertex.t()) ::
  [Clarity.Vertex.t()] | false
```

Gets the shortest path between two vertices.
Returns false if no path exists.

# `get_update_count`

```elixir
@spec get_update_count(t()) :: pos_integer()
```

Gets the current update count for the graph.

The count remains stable when no mutations occur and increases monotonically
when the graph is modified. Use this for change detection to invalidate
cached subgraphs.

Do not rely on specific count values or increment amounts as the internal
update mechanism may change.

## Example

    count1 = Graph.get_update_count(graph)
    # ... operations that might modify graph ...
    count2 = Graph.get_update_count(graph)
    
    if count2 > count1, do: # graph changed

# `get_vertex`

```elixir
@spec get_vertex(t(), String.t()) :: Clarity.Vertex.t() | nil
```

Looks up a vertex struct by its ID.

# `handover`

```elixir
@spec handover(t(), pid()) :: result(t())
```

Transfers ownership of all ETS tables to another process.

Used to hand over a loaded graph from the Cache process to the Server process.
Returns an updated graph struct with the new owner.

# `in_degree`

```elixir
@spec in_degree(t(), Clarity.Vertex.t()) :: non_neg_integer()
```

Gets the total in-degree for a vertex across all edge types.

# `in_degree`

```elixir
@spec in_degree(t(), Clarity.Vertex.t(), :digraph.label()) :: non_neg_integer()
```

Gets the in-degree for a vertex for a specific edge type.

# `in_edges`

```elixir
@spec in_edges(t(), Clarity.Vertex.t()) :: [:digraph.edge()]
```

Gets incoming edges for a vertex.

# `in_neighbors`

```elixir
@spec in_neighbors(t(), Clarity.Vertex.t()) :: [Clarity.Vertex.t()]
```

Gets all vertices that are direct sources of incoming edges to a vertex.

# `load`

```elixir
@spec load(Path.t()) :: result(t())
```

Loads a persisted graph from disk.

> #### Security Warning {: .warning}
>
> Only load trusted graphs. ETS tables can contain arbitrary terms including
> atoms and functions that may crash the VM if malicious.

Returns `{:error, posix}` on file errors (e.g., `:enoent`, `:eacces`).

# `navigation_children`

```elixir
@spec navigation_children(t(), Clarity.Vertex.t()) :: %{
  required(:digraph.label()) =&gt; [Clarity.Vertex.t()]
}
```

Gets the direct children of a vertex in the tree graph, grouped by edge label.

Returns a map where keys are edge labels and values are lists of child vertices,
sorted by vertex name for consistent ordering.

## Examples

    children = Graph.navigation_children(graph, root_vertex)
    # Returns: %{:application => [app1, app2], :module => [mod1]}

# `new`

```elixir
@spec new() :: t()
```

Creates a new graph.

# `out_degree`

```elixir
@spec out_degree(t(), Clarity.Vertex.t()) :: non_neg_integer()
```

Gets the total out-degree for a vertex across all edge types.

# `out_degree`

```elixir
@spec out_degree(t(), Clarity.Vertex.t(), :digraph.label()) :: non_neg_integer()
```

Gets the out-degree for a vertex for a specific edge type.

# `out_edges`

```elixir
@spec out_edges(t(), Clarity.Vertex.t()) :: [:digraph.edge()]
```

Gets outgoing edges for a vertex.

# `out_neighbors`

```elixir
@spec out_neighbors(t(), Clarity.Vertex.t()) :: [Clarity.Vertex.t()]
```

Gets all vertices that are direct targets of outgoing edges from a vertex.

# `pack_digraph`

```elixir
@spec pack_digraph(:ets.tid(), :ets.tid(), :ets.tid(), boolean()) :: :digraph.graph()
```

# `persist`

```elixir
@spec persist(t(), Path.t()) :: result()
```

Persists a graph to disk.

The graph must not be a subgraph.

Returns `{:error, posix}` on file errors (e.g., `:enoent`, `:eacces`, `:enospc`).

# `purge`

```elixir
@spec purge(t(), Clarity.Vertex.t()) :: result([Clarity.Vertex.t()])
```

Purges a vertex and all vertices that were caused by it.

# `unpack_digraph`

```elixir
@spec unpack_digraph(:digraph.graph()) ::
  {:ets.tid(), :ets.tid(), :ets.tid(), boolean()}
```

# `vertex_count`

```elixir
@spec vertex_count(t()) :: non_neg_integer()
```

Gets the total number of vertices.

# `vertices`

```elixir
@spec vertices(t(), query()) :: [Clarity.Vertex.t()]
```

Gets all vertices matching the query.

## Query Syntax

Queries support complex boolean expressions using operations, operators, and fields.

### Operations
- `{:and, query1, query2}` - Both queries must match
- `{:or, query1, query2}` - Either query must match
- `{:not, query}` - Query must not match

### Operators
- `{:==, field, value}` - Field equals value
- `{:!=, field, value}` - Field does not equal value
- `{:in, field, values}` - Field is in list of values

### Fields
- `:vertex_type` - The module of the vertex (e.g., `Application`, `Module`)
- `:vertex_id` - The unique ID string of the vertex
- `{:field, :field_name}` - A field within the vertex struct (e.g., `{:field, :app}`, `{:field, :module}`)

## Examples

    # All Application vertices
    Graph.vertices(graph, {:==, :vertex_type, Application})

    # Application OR Root vertices
    Graph.vertices(graph, {:or,
      {:==, :vertex_type, Application},
      {:==, :vertex_type, Root}
    })

    # Same as above, using :in
    Graph.vertices(graph, {:in, :vertex_type, [Application, Root]})

    # Complex: Root/Application OR (Module with specific ID)
    Graph.vertices(graph, {:or,
      {:in, :vertex_type, [Root, Application]},
      {:and, {:==, :vertex_type, Module}, {:==, :vertex_id, "module:Foo"}}
    })

    # All vertices except Root
    Graph.vertices(graph, {:not, {:==, :vertex_type, Root}})

    # Query by field value
    Graph.vertices(graph, {:and,
      {:==, :vertex_type, Application},
      {:==, {:field, :app}, :my_app}
    })

    # All vertices (default)
    Graph.vertices(graph, true)
    Graph.vertices(graph)

---

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