Coverage for src / lexigram / contracts / data / graph / protocols.py: 0%

39 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-15 18:57 +0800

1"""Graph store protocol definitions.""" 

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable 

6 

7from lexigram.contracts.data.graph.enums import EdgeDirection 

8 

9if TYPE_CHECKING: 

10 from lexigram.contracts.core.health import HealthCheckResult 

11 from lexigram.contracts.data.graph.filters import PropertyFilter 

12 from lexigram.contracts.data.graph.types import ( 

13 BulkEdgeResult, 

14 BulkNodeResult, 

15 ConstraintSpec, 

16 EdgeResult, 

17 EdgeSpec, 

18 GraphEdge, 

19 GraphInfo, 

20 GraphNode, 

21 GraphPath, 

22 IndexSpec, 

23 NodeResult, 

24 NodeSpec, 

25 TraversalQuery, 

26 ) 

27 

28 

29@runtime_checkable 

30class GraphStoreProtocol(Protocol): 

31 """Top-level graph store lifecycle and database management. 

32 

33 Manages connections to the graph backend and provides access to 

34 individual graph databases. 

35 """ 

36 

37 async def connect(self) -> None: 

38 """Establish connection to the graph store.""" 

39 ... 

40 

41 async def disconnect(self) -> None: 

42 """Close all connections and release resources.""" 

43 ... 

44 

45 async def health_check(self, timeout: float = 5.0) -> HealthCheckResult: 

46 """Check store connectivity and readiness.""" 

47 ... 

48 

49 async def get_graph(self, name: str | None = None) -> GraphProtocol: 

50 """Get a handle to a graph database. 

51 

52 Args: 

53 name: Database name. ``None`` returns the default graph. 

54 

55 Raises: 

56 GraphNotFoundError: If the named graph does not exist. 

57 """ 

58 ... 

59 

60 async def list_graphs(self) -> list[GraphInfo]: 

61 """List all available graph databases.""" 

62 ... 

63 

64 async def create_graph(self, name: str) -> None: 

65 """Create a new graph database. 

66 

67 Raises: 

68 GraphAlreadyExistsError: If the graph already exists. 

69 """ 

70 ... 

71 

72 async def delete_graph(self, name: str) -> None: 

73 """Delete a graph database and all its data. 

74 

75 Raises: 

76 GraphNotFoundError: If the graph does not exist. 

77 """ 

78 ... 

79 

80 

81@runtime_checkable 

82class GraphProtocol(Protocol): 

83 """All operations on a single graph database. 

84 

85 Provides CRUD for nodes and edges, traversal queries, raw query 

86 passthrough, bulk operations, and schema management. 

87 """ 

88 

89 # ── Node Operations ─────────────────────────────────────────── 

90 

91 async def create_node( 

92 self, 

93 labels: list[str], 

94 properties: dict[str, Any] | None = None, 

95 node_id: str | None = None, 

96 ) -> NodeResult: 

97 """Create a node with labels and properties.""" 

98 ... 

99 

100 async def get_node(self, node_id: str) -> GraphNode | None: 

101 """Retrieve a node by ID. Returns None if not found.""" 

102 ... 

103 

104 async def find_nodes( 

105 self, 

106 labels: list[str] | None = None, 

107 filter: PropertyFilter | None = None, 

108 limit: int = 100, 

109 skip: int = 0, 

110 ) -> list[GraphNode]: 

111 """Find nodes matching labels and/or property filter.""" 

112 ... 

113 

114 async def update_node( 

115 self, 

116 node_id: str, 

117 properties: dict[str, Any], 

118 merge: bool = True, 

119 ) -> bool: 

120 """Update node properties.""" 

121 ... 

122 

123 async def delete_node( 

124 self, 

125 node_id: str, 

126 detach: bool = True, 

127 ) -> bool: 

128 """Delete a node.""" 

129 ... 

130 

131 async def neighbors( 

132 self, 

133 node_id: str, 

134 depth: int = 1, 

135 direction: EdgeDirection = EdgeDirection.BOTH, 

136 edge_types: list[str] | None = None, 

137 ) -> list[GraphNode]: 

138 """Get neighbouring nodes reachable within the given depth.""" 

139 ... 

140 

141 async def count_nodes(self) -> int: 

142 """Return the total number of nodes in the graph.""" 

143 ... 

144 

145 async def count_edges(self) -> int: 

146 """Return the total number of edges in the graph.""" 

147 ... 

148 

149 async def get_labels(self) -> list[str]: 

150 """Return all unique node labels in the graph.""" 

151 ... 

152 

153 async def get_edge_types(self) -> list[str]: 

154 """Return all unique edge types in the graph.""" 

155 ... 

156 

157 # ── Edge Operations ─────────────────────────────────────────── 

158 

159 async def create_edge( 

160 self, 

161 source_id: str, 

162 target_id: str, 

163 edge_type: str, 

164 properties: dict[str, Any] | None = None, 

165 ) -> EdgeResult: 

166 """Create a directed edge between two nodes.""" 

167 ... 

168 

169 async def get_edge(self, edge_id: str) -> GraphEdge | None: 

170 """Retrieve an edge by ID.""" 

171 ... 

172 

173 async def get_edges( 

174 self, 

175 node_id: str, 

176 direction: EdgeDirection = EdgeDirection.BOTH, 

177 edge_types: list[str] | None = None, 

178 limit: int = 100, 

179 ) -> list[GraphEdge]: 

180 """Get edges connected to a node.""" 

181 ... 

182 

183 async def update_edge( 

184 self, 

185 edge_id: str, 

186 properties: dict[str, Any], 

187 merge: bool = True, 

188 ) -> bool: 

189 """Update edge properties.""" 

190 ... 

191 

192 async def delete_edge(self, edge_id: str) -> bool: 

193 """Delete an edge by ID.""" 

194 ... 

195 

196 # ── Traversal ───────────────────────────────────────────────── 

197 

198 async def traverse( 

199 self, 

200 query: TraversalQuery, 

201 ) -> list[GraphPath]: 

202 """Execute a traversal query and return matching paths.""" 

203 ... 

204 

205 async def shortest_path( 

206 self, 

207 from_id: str, 

208 to_id: str, 

209 max_depth: int = 10, 

210 edge_types: list[str] | None = None, 

211 direction: EdgeDirection = EdgeDirection.BOTH, 

212 ) -> GraphPath | None: 

213 """Find the shortest path between two nodes.""" 

214 ... 

215 

216 # ── Raw Query ───────────────────────────────────────────────── 

217 

218 async def query( 

219 self, 

220 query_string: str, 

221 parameters: dict[str, Any] | None = None, 

222 ) -> list[dict[str, Any]]: 

223 """Execute a raw backend-specific query (Cypher, Gremlin, etc.).""" 

224 ... 

225 

226 # ── Bulk Operations ─────────────────────────────────────────── 

227 

228 async def bulk_create_nodes( 

229 self, 

230 nodes: list[NodeSpec], 

231 ) -> BulkNodeResult: 

232 """Create multiple nodes in a single operation.""" 

233 ... 

234 

235 async def bulk_create_edges( 

236 self, 

237 edges: list[EdgeSpec], 

238 ) -> BulkEdgeResult: 

239 """Create multiple edges in a single operation.""" 

240 ... 

241 

242 # ── Schema ──────────────────────────────────────────────────── 

243 

244 async def create_index(self, spec: IndexSpec) -> None: 

245 """Create an index on node label + properties.""" 

246 ... 

247 

248 async def drop_index(self, name: str) -> None: 

249 """Drop an index by name.""" 

250 ... 

251 

252 async def create_constraint(self, spec: ConstraintSpec) -> None: 

253 """Create a schema constraint.""" 

254 ... 

255 

256 async def drop_constraint(self, name: str) -> None: 

257 """Drop a constraint by name.""" 

258 ... 

259 

260 

261__all__ = [ 

262 "GraphProtocol", 

263 "GraphStoreProtocol", 

264]