exonware-xwnode 0.0.1.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- exonware/__init__.py +14 -0
- exonware/xwnode/__init__.py +127 -0
- exonware/xwnode/base.py +676 -0
- exonware/xwnode/config.py +178 -0
- exonware/xwnode/contracts.py +730 -0
- exonware/xwnode/errors.py +503 -0
- exonware/xwnode/facade.py +460 -0
- exonware/xwnode/strategies/__init__.py +158 -0
- exonware/xwnode/strategies/advisor.py +463 -0
- exonware/xwnode/strategies/edges/__init__.py +32 -0
- exonware/xwnode/strategies/edges/adj_list.py +227 -0
- exonware/xwnode/strategies/edges/adj_matrix.py +391 -0
- exonware/xwnode/strategies/edges/base.py +169 -0
- exonware/xwnode/strategies/flyweight.py +328 -0
- exonware/xwnode/strategies/impls/__init__.py +13 -0
- exonware/xwnode/strategies/impls/_base_edge.py +403 -0
- exonware/xwnode/strategies/impls/_base_node.py +307 -0
- exonware/xwnode/strategies/impls/edge_adj_list.py +353 -0
- exonware/xwnode/strategies/impls/edge_adj_matrix.py +445 -0
- exonware/xwnode/strategies/impls/edge_bidir_wrapper.py +455 -0
- exonware/xwnode/strategies/impls/edge_block_adj_matrix.py +539 -0
- exonware/xwnode/strategies/impls/edge_coo.py +533 -0
- exonware/xwnode/strategies/impls/edge_csc.py +447 -0
- exonware/xwnode/strategies/impls/edge_csr.py +492 -0
- exonware/xwnode/strategies/impls/edge_dynamic_adj_list.py +503 -0
- exonware/xwnode/strategies/impls/edge_flow_network.py +555 -0
- exonware/xwnode/strategies/impls/edge_hyperedge_set.py +516 -0
- exonware/xwnode/strategies/impls/edge_neural_graph.py +650 -0
- exonware/xwnode/strategies/impls/edge_octree.py +574 -0
- exonware/xwnode/strategies/impls/edge_property_store.py +655 -0
- exonware/xwnode/strategies/impls/edge_quadtree.py +519 -0
- exonware/xwnode/strategies/impls/edge_rtree.py +820 -0
- exonware/xwnode/strategies/impls/edge_temporal_edgeset.py +558 -0
- exonware/xwnode/strategies/impls/edge_tree_graph_basic.py +271 -0
- exonware/xwnode/strategies/impls/edge_weighted_graph.py +411 -0
- exonware/xwnode/strategies/manager.py +775 -0
- exonware/xwnode/strategies/metrics.py +538 -0
- exonware/xwnode/strategies/migration.py +432 -0
- exonware/xwnode/strategies/nodes/__init__.py +50 -0
- exonware/xwnode/strategies/nodes/_base_node.py +307 -0
- exonware/xwnode/strategies/nodes/adjacency_list.py +267 -0
- exonware/xwnode/strategies/nodes/aho_corasick.py +345 -0
- exonware/xwnode/strategies/nodes/array_list.py +209 -0
- exonware/xwnode/strategies/nodes/base.py +247 -0
- exonware/xwnode/strategies/nodes/deque.py +200 -0
- exonware/xwnode/strategies/nodes/hash_map.py +135 -0
- exonware/xwnode/strategies/nodes/heap.py +307 -0
- exonware/xwnode/strategies/nodes/linked_list.py +232 -0
- exonware/xwnode/strategies/nodes/node_aho_corasick.py +520 -0
- exonware/xwnode/strategies/nodes/node_array_list.py +175 -0
- exonware/xwnode/strategies/nodes/node_avl_tree.py +371 -0
- exonware/xwnode/strategies/nodes/node_b_plus_tree.py +542 -0
- exonware/xwnode/strategies/nodes/node_bitmap.py +420 -0
- exonware/xwnode/strategies/nodes/node_bitset_dynamic.py +513 -0
- exonware/xwnode/strategies/nodes/node_bloom_filter.py +347 -0
- exonware/xwnode/strategies/nodes/node_btree.py +357 -0
- exonware/xwnode/strategies/nodes/node_count_min_sketch.py +470 -0
- exonware/xwnode/strategies/nodes/node_cow_tree.py +473 -0
- exonware/xwnode/strategies/nodes/node_cuckoo_hash.py +392 -0
- exonware/xwnode/strategies/nodes/node_fenwick_tree.py +301 -0
- exonware/xwnode/strategies/nodes/node_hash_map.py +269 -0
- exonware/xwnode/strategies/nodes/node_heap.py +191 -0
- exonware/xwnode/strategies/nodes/node_hyperloglog.py +407 -0
- exonware/xwnode/strategies/nodes/node_linked_list.py +409 -0
- exonware/xwnode/strategies/nodes/node_lsm_tree.py +400 -0
- exonware/xwnode/strategies/nodes/node_ordered_map.py +390 -0
- exonware/xwnode/strategies/nodes/node_ordered_map_balanced.py +565 -0
- exonware/xwnode/strategies/nodes/node_patricia.py +512 -0
- exonware/xwnode/strategies/nodes/node_persistent_tree.py +378 -0
- exonware/xwnode/strategies/nodes/node_radix_trie.py +452 -0
- exonware/xwnode/strategies/nodes/node_red_black_tree.py +497 -0
- exonware/xwnode/strategies/nodes/node_roaring_bitmap.py +570 -0
- exonware/xwnode/strategies/nodes/node_segment_tree.py +289 -0
- exonware/xwnode/strategies/nodes/node_set_hash.py +354 -0
- exonware/xwnode/strategies/nodes/node_set_tree.py +480 -0
- exonware/xwnode/strategies/nodes/node_skip_list.py +316 -0
- exonware/xwnode/strategies/nodes/node_splay_tree.py +393 -0
- exonware/xwnode/strategies/nodes/node_suffix_array.py +487 -0
- exonware/xwnode/strategies/nodes/node_treap.py +387 -0
- exonware/xwnode/strategies/nodes/node_tree_graph_hybrid.py +1434 -0
- exonware/xwnode/strategies/nodes/node_trie.py +252 -0
- exonware/xwnode/strategies/nodes/node_union_find.py +187 -0
- exonware/xwnode/strategies/nodes/node_xdata_optimized.py +369 -0
- exonware/xwnode/strategies/nodes/priority_queue.py +209 -0
- exonware/xwnode/strategies/nodes/queue.py +161 -0
- exonware/xwnode/strategies/nodes/sparse_matrix.py +206 -0
- exonware/xwnode/strategies/nodes/stack.py +152 -0
- exonware/xwnode/strategies/nodes/trie.py +274 -0
- exonware/xwnode/strategies/nodes/union_find.py +283 -0
- exonware/xwnode/strategies/pattern_detector.py +603 -0
- exonware/xwnode/strategies/performance_monitor.py +487 -0
- exonware/xwnode/strategies/queries/__init__.py +24 -0
- exonware/xwnode/strategies/queries/base.py +236 -0
- exonware/xwnode/strategies/queries/cql.py +201 -0
- exonware/xwnode/strategies/queries/cypher.py +181 -0
- exonware/xwnode/strategies/queries/datalog.py +70 -0
- exonware/xwnode/strategies/queries/elastic_dsl.py +70 -0
- exonware/xwnode/strategies/queries/eql.py +70 -0
- exonware/xwnode/strategies/queries/flux.py +70 -0
- exonware/xwnode/strategies/queries/gql.py +70 -0
- exonware/xwnode/strategies/queries/graphql.py +240 -0
- exonware/xwnode/strategies/queries/gremlin.py +181 -0
- exonware/xwnode/strategies/queries/hiveql.py +214 -0
- exonware/xwnode/strategies/queries/hql.py +70 -0
- exonware/xwnode/strategies/queries/jmespath.py +219 -0
- exonware/xwnode/strategies/queries/jq.py +66 -0
- exonware/xwnode/strategies/queries/json_query.py +66 -0
- exonware/xwnode/strategies/queries/jsoniq.py +248 -0
- exonware/xwnode/strategies/queries/kql.py +70 -0
- exonware/xwnode/strategies/queries/linq.py +238 -0
- exonware/xwnode/strategies/queries/logql.py +70 -0
- exonware/xwnode/strategies/queries/mql.py +68 -0
- exonware/xwnode/strategies/queries/n1ql.py +210 -0
- exonware/xwnode/strategies/queries/partiql.py +70 -0
- exonware/xwnode/strategies/queries/pig.py +215 -0
- exonware/xwnode/strategies/queries/promql.py +70 -0
- exonware/xwnode/strategies/queries/sparql.py +220 -0
- exonware/xwnode/strategies/queries/sql.py +275 -0
- exonware/xwnode/strategies/queries/xml_query.py +66 -0
- exonware/xwnode/strategies/queries/xpath.py +223 -0
- exonware/xwnode/strategies/queries/xquery.py +258 -0
- exonware/xwnode/strategies/queries/xwnode_executor.py +332 -0
- exonware/xwnode/strategies/queries/xwquery_strategy.py +424 -0
- exonware/xwnode/strategies/registry.py +604 -0
- exonware/xwnode/strategies/simple.py +273 -0
- exonware/xwnode/strategies/utils.py +532 -0
- exonware/xwnode/types.py +912 -0
- exonware/xwnode/version.py +78 -0
- exonware_xwnode-0.0.1.12.dist-info/METADATA +169 -0
- exonware_xwnode-0.0.1.12.dist-info/RECORD +132 -0
- exonware_xwnode-0.0.1.12.dist-info/WHEEL +4 -0
- exonware_xwnode-0.0.1.12.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,730 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
Contract interfaces for XWNode Strategy Pattern.
|
4
|
+
|
5
|
+
This module defines the contracts that all node and edge strategies must implement.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from abc import ABC, abstractmethod
|
9
|
+
from typing import Any, Iterator, Optional, Dict, List, Union, Callable
|
10
|
+
from enum import Enum, Flag
|
11
|
+
|
12
|
+
# Import all enums from types.py to avoid circular references
|
13
|
+
from .types import (
|
14
|
+
NodeMode, EdgeMode, NodeTrait, EdgeTrait, QueryMode, QueryTrait
|
15
|
+
)
|
16
|
+
|
17
|
+
|
18
|
+
class iNodeStrategy(ABC):
|
19
|
+
"""
|
20
|
+
Abstract interface for node strategies.
|
21
|
+
|
22
|
+
All node strategies must implement this interface to ensure
|
23
|
+
compatibility with the XWNode facade.
|
24
|
+
"""
|
25
|
+
|
26
|
+
@abstractmethod
|
27
|
+
def create_from_data(self, data: Any) -> 'iNodeStrategy':
|
28
|
+
"""Create a new strategy instance from data."""
|
29
|
+
pass
|
30
|
+
|
31
|
+
@abstractmethod
|
32
|
+
def to_native(self) -> Any:
|
33
|
+
"""Convert to native Python object."""
|
34
|
+
pass
|
35
|
+
|
36
|
+
@abstractmethod
|
37
|
+
def get(self, path: str, default: Any = None) -> Optional['iNodeStrategy']:
|
38
|
+
"""Get a child node by path."""
|
39
|
+
pass
|
40
|
+
|
41
|
+
@abstractmethod
|
42
|
+
def put(self, path: str, value: Any) -> 'iNodeStrategy':
|
43
|
+
"""Set a value at path."""
|
44
|
+
pass
|
45
|
+
|
46
|
+
@abstractmethod
|
47
|
+
def delete(self, path: str) -> bool:
|
48
|
+
"""Delete a node at path."""
|
49
|
+
pass
|
50
|
+
|
51
|
+
@abstractmethod
|
52
|
+
def exists(self, path: str) -> bool:
|
53
|
+
"""Check if path exists."""
|
54
|
+
pass
|
55
|
+
|
56
|
+
@abstractmethod
|
57
|
+
def keys(self) -> Iterator[str]:
|
58
|
+
"""Get keys (for dict-like nodes)."""
|
59
|
+
pass
|
60
|
+
|
61
|
+
@abstractmethod
|
62
|
+
def values(self) -> Iterator['iNodeStrategy']:
|
63
|
+
"""Get values (for dict-like nodes)."""
|
64
|
+
pass
|
65
|
+
|
66
|
+
@abstractmethod
|
67
|
+
def items(self) -> Iterator[tuple[str, 'iNodeStrategy']]:
|
68
|
+
"""Get items (for dict-like nodes)."""
|
69
|
+
pass
|
70
|
+
|
71
|
+
@abstractmethod
|
72
|
+
def __len__(self) -> int:
|
73
|
+
"""Get length."""
|
74
|
+
pass
|
75
|
+
|
76
|
+
@abstractmethod
|
77
|
+
def __iter__(self) -> Iterator['iNodeStrategy']:
|
78
|
+
"""Iterate over children."""
|
79
|
+
pass
|
80
|
+
|
81
|
+
@abstractmethod
|
82
|
+
def __getitem__(self, key: Union[str, int]) -> 'iNodeStrategy':
|
83
|
+
"""Get child by key or index."""
|
84
|
+
pass
|
85
|
+
|
86
|
+
@abstractmethod
|
87
|
+
def __setitem__(self, key: Union[str, int], value: Any) -> None:
|
88
|
+
"""Set child by key or index."""
|
89
|
+
pass
|
90
|
+
|
91
|
+
@abstractmethod
|
92
|
+
def __contains__(self, key: Union[str, int]) -> bool:
|
93
|
+
"""Check if key exists."""
|
94
|
+
pass
|
95
|
+
|
96
|
+
# Type checking properties
|
97
|
+
@property
|
98
|
+
@abstractmethod
|
99
|
+
def is_leaf(self) -> bool:
|
100
|
+
"""Check if this is a leaf node."""
|
101
|
+
pass
|
102
|
+
|
103
|
+
@property
|
104
|
+
@abstractmethod
|
105
|
+
def is_list(self) -> bool:
|
106
|
+
"""Check if this is a list node."""
|
107
|
+
pass
|
108
|
+
|
109
|
+
@property
|
110
|
+
@abstractmethod
|
111
|
+
def is_dict(self) -> bool:
|
112
|
+
"""Check if this is a dict node."""
|
113
|
+
pass
|
114
|
+
|
115
|
+
@property
|
116
|
+
@abstractmethod
|
117
|
+
def is_reference(self) -> bool:
|
118
|
+
"""Check if this is a reference node."""
|
119
|
+
pass
|
120
|
+
|
121
|
+
@property
|
122
|
+
@abstractmethod
|
123
|
+
def is_object(self) -> bool:
|
124
|
+
"""Check if this is an object node."""
|
125
|
+
pass
|
126
|
+
|
127
|
+
@property
|
128
|
+
@abstractmethod
|
129
|
+
def type(self) -> str:
|
130
|
+
"""Get the type of this node."""
|
131
|
+
pass
|
132
|
+
|
133
|
+
@property
|
134
|
+
@abstractmethod
|
135
|
+
def value(self) -> Any:
|
136
|
+
"""Get the value of this node."""
|
137
|
+
pass
|
138
|
+
|
139
|
+
# Optional properties with default implementations
|
140
|
+
@property
|
141
|
+
def uri(self) -> Optional[str]:
|
142
|
+
"""Get URI (for reference/object nodes)."""
|
143
|
+
return None
|
144
|
+
|
145
|
+
@property
|
146
|
+
def reference_type(self) -> Optional[str]:
|
147
|
+
"""Get reference type (for reference nodes)."""
|
148
|
+
return None
|
149
|
+
|
150
|
+
@property
|
151
|
+
def object_type(self) -> Optional[str]:
|
152
|
+
"""Get object type (for object nodes)."""
|
153
|
+
return None
|
154
|
+
|
155
|
+
@property
|
156
|
+
def mime_type(self) -> Optional[str]:
|
157
|
+
"""Get MIME type (for object nodes)."""
|
158
|
+
return None
|
159
|
+
|
160
|
+
@property
|
161
|
+
def metadata(self) -> Optional[Dict[str, Any]]:
|
162
|
+
"""Get metadata (for reference/object nodes)."""
|
163
|
+
return None
|
164
|
+
|
165
|
+
# Strategy information
|
166
|
+
@property
|
167
|
+
@abstractmethod
|
168
|
+
def strategy_name(self) -> str:
|
169
|
+
"""Get the name of this strategy."""
|
170
|
+
pass
|
171
|
+
|
172
|
+
@property
|
173
|
+
@abstractmethod
|
174
|
+
def supported_traits(self) -> List[NodeTrait]:
|
175
|
+
"""Get supported traits for this strategy."""
|
176
|
+
pass
|
177
|
+
|
178
|
+
|
179
|
+
class iEdgeStrategy(ABC):
|
180
|
+
"""
|
181
|
+
Abstract interface for edge strategies.
|
182
|
+
|
183
|
+
All edge strategies must implement this interface to ensure
|
184
|
+
compatibility with the XWNode graph operations, including advanced
|
185
|
+
features like edge types, weights, properties, and graph algorithms.
|
186
|
+
"""
|
187
|
+
|
188
|
+
@abstractmethod
|
189
|
+
def add_edge(self, source: str, target: str, edge_type: str = "default",
|
190
|
+
weight: float = 1.0, properties: Optional[Dict[str, Any]] = None,
|
191
|
+
is_bidirectional: bool = False, edge_id: Optional[str] = None) -> str:
|
192
|
+
"""Add an edge between source and target with advanced properties."""
|
193
|
+
pass
|
194
|
+
|
195
|
+
@abstractmethod
|
196
|
+
def remove_edge(self, source: str, target: str, edge_id: Optional[str] = None) -> bool:
|
197
|
+
"""Remove an edge between source and target."""
|
198
|
+
pass
|
199
|
+
|
200
|
+
@abstractmethod
|
201
|
+
def has_edge(self, source: str, target: str) -> bool:
|
202
|
+
"""Check if edge exists between source and target."""
|
203
|
+
pass
|
204
|
+
|
205
|
+
@abstractmethod
|
206
|
+
def get_neighbors(self, node: str, edge_type: Optional[str] = None, direction: str = "outgoing") -> List[str]:
|
207
|
+
"""Get neighbors of a node with optional filtering."""
|
208
|
+
pass
|
209
|
+
|
210
|
+
@abstractmethod
|
211
|
+
def get_edges(self, edge_type: Optional[str] = None, direction: str = "both") -> List[Dict[str, Any]]:
|
212
|
+
"""Get all edges with metadata."""
|
213
|
+
pass
|
214
|
+
|
215
|
+
@abstractmethod
|
216
|
+
def get_edge_data(self, source: str, target: str, edge_id: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
217
|
+
"""Get edge data/properties."""
|
218
|
+
pass
|
219
|
+
|
220
|
+
@abstractmethod
|
221
|
+
def shortest_path(self, source: str, target: str, edge_type: Optional[str] = None) -> List[str]:
|
222
|
+
"""Find shortest path between nodes."""
|
223
|
+
pass
|
224
|
+
|
225
|
+
@abstractmethod
|
226
|
+
def find_cycles(self, start_node: str, edge_type: Optional[str] = None, max_depth: int = 10) -> List[List[str]]:
|
227
|
+
"""Find cycles in the graph."""
|
228
|
+
pass
|
229
|
+
|
230
|
+
@abstractmethod
|
231
|
+
def traverse_graph(self, start_node: str, strategy: str = "bfs", max_depth: int = 100,
|
232
|
+
edge_type: Optional[str] = None) -> Iterator[str]:
|
233
|
+
"""Traverse the graph with cycle detection."""
|
234
|
+
pass
|
235
|
+
|
236
|
+
@abstractmethod
|
237
|
+
def is_connected(self, source: str, target: str, edge_type: Optional[str] = None) -> bool:
|
238
|
+
"""Check if nodes are connected."""
|
239
|
+
pass
|
240
|
+
|
241
|
+
@abstractmethod
|
242
|
+
def __len__(self) -> int:
|
243
|
+
"""Get number of edges."""
|
244
|
+
pass
|
245
|
+
|
246
|
+
@abstractmethod
|
247
|
+
def __iter__(self) -> Iterator[Dict[str, Any]]:
|
248
|
+
"""Iterate over edges with full metadata."""
|
249
|
+
pass
|
250
|
+
|
251
|
+
@property
|
252
|
+
@abstractmethod
|
253
|
+
def strategy_name(self) -> str:
|
254
|
+
"""Get the name of this strategy."""
|
255
|
+
pass
|
256
|
+
|
257
|
+
@property
|
258
|
+
@abstractmethod
|
259
|
+
def supported_traits(self) -> List[EdgeTrait]:
|
260
|
+
"""Get supported traits for this strategy."""
|
261
|
+
pass
|
262
|
+
|
263
|
+
|
264
|
+
class iEdge(ABC):
|
265
|
+
"""
|
266
|
+
Abstract interface for edge facade.
|
267
|
+
|
268
|
+
This defines the public interface for edge operations with advanced features
|
269
|
+
including edge types, weights, properties, and graph algorithms.
|
270
|
+
"""
|
271
|
+
|
272
|
+
@abstractmethod
|
273
|
+
def add_edge(self, source: str, target: str, edge_type: str = "default",
|
274
|
+
weight: float = 1.0, properties: Optional[Dict[str, Any]] = None,
|
275
|
+
is_bidirectional: bool = False, edge_id: Optional[str] = None) -> str:
|
276
|
+
"""Add an edge between source and target with advanced properties."""
|
277
|
+
pass
|
278
|
+
|
279
|
+
@abstractmethod
|
280
|
+
def remove_edge(self, source: str, target: str, edge_id: Optional[str] = None) -> bool:
|
281
|
+
"""Remove an edge between source and target."""
|
282
|
+
pass
|
283
|
+
|
284
|
+
@abstractmethod
|
285
|
+
def has_edge(self, source: str, target: str) -> bool:
|
286
|
+
"""Check if edge exists between source and target."""
|
287
|
+
pass
|
288
|
+
|
289
|
+
@abstractmethod
|
290
|
+
def get_neighbors(self, node: str, edge_type: Optional[str] = None, direction: str = "outgoing") -> List[str]:
|
291
|
+
"""Get neighbors of a node with optional filtering."""
|
292
|
+
pass
|
293
|
+
|
294
|
+
@abstractmethod
|
295
|
+
def get_edges(self, edge_type: Optional[str] = None, direction: str = "both") -> List[Dict[str, Any]]:
|
296
|
+
"""Get all edges with metadata."""
|
297
|
+
pass
|
298
|
+
|
299
|
+
@abstractmethod
|
300
|
+
def get_edge_data(self, source: str, target: str, edge_id: Optional[str] = None) -> Optional[Dict[str, Any]]:
|
301
|
+
"""Get edge data/properties."""
|
302
|
+
pass
|
303
|
+
|
304
|
+
@abstractmethod
|
305
|
+
def shortest_path(self, source: str, target: str, edge_type: Optional[str] = None) -> List[str]:
|
306
|
+
"""Find shortest path between nodes."""
|
307
|
+
pass
|
308
|
+
|
309
|
+
@abstractmethod
|
310
|
+
def find_cycles(self, start_node: str, edge_type: Optional[str] = None, max_depth: int = 10) -> List[List[str]]:
|
311
|
+
"""Find cycles in the graph."""
|
312
|
+
pass
|
313
|
+
|
314
|
+
@abstractmethod
|
315
|
+
def traverse_graph(self, start_node: str, strategy: str = "bfs", max_depth: int = 100,
|
316
|
+
edge_type: Optional[str] = None) -> Iterator[str]:
|
317
|
+
"""Traverse the graph with cycle detection."""
|
318
|
+
pass
|
319
|
+
|
320
|
+
@abstractmethod
|
321
|
+
def is_connected(self, source: str, target: str, edge_type: Optional[str] = None) -> bool:
|
322
|
+
"""Check if nodes are connected."""
|
323
|
+
pass
|
324
|
+
|
325
|
+
@abstractmethod
|
326
|
+
def __len__(self) -> int:
|
327
|
+
"""Get number of edges."""
|
328
|
+
pass
|
329
|
+
|
330
|
+
@abstractmethod
|
331
|
+
def __iter__(self) -> Iterator[Dict[str, Any]]:
|
332
|
+
"""Iterate over edges with full metadata."""
|
333
|
+
pass
|
334
|
+
|
335
|
+
@abstractmethod
|
336
|
+
def to_native(self) -> Any:
|
337
|
+
"""Convert to native Python object."""
|
338
|
+
pass
|
339
|
+
|
340
|
+
@abstractmethod
|
341
|
+
def copy(self) -> 'iEdge':
|
342
|
+
"""Create a deep copy."""
|
343
|
+
pass
|
344
|
+
|
345
|
+
|
346
|
+
class iNodeFacade(ABC):
|
347
|
+
"""
|
348
|
+
Abstract interface for the XWNode facade.
|
349
|
+
|
350
|
+
This defines the public interface that XWNode must implement.
|
351
|
+
"""
|
352
|
+
|
353
|
+
@abstractmethod
|
354
|
+
def get(self, path: str, default: Any = None) -> Optional['iNodeFacade']:
|
355
|
+
"""Get a node by path."""
|
356
|
+
pass
|
357
|
+
|
358
|
+
@abstractmethod
|
359
|
+
def set(self, path: str, value: Any, in_place: bool = True) -> 'iNodeFacade':
|
360
|
+
"""Set a value at path."""
|
361
|
+
pass
|
362
|
+
|
363
|
+
@abstractmethod
|
364
|
+
def delete(self, path: str, in_place: bool = True) -> 'iNodeFacade':
|
365
|
+
"""Delete a node at path."""
|
366
|
+
pass
|
367
|
+
|
368
|
+
@abstractmethod
|
369
|
+
def exists(self, path: str) -> bool:
|
370
|
+
"""Check if path exists."""
|
371
|
+
pass
|
372
|
+
|
373
|
+
@abstractmethod
|
374
|
+
def find(self, path: str, in_place: bool = False) -> Optional['iNodeFacade']:
|
375
|
+
"""Find a node by path."""
|
376
|
+
pass
|
377
|
+
|
378
|
+
@abstractmethod
|
379
|
+
def to_native(self) -> Any:
|
380
|
+
"""Convert to native Python object."""
|
381
|
+
pass
|
382
|
+
|
383
|
+
@abstractmethod
|
384
|
+
def copy(self) -> 'iNodeFacade':
|
385
|
+
"""Create a deep copy."""
|
386
|
+
pass
|
387
|
+
|
388
|
+
@abstractmethod
|
389
|
+
def count(self, path: str = ".") -> int:
|
390
|
+
"""Count nodes at path."""
|
391
|
+
pass
|
392
|
+
|
393
|
+
@abstractmethod
|
394
|
+
def flatten(self, separator: str = ".") -> Dict[str, Any]:
|
395
|
+
"""Flatten to dictionary."""
|
396
|
+
pass
|
397
|
+
|
398
|
+
@abstractmethod
|
399
|
+
def merge(self, other: 'iNodeFacade', strategy: str = "replace") -> 'iNodeFacade':
|
400
|
+
"""Merge with another node."""
|
401
|
+
pass
|
402
|
+
|
403
|
+
@abstractmethod
|
404
|
+
def diff(self, other: 'iNodeFacade') -> Dict[str, Any]:
|
405
|
+
"""Get differences with another node."""
|
406
|
+
pass
|
407
|
+
|
408
|
+
@abstractmethod
|
409
|
+
def transform(self, transformer: callable) -> 'iNodeFacade':
|
410
|
+
"""Transform using a function."""
|
411
|
+
pass
|
412
|
+
|
413
|
+
@abstractmethod
|
414
|
+
def select(self, *paths: str) -> Dict[str, 'iNodeFacade']:
|
415
|
+
"""Select multiple paths."""
|
416
|
+
pass
|
417
|
+
|
418
|
+
# Container methods
|
419
|
+
@abstractmethod
|
420
|
+
def __len__(self) -> int:
|
421
|
+
"""Get length."""
|
422
|
+
pass
|
423
|
+
|
424
|
+
@abstractmethod
|
425
|
+
def __iter__(self) -> Iterator['iNodeFacade']:
|
426
|
+
"""Iterate over children."""
|
427
|
+
pass
|
428
|
+
|
429
|
+
@abstractmethod
|
430
|
+
def __getitem__(self, key: Union[str, int]) -> 'iNodeFacade':
|
431
|
+
"""Get child by key or index."""
|
432
|
+
pass
|
433
|
+
|
434
|
+
@abstractmethod
|
435
|
+
def __setitem__(self, key: Union[str, int], value: Any) -> None:
|
436
|
+
"""Set child by key or index."""
|
437
|
+
pass
|
438
|
+
|
439
|
+
@abstractmethod
|
440
|
+
def __contains__(self, key: Union[str, int]) -> bool:
|
441
|
+
"""Check if key exists."""
|
442
|
+
pass
|
443
|
+
|
444
|
+
# Type checking properties
|
445
|
+
@property
|
446
|
+
@abstractmethod
|
447
|
+
def is_leaf(self) -> bool:
|
448
|
+
"""Check if this is a leaf node."""
|
449
|
+
pass
|
450
|
+
|
451
|
+
@property
|
452
|
+
@abstractmethod
|
453
|
+
def is_list(self) -> bool:
|
454
|
+
"""Check if this is a list node."""
|
455
|
+
pass
|
456
|
+
|
457
|
+
@property
|
458
|
+
@abstractmethod
|
459
|
+
def is_dict(self) -> bool:
|
460
|
+
"""Check if this is a dict node."""
|
461
|
+
pass
|
462
|
+
|
463
|
+
@property
|
464
|
+
@abstractmethod
|
465
|
+
def type(self) -> str:
|
466
|
+
"""Get the type of this node."""
|
467
|
+
pass
|
468
|
+
|
469
|
+
@property
|
470
|
+
@abstractmethod
|
471
|
+
def value(self) -> Any:
|
472
|
+
"""Get the value of this node."""
|
473
|
+
pass
|
474
|
+
|
475
|
+
# ============================================================================
|
476
|
+
# QUERY INTERFACES
|
477
|
+
# ============================================================================
|
478
|
+
|
479
|
+
# QueryMode and QueryTrait are now imported from types.py
|
480
|
+
|
481
|
+
|
482
|
+
class IQueryStrategy(ABC):
|
483
|
+
"""
|
484
|
+
Abstract interface for query strategies.
|
485
|
+
|
486
|
+
All query strategies must implement this interface to ensure
|
487
|
+
compatibility with the XWQuery facade and automatic query detection.
|
488
|
+
"""
|
489
|
+
|
490
|
+
@abstractmethod
|
491
|
+
def execute(self, query_string: str, context: Dict[str, Any] = None) -> 'iQueryResult':
|
492
|
+
"""Execute a query and return results."""
|
493
|
+
pass
|
494
|
+
|
495
|
+
@abstractmethod
|
496
|
+
def validate_query(self, query_string: str) -> bool:
|
497
|
+
"""Validate if the query string is valid for this strategy."""
|
498
|
+
pass
|
499
|
+
|
500
|
+
@abstractmethod
|
501
|
+
def get_query_plan(self, query_string: str) -> Dict[str, Any]:
|
502
|
+
"""Get the execution plan for a query."""
|
503
|
+
pass
|
504
|
+
|
505
|
+
@abstractmethod
|
506
|
+
def get_mode(self) -> QueryMode:
|
507
|
+
"""Get the query mode this strategy handles."""
|
508
|
+
pass
|
509
|
+
|
510
|
+
@abstractmethod
|
511
|
+
def get_traits(self) -> QueryTrait:
|
512
|
+
"""Get the traits/capabilities this strategy supports."""
|
513
|
+
pass
|
514
|
+
|
515
|
+
@abstractmethod
|
516
|
+
def can_handle(self, query_string: str) -> bool:
|
517
|
+
"""Check if this strategy can handle the given query string."""
|
518
|
+
pass
|
519
|
+
|
520
|
+
@abstractmethod
|
521
|
+
def get_supported_operations(self) -> List[str]:
|
522
|
+
"""Get list of supported query operations."""
|
523
|
+
pass
|
524
|
+
|
525
|
+
@abstractmethod
|
526
|
+
def estimate_complexity(self, query_string: str) -> Dict[str, Any]:
|
527
|
+
"""Estimate query complexity and resource requirements."""
|
528
|
+
pass
|
529
|
+
|
530
|
+
|
531
|
+
# ============================================================================
|
532
|
+
# QUERY ERROR CLASSES
|
533
|
+
# ============================================================================
|
534
|
+
|
535
|
+
class XWQueryError(Exception):
|
536
|
+
"""Base exception for all query-related errors."""
|
537
|
+
|
538
|
+
def __init__(self, message: str, query_string: str = None, cause: Exception = None):
|
539
|
+
super().__init__(message)
|
540
|
+
self.query_string = query_string
|
541
|
+
self.cause = cause
|
542
|
+
|
543
|
+
def __str__(self):
|
544
|
+
base_msg = super().__str__()
|
545
|
+
if self.query_string:
|
546
|
+
return f"{base_msg} (Query: {self.query_string[:100]}...)"
|
547
|
+
return base_msg
|
548
|
+
|
549
|
+
|
550
|
+
class XWQueryValidationError(XWQueryError):
|
551
|
+
"""Raised when query validation fails."""
|
552
|
+
pass
|
553
|
+
|
554
|
+
|
555
|
+
class XWQueryExecutionError(XWQueryError):
|
556
|
+
"""Raised when query execution fails."""
|
557
|
+
pass
|
558
|
+
|
559
|
+
|
560
|
+
class XWQueryParseError(XWQueryError):
|
561
|
+
"""Raised when query parsing fails."""
|
562
|
+
pass
|
563
|
+
|
564
|
+
|
565
|
+
class XWQueryTimeoutError(XWQueryError):
|
566
|
+
"""Raised when query execution times out."""
|
567
|
+
pass
|
568
|
+
|
569
|
+
|
570
|
+
class XWQueryNotSupportedError(XWQueryError):
|
571
|
+
"""Raised when a query operation is not supported."""
|
572
|
+
pass
|
573
|
+
|
574
|
+
|
575
|
+
class XWQueryStrategyNotFoundError(XWQueryError):
|
576
|
+
"""Raised when no suitable query strategy is found."""
|
577
|
+
pass
|
578
|
+
|
579
|
+
|
580
|
+
class XWQueryContextError(XWQueryError):
|
581
|
+
"""Raised when query context is invalid or missing."""
|
582
|
+
pass
|
583
|
+
|
584
|
+
class iQueryResult(ABC):
|
585
|
+
"""Interface for query results."""
|
586
|
+
|
587
|
+
@property
|
588
|
+
@abstractmethod
|
589
|
+
def nodes(self) -> List['iNodeFacade']:
|
590
|
+
"""Get result nodes."""
|
591
|
+
pass
|
592
|
+
|
593
|
+
@property
|
594
|
+
@abstractmethod
|
595
|
+
def metadata(self) -> Dict[str, Any]:
|
596
|
+
"""Get result metadata."""
|
597
|
+
pass
|
598
|
+
|
599
|
+
@abstractmethod
|
600
|
+
def first(self) -> Optional['iNodeFacade']:
|
601
|
+
"""Get first result."""
|
602
|
+
pass
|
603
|
+
|
604
|
+
@abstractmethod
|
605
|
+
def count(self) -> int:
|
606
|
+
"""Get result count."""
|
607
|
+
pass
|
608
|
+
|
609
|
+
@abstractmethod
|
610
|
+
def filter(self, predicate: Callable[['iNodeFacade'], bool]) -> 'iQueryResult':
|
611
|
+
"""Filter results."""
|
612
|
+
pass
|
613
|
+
|
614
|
+
@abstractmethod
|
615
|
+
def limit(self, limit: int) -> 'iQueryResult':
|
616
|
+
"""Limit results."""
|
617
|
+
pass
|
618
|
+
|
619
|
+
@abstractmethod
|
620
|
+
def offset(self, offset: int) -> 'iQueryResult':
|
621
|
+
"""Offset results."""
|
622
|
+
pass
|
623
|
+
|
624
|
+
|
625
|
+
class iQueryEngine(ABC):
|
626
|
+
"""Interface for query engines."""
|
627
|
+
|
628
|
+
@abstractmethod
|
629
|
+
def execute_query(self, query_string: str, context: Dict[str, Any]) -> iQueryResult:
|
630
|
+
"""Execute query and return results."""
|
631
|
+
pass
|
632
|
+
|
633
|
+
@abstractmethod
|
634
|
+
def parse_query(self, query_string: str) -> Dict[str, Any]:
|
635
|
+
"""Parse query string into structured format."""
|
636
|
+
pass
|
637
|
+
|
638
|
+
@abstractmethod
|
639
|
+
def validate_query(self, query_string: str) -> bool:
|
640
|
+
"""Validate query string."""
|
641
|
+
pass
|
642
|
+
|
643
|
+
|
644
|
+
class iQuery(ABC):
|
645
|
+
"""Interface for query capabilities."""
|
646
|
+
|
647
|
+
@abstractmethod
|
648
|
+
def query(self, query_string: str, query_type: str = "hybrid", **kwargs) -> iQueryResult:
|
649
|
+
"""Execute a query."""
|
650
|
+
pass
|
651
|
+
|
652
|
+
@abstractmethod
|
653
|
+
def find_nodes(self, predicate: Callable[['iNodeFacade'], bool], max_results: Optional[int] = None) -> iQueryResult:
|
654
|
+
"""Find nodes matching predicate."""
|
655
|
+
pass
|
656
|
+
|
657
|
+
@abstractmethod
|
658
|
+
def find_by_path(self, path_pattern: str) -> iQueryResult:
|
659
|
+
"""Find nodes by path pattern."""
|
660
|
+
pass
|
661
|
+
|
662
|
+
@abstractmethod
|
663
|
+
def find_by_value(self, value: Any, exact_match: bool = True) -> iQueryResult:
|
664
|
+
"""Find nodes by value."""
|
665
|
+
pass
|
666
|
+
|
667
|
+
@abstractmethod
|
668
|
+
def count_nodes(self, predicate: Optional[Callable[['iNodeFacade'], bool]] = None) -> int:
|
669
|
+
"""Count nodes matching predicate."""
|
670
|
+
pass
|
671
|
+
|
672
|
+
@abstractmethod
|
673
|
+
def select(self, selector: str, **kwargs) -> List['iNodeFacade']:
|
674
|
+
"""Select nodes using a selector expression."""
|
675
|
+
pass
|
676
|
+
|
677
|
+
@abstractmethod
|
678
|
+
def filter(self, condition: str, **kwargs) -> List['iNodeFacade']:
|
679
|
+
"""Filter nodes based on a condition."""
|
680
|
+
pass
|
681
|
+
|
682
|
+
@abstractmethod
|
683
|
+
def where(self, condition: str) -> List['iNodeFacade']:
|
684
|
+
"""Filter nodes using a where condition."""
|
685
|
+
pass
|
686
|
+
|
687
|
+
@abstractmethod
|
688
|
+
def sort(self, key: str = None, reverse: bool = False) -> List['iNodeFacade']:
|
689
|
+
"""Sort nodes by a key."""
|
690
|
+
pass
|
691
|
+
|
692
|
+
@abstractmethod
|
693
|
+
def limit(self, count: int) -> List['iNodeFacade']:
|
694
|
+
"""Limit the number of results."""
|
695
|
+
pass
|
696
|
+
|
697
|
+
@abstractmethod
|
698
|
+
def skip(self, count: int) -> List['iNodeFacade']:
|
699
|
+
"""Skip a number of results."""
|
700
|
+
pass
|
701
|
+
|
702
|
+
@abstractmethod
|
703
|
+
def first(self) -> Optional['iNodeFacade']:
|
704
|
+
"""Get the first result."""
|
705
|
+
pass
|
706
|
+
|
707
|
+
@abstractmethod
|
708
|
+
def last(self) -> Optional['iNodeFacade']:
|
709
|
+
"""Get the last result."""
|
710
|
+
pass
|
711
|
+
|
712
|
+
@abstractmethod
|
713
|
+
def group_by(self, key: str) -> Dict[str, List['iNodeFacade']]:
|
714
|
+
"""Group nodes by a key."""
|
715
|
+
pass
|
716
|
+
|
717
|
+
@abstractmethod
|
718
|
+
def distinct(self, key: str = None) -> List['iNodeFacade']:
|
719
|
+
"""Get distinct values."""
|
720
|
+
pass
|
721
|
+
|
722
|
+
@abstractmethod
|
723
|
+
def clear_query_cache(self):
|
724
|
+
"""Clear the query cache."""
|
725
|
+
pass
|
726
|
+
|
727
|
+
@abstractmethod
|
728
|
+
def get_query_stats(self) -> Dict[str, Any]:
|
729
|
+
"""Get query execution statistics."""
|
730
|
+
pass
|