Ragex.Graph.Store (Ragex v0.32.2)

View Source

Knowledge graph storage dispatcher.

Manages nodes (modules, functions, types, etc.) and edges (calls, imports, etc.) representing relationships in the codebase.

The actual storage is delegated to the configured backend module (see Ragex.Store.Backend). The default is ETS; dllb can be selected via:

config :ragex, :store_backend, :dllb

Summary

Functions

Adds an edge between two nodes.

Adds multiple edges to the graph in bulk.

Adds a node to the graph.

Adds multiple nodes to the graph in bulk.

Returns a specification to start this module under a supervisor.

Clears all data from the graph.

Triggers database compaction on the underlying store to reclaim free space.

Returns the number of stored embeddings (vectors) in the active backend.

Counts nodes of a specific type.

Returns the ETS table reference for embeddings.

Finds a function node by module and name (any arity).

Finds a node by type and id.

Gets the weight of an edge between two nodes.

Retrieves the embedding for a node.

Retrieves a function node by module, name, and arity.

Gets all incoming edges to a node of a specific type.

Retrieves a module node by name.

Retrieves a node by its composite key.

Gets all outgoing edges from a node of a specific type.

Lists all edges with optional filtering by type and limit.

Lists all embeddings with optional type filter.

Lists function nodes with optional filtering by module.

Lists all module nodes.

Lists nodes with optional filtering by type.

Switches the store to a specific project path.

Removes a node from the graph.

Saves current graph, embeddings, and analysis cache to disk for the given project path.

Returns statistics about the graph.

Stores an embedding vector for a node.

Stores a batch of embedding vectors.

Synchronous barrier: blocks until all pending casts have been processed.

Merges additional metadata into an existing node.

Functions

add_edge(from_node, to_node, edge_type, opts \\ [])

@spec add_edge(term(), term(), atom(), keyword()) :: :ok

Adds an edge between two nodes.

Edge types: :calls, :imports, :defines, :inherits, :implements

Options

  • :weight - Edge weight (default: 1.0) for weighted graph algorithms
  • :metadata - Additional metadata map

add_edges(edges)

@spec add_edges([tuple()]) :: :ok

Adds multiple edges to the graph in bulk.

add_node(node_type, node_id, data)

@spec add_node(atom(), term(), map()) :: :ok

Adds a node to the graph.

Node types: :module, :function, :type, :variable, :file

add_nodes(nodes)

@spec add_nodes([map()]) :: :ok

Adds multiple nodes to the graph in bulk.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

clear()

@spec clear() :: :ok

Clears all data from the graph.

compact()

@spec compact() :: :ok | {:error, term()}

Triggers database compaction on the underlying store to reclaim free space.

count_embeddings()

@spec count_embeddings() :: non_neg_integer()

Returns the number of stored embeddings (vectors) in the active backend.

count_nodes_by_type(node_type)

@spec count_nodes_by_type(atom()) :: non_neg_integer()

Counts nodes of a specific type.

embeddings_table()

@spec embeddings_table() :: atom()

Returns the ETS table reference for embeddings.

Useful for direct access or persistence operations.

find_function(module, name)

@spec find_function(term(), atom()) :: map() | nil

Finds a function node by module and name (any arity).

find_node(node_type, node_id)

@spec find_node(atom(), term()) :: map() | nil

Finds a node by type and id.

get_edge_weight(from_node, to_node, edge_type)

@spec get_edge_weight(term(), term(), atom()) :: float() | nil

Gets the weight of an edge between two nodes.

Returns the weight (default: 1.0) if edge exists, nil otherwise.

get_embedding(node_type, node_id)

@spec get_embedding(atom(), term()) :: {[float()], String.t()} | nil

Retrieves the embedding for a node.

Returns {embedding, text} tuple or nil if not found.

get_function(module, name, arity)

@spec get_function(term(), atom(), non_neg_integer()) :: map() | nil

Retrieves a function node by module, name, and arity.

Returns the function data or nil if not found.

Parameters

  • module - The module containing the function
  • name - The function name (atom)
  • arity - The function arity (non-negative integer)

Examples

iex> Store.add_node(:function, {MyModule, :test, 2}, %{name: :test, arity: 2})
iex> Store.get_function(MyModule, :test, 2)
%{name: :test, arity: 2}

iex> Store.get_function(MyModule, :nonexistent, 1)
nil

get_incoming_edges(to_node, edge_type)

@spec get_incoming_edges(term(), atom()) :: [map()]

Gets all incoming edges to a node of a specific type.

get_module(module)

@spec get_module(term()) :: map() | nil

Retrieves a module node by name.

Returns the module data or nil if not found.

Examples

iex> Store.add_node(:module, MyModule, %{name: MyModule, file: "lib/my_module.ex"})
iex> Store.get_module(MyModule)
%{name: MyModule, file: "lib/my_module.ex"}

iex> Store.get_module(NonExistentModule)
nil

get_node(arg)

@spec get_node({atom(), term()}) :: map() | nil

Retrieves a node by its composite key.

Parameters

  • node_key - Tuple of {node_type, node_id}

Returns

  • Node data map if found
  • nil if not found

Examples

iex> Store.add_node(:module, MyModule, %{name: MyModule})
iex> Store.get_node({:module, MyModule})
%{name: MyModule}

iex> Store.get_node({:module, NonExistent})
nil

get_outgoing_edges(from_node, edge_type)

@spec get_outgoing_edges(term(), atom()) :: [map()]

Gets all outgoing edges from a node of a specific type.

list_edges(opts \\ [])

@spec list_edges(keyword()) :: [map()]

Lists all edges with optional filtering by type and limit.

Parameters

  • opts - Keyword list with options:
    • :edge_type - Filter by edge type (optional)
    • :limit - Maximum number of edges to return (default: 1000)

Returns

List of edge maps with keys:

  • :from - Source node
  • :to - Target node
  • :type - Edge type
  • :metadata - Edge metadata including weight

Examples

iex> Store.list_edges(limit: 100)
[%{from: node1, to: node2, type: :calls, metadata: %{weight: 1.0}}, ...]

iex> Store.list_edges(edge_type: :imports)
[%{from: mod1, to: mod2, type: :imports, metadata: %{weight: 1.0}}, ...]

list_embeddings(node_type \\ nil, limit \\ 1000)

@spec list_embeddings(atom() | nil, non_neg_integer() | :infinity) :: [tuple()]

Lists all embeddings with optional type filter.

Returns list of {node_type, node_id, embedding, text} tuples.

list_functions(opts \\ [])

@spec list_functions(keyword()) :: [%{id: term(), data: map()}]

Lists function nodes with optional filtering by module.

Parameters

  • opts - Keyword list with options:
    • :module - Filter by module (optional)
    • :limit - Maximum number of functions to return (default: 1000)

Returns

List of function maps with keys:

  • :id - Function identifier tuple {module, name, arity}
  • :data - Function data

Examples

iex> Store.add_node(:function, {MyModule, :test, 2}, %{name: :test, arity: 2})
iex> Store.list_functions()
[%{id: {MyModule, :test, 2}, data: %{name: :test, arity: 2}}]

iex> Store.list_functions(module: MyModule, limit: 50)
[%{id: {MyModule, :test, 2}, data: ...}, ...]

list_modules()

@spec list_modules() :: [%{id: term(), data: map()}]

Lists all module nodes.

Returns

List of module maps with keys:

  • :id - Module identifier (atom)
  • :data - Module data

Examples

iex> Store.add_node(:module, ModuleA, %{name: ModuleA, file: "lib/a.ex"})
iex> Store.add_node(:module, ModuleB, %{name: ModuleB, file: "lib/b.ex"})
iex> Store.list_modules()
[%{id: ModuleA, data: %{name: ModuleA, file: "lib/a.ex"}}, ...]

list_nodes(node_type \\ nil, limit \\ 1000)

@spec list_nodes(atom() | nil, non_neg_integer() | :infinity) :: [map()]

Lists nodes with optional filtering by type.

load_project(project_path)

@spec load_project(String.t()) :: :ok

Switches the store to a specific project path.

Clears all current graph data (nodes, edges, embeddings, file tracker), then loads the cached graph and embeddings for the given project path. This ensures the store contains only data for the target project.

Parameters

  • project_path - Absolute path to the project directory

Returns

  • :ok

remove_node(node_type, node_id)

@spec remove_node(atom(), term()) :: :ok

Removes a node from the graph.

Also removes all edges connected to this node (both incoming and outgoing) and any embedding associated with the node.

Returns :ok if successful, {:error, :timeout} on timeout.

save_cache(project_path \\ nil)

@spec save_cache(String.t() | nil) :: :ok

Saves current graph, embeddings, and analysis cache to disk for the given project path.

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

stats()

@spec stats() :: map()

Returns statistics about the graph.

store_embedding(node_type, node_id, embedding, text)

@spec store_embedding(atom(), term(), [float()], String.t()) :: :ok

Stores an embedding vector for a node.

store_embeddings(embeddings)

@spec store_embeddings([tuple()]) :: :ok

Stores a batch of embedding vectors.

sync()

@spec sync() :: :ok

Synchronous barrier: blocks until all pending casts have been processed.

Useful in tests to ensure async operations (add_node, add_edge, etc.) have been applied to ETS before querying.

update_node_metadata(node_type, node_id, new_metadata)

@spec update_node_metadata(atom(), term(), map()) :: :ok

Merges additional metadata into an existing node.

If the node exists, its metadata map is deep-merged with new_metadata. If the node does not exist, this is a no-op.

Parameters

  • node_type -- :module, :function, etc.
  • node_id -- the node identifier
  • new_metadata -- map of metadata to merge