Constructor

ISONGraph(name: str = "graph", directed: bool = True)
Parameter Type Default Description
name str "graph" Name identifier for the graph
directed bool True Whether edges are directed

Data Types

NodeRef

A tuple identifying a node: (type: str, id: Any)

NodeRef = Tuple[str, Any]  # e.g., ('person', 1), ('company', 'acme')

Node

@dataclass
class Node:
    type: str           # Node type (e.g., 'person', 'product')
    id: Any             # Unique ID within type
    properties: Dict    # Key-value properties

Edge

@dataclass
class Edge:
    rel_type: str       # Relationship type (e.g., 'KNOWS', 'PURCHASED')
    source: NodeRef     # Source node reference
    target: NodeRef     # Target node reference
    properties: Dict    # Key-value properties

Path

@dataclass
class Path:
    nodes: List[NodeRef]  # Nodes in order
    edges: List[Edge]     # Edges connecting nodes
    length: int           # Number of edges
    start: NodeRef        # First node
    end: NodeRef          # Last node

Direction

class Direction(Enum):
    OUT = "out"    # Outgoing edges (default)
    IN = "in"      # Incoming edges
    BOTH = "both"  # Both directions

add_node

Add a node to the graph.

add_node(type: str, id: Any, **properties) -> Node
ParameterTypeDescription
typestrNode type (e.g., 'person', 'product')
idAnyUnique identifier within type
**propertiesAnyKey-value properties
graph.add_node('person', 1, name='Alice', age=30)
graph.add_node('company', 'acme', name='ACME Corp', employees=500)

get_node

Retrieve a node by type and ID.

get_node(type: str, id: Any) -> Optional[Node]
node = graph.get_node('person', 1)
if node:
    print(node.properties['name'])  # 'Alice'

has_node

Check if a node exists.

has_node(type: str, id: Any) -> bool

update_node

Update node properties (merges with existing).

update_node(type: str, id: Any, **properties) -> Optional[Node]

remove_node

Remove a node and all its edges.

remove_node(type: str, id: Any) -> bool

nodes

Iterate over nodes, optionally filtered by type.

nodes(type: Optional[str] = None) -> Iterator[Node]
# All nodes
for node in graph.nodes():
    print(node)

# Only person nodes
for person in graph.nodes('person'):
    print(person.properties['name'])

add_edge

Add an edge between two nodes.

add_edge(rel_type: str, source: NodeRef, target: NodeRef, **properties) -> Edge
graph.add_edge('KNOWS', ('person', 1), ('person', 2), since=2020)
graph.add_edge('WORKS_AT', ('person', 1), ('company', 'acme'), role='Engineer')

neighbors

Get adjacent nodes via a relationship type.

neighbors(
    node: NodeRef,
    rel_type: Optional[str] = None,
    direction: Direction = Direction.OUT
) -> List[NodeRef]
# People Alice knows
friends = graph.neighbors(('person', 1), 'KNOWS')

# People who know Alice
followers = graph.neighbors(('person', 1), 'KNOWS', Direction.IN)

# All connections (any relationship)
all_neighbors = graph.neighbors(('person', 1), direction=Direction.BOTH)

multi_hop

Traverse N hops from a starting node.

multi_hop(
    start: NodeRef,
    rel_type: str,
    hops: int,
    direction: Direction = Direction.OUT
) -> List[NodeRef]
# Friends of friends
fof = graph.multi_hop(('person', 1), 'KNOWS', hops=2)

# 3 hops away
distant = graph.multi_hop(('person', 1), 'KNOWS', hops=3)

Fluent Traversal API

Chainable interface for complex traversals.

graph.start(node_ref)
    .hop(rel_type, direction, where)  # Single hop
    .hops(n, rel_type, direction)     # N hops
    .filter(predicate_fn)             # Filter nodes
    .collect()                        # -> List[NodeRef]
    .collect_nodes()                  # -> List[Node]
    .count()                          # -> int
    .first()                          # -> Optional[NodeRef]
# Complex traversal example
results = graph.start(('person', 1)) \
    .hop('KNOWS') \
    .hop('WORKS_AT') \
    .filter(lambda n: n.properties.get('employees', 0) > 100) \
    .collect_nodes()

for company in results:
    print(company.properties['name'])

shortest_path

Find the shortest path between two nodes using BFS.

shortest_path(
    start: NodeRef,
    end: NodeRef,
    rel_type: Optional[str] = None,
    max_hops: int = 10
) -> Optional[Path]
path = graph.shortest_path(('person', 1), ('person', 5))
if path:
    print(f"Length: {path.length}")
    print(f"Nodes: {path.nodes}")

all_paths

Find all paths between two nodes using DFS.

all_paths(
    start: NodeRef,
    end: NodeRef,
    rel_type: Optional[str] = None,
    max_hops: int = 10
) -> List[Path]

Degree Analysis

# Incoming edges count
in_deg = graph.in_degree(('person', 1))

# Outgoing edges count
out_deg = graph.out_degree(('person', 1))

# Total degree
total = graph.degree(('person', 1))

is_connected

Check if all nodes are reachable from any starting node.

is_connected() -> bool

has_cycle

Detect cycles in the graph.

has_cycle(rel_type: Optional[str] = None) -> bool
# Check for cycles in KNOWS relationships
if graph.has_cycle('KNOWS'):
    print("Circular friendships detected!")

# Check entire graph
if graph.has_cycle():
    print("Graph contains cycles")