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,432 @@
|
|
1
|
+
"""
|
2
|
+
Strategy Migration System
|
3
|
+
|
4
|
+
This module implements seamless migration between different node and edge
|
5
|
+
strategies while preserving data integrity.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Dict, List, Optional, Set, Tuple, Type
|
9
|
+
from enum import Enum
|
10
|
+
import threading
|
11
|
+
import time
|
12
|
+
from ..types import NodeMode, EdgeMode, NodeTrait, EdgeTrait
|
13
|
+
from .registry import get_registry
|
14
|
+
from ..errors import XWNodeStrategyError, XWNodeError
|
15
|
+
import logging
|
16
|
+
|
17
|
+
logger = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
|
20
|
+
class MigrationPlan:
|
21
|
+
"""Represents a strategy migration plan with validation and cost estimation."""
|
22
|
+
|
23
|
+
def __init__(self, source_mode: Any, target_mode: Any, strategy_type: str):
|
24
|
+
self.source_mode = source_mode
|
25
|
+
self.target_mode = target_mode
|
26
|
+
self.strategy_type = strategy_type # 'node' or 'edge'
|
27
|
+
self.cost_estimate = 0
|
28
|
+
self.data_loss_risk = 'none' # 'none', 'low', 'medium', 'high'
|
29
|
+
self.warnings: List[str] = []
|
30
|
+
self.required_operations: List[str] = []
|
31
|
+
self.estimated_time_ms = 0
|
32
|
+
|
33
|
+
def add_warning(self, warning: str) -> None:
|
34
|
+
"""Add a migration warning."""
|
35
|
+
self.warnings.append(warning)
|
36
|
+
|
37
|
+
def add_operation(self, operation: str) -> None:
|
38
|
+
"""Add a required migration operation."""
|
39
|
+
self.required_operations.append(operation)
|
40
|
+
|
41
|
+
def is_safe(self) -> bool:
|
42
|
+
"""Check if migration is considered safe."""
|
43
|
+
return self.data_loss_risk in ['none', 'low'] and len(self.warnings) <= 2
|
44
|
+
|
45
|
+
|
46
|
+
class xStrategyMigrator:
|
47
|
+
"""
|
48
|
+
Handles migration between different node and edge strategies.
|
49
|
+
|
50
|
+
Provides safe, efficient migration with rollback capabilities
|
51
|
+
and data integrity guarantees.
|
52
|
+
"""
|
53
|
+
|
54
|
+
def __init__(self):
|
55
|
+
self._registry = get_registry()
|
56
|
+
self._migration_lock = threading.RLock()
|
57
|
+
self._migration_history: List[Dict[str, Any]] = []
|
58
|
+
|
59
|
+
# ============================================================================
|
60
|
+
# MIGRATION PLANNING
|
61
|
+
# ============================================================================
|
62
|
+
|
63
|
+
def plan_node_migration(self, source_mode: NodeMode, target_mode: NodeMode,
|
64
|
+
source_traits: NodeTrait = NodeTrait.NONE,
|
65
|
+
target_traits: NodeTrait = NodeTrait.NONE,
|
66
|
+
data_size: int = 0) -> MigrationPlan:
|
67
|
+
"""Plan a migration between node strategies."""
|
68
|
+
plan = MigrationPlan(source_mode, target_mode, 'node')
|
69
|
+
|
70
|
+
# Basic validation
|
71
|
+
if source_mode == target_mode and source_traits == target_traits:
|
72
|
+
plan.add_warning("Source and target are identical")
|
73
|
+
return plan
|
74
|
+
|
75
|
+
# Check strategy availability
|
76
|
+
if not self._registry.has_node_strategy(target_mode):
|
77
|
+
raise XWNodeStrategyError(f"Target node strategy {target_mode.name} not available")
|
78
|
+
|
79
|
+
# Analyze compatibility
|
80
|
+
self._analyze_node_compatibility(plan, source_mode, target_mode,
|
81
|
+
source_traits, target_traits, data_size)
|
82
|
+
|
83
|
+
return plan
|
84
|
+
|
85
|
+
def plan_edge_migration(self, source_mode: EdgeMode, target_mode: EdgeMode,
|
86
|
+
source_traits: EdgeTrait = EdgeTrait.NONE,
|
87
|
+
target_traits: EdgeTrait = EdgeTrait.NONE,
|
88
|
+
edge_count: int = 0, vertex_count: int = 0) -> MigrationPlan:
|
89
|
+
"""Plan a migration between edge strategies."""
|
90
|
+
plan = MigrationPlan(source_mode, target_mode, 'edge')
|
91
|
+
|
92
|
+
# Basic validation
|
93
|
+
if source_mode == target_mode and source_traits == target_traits:
|
94
|
+
plan.add_warning("Source and target are identical")
|
95
|
+
return plan
|
96
|
+
|
97
|
+
# Check strategy availability
|
98
|
+
if not self._registry.has_edge_strategy(target_mode):
|
99
|
+
raise XWNodeStrategyError(f"Target edge strategy {target_mode.name} not available")
|
100
|
+
|
101
|
+
# Analyze compatibility
|
102
|
+
self._analyze_edge_compatibility(plan, source_mode, target_mode,
|
103
|
+
source_traits, target_traits, edge_count, vertex_count)
|
104
|
+
|
105
|
+
return plan
|
106
|
+
|
107
|
+
def _analyze_node_compatibility(self, plan: MigrationPlan, source: NodeMode, target: NodeMode,
|
108
|
+
source_traits: NodeTrait, target_traits: NodeTrait,
|
109
|
+
data_size: int) -> None:
|
110
|
+
"""Analyze compatibility between node strategies."""
|
111
|
+
# Define migration rules and costs
|
112
|
+
migration_matrix = {
|
113
|
+
# From LEGACY
|
114
|
+
(NodeMode.LEGACY, NodeMode.HASH_MAP): {'cost': 1, 'risk': 'none', 'ops': ['copy_dict']},
|
115
|
+
(NodeMode.LEGACY, NodeMode.ARRAY_LIST): {'cost': 1, 'risk': 'low', 'ops': ['copy_list']},
|
116
|
+
(NodeMode.LEGACY, NodeMode.TRIE): {'cost': 2, 'risk': 'low', 'ops': ['copy_dict', 'build_trie']},
|
117
|
+
(NodeMode.LEGACY, NodeMode.HEAP): {'cost': 3, 'risk': 'medium', 'ops': ['extract_values', 'heapify']},
|
118
|
+
(NodeMode.LEGACY, NodeMode.B_TREE): {'cost': 3, 'risk': 'low', 'ops': ['sort_keys', 'build_btree']},
|
119
|
+
|
120
|
+
# From HASH_MAP
|
121
|
+
(NodeMode.HASH_MAP, NodeMode.LEGACY): {'cost': 1, 'risk': 'none', 'ops': ['copy_dict']},
|
122
|
+
(NodeMode.HASH_MAP, NodeMode.ARRAY_LIST): {'cost': 2, 'risk': 'high', 'ops': ['convert_dict_to_list']},
|
123
|
+
(NodeMode.HASH_MAP, NodeMode.TRIE): {'cost': 2, 'risk': 'low', 'ops': ['rebuild_trie']},
|
124
|
+
(NodeMode.HASH_MAP, NodeMode.B_TREE): {'cost': 2, 'risk': 'low', 'ops': ['sort_rebuild']},
|
125
|
+
|
126
|
+
# From ARRAY_LIST
|
127
|
+
(NodeMode.ARRAY_LIST, NodeMode.LEGACY): {'cost': 1, 'risk': 'none', 'ops': ['copy_list']},
|
128
|
+
(NodeMode.ARRAY_LIST, NodeMode.HASH_MAP): {'cost': 1, 'risk': 'low', 'ops': ['index_as_keys']},
|
129
|
+
(NodeMode.ARRAY_LIST, NodeMode.HEAP): {'cost': 2, 'risk': 'low', 'ops': ['heapify_list']},
|
130
|
+
|
131
|
+
# Specialized migrations
|
132
|
+
(NodeMode.HEAP, NodeMode.ARRAY_LIST): {'cost': 2, 'risk': 'medium', 'ops': ['heap_sort', 'to_list']},
|
133
|
+
(NodeMode.B_TREE, NodeMode.HASH_MAP): {'cost': 2, 'risk': 'low', 'ops': ['flatten_tree']},
|
134
|
+
(NodeMode.TRIE, NodeMode.HASH_MAP): {'cost': 2, 'risk': 'low', 'ops': ['flatten_trie']},
|
135
|
+
}
|
136
|
+
|
137
|
+
migration_key = (source, target)
|
138
|
+
if migration_key in migration_matrix:
|
139
|
+
rules = migration_matrix[migration_key]
|
140
|
+
plan.cost_estimate = rules['cost']
|
141
|
+
plan.data_loss_risk = rules['risk']
|
142
|
+
for op in rules['ops']:
|
143
|
+
plan.add_operation(op)
|
144
|
+
else:
|
145
|
+
# Default fallback through LEGACY
|
146
|
+
plan.cost_estimate = 4
|
147
|
+
plan.data_loss_risk = 'medium'
|
148
|
+
plan.add_operation('migrate_via_legacy')
|
149
|
+
plan.add_warning(f"No direct migration path from {source.name} to {target.name}")
|
150
|
+
|
151
|
+
# Adjust based on data size
|
152
|
+
if data_size > 10000:
|
153
|
+
plan.cost_estimate += 2
|
154
|
+
plan.estimated_time_ms = data_size * 0.1
|
155
|
+
elif data_size > 1000:
|
156
|
+
plan.cost_estimate += 1
|
157
|
+
plan.estimated_time_ms = data_size * 0.05
|
158
|
+
else:
|
159
|
+
plan.estimated_time_ms = max(10, data_size * 0.01)
|
160
|
+
|
161
|
+
# Trait compatibility warnings
|
162
|
+
self._check_trait_compatibility(plan, source_traits, target_traits, 'node')
|
163
|
+
|
164
|
+
def _analyze_edge_compatibility(self, plan: MigrationPlan, source: EdgeMode, target: EdgeMode,
|
165
|
+
source_traits: EdgeTrait, target_traits: EdgeTrait,
|
166
|
+
edge_count: int, vertex_count: int) -> None:
|
167
|
+
"""Analyze compatibility between edge strategies."""
|
168
|
+
# Define edge migration rules
|
169
|
+
migration_matrix = {
|
170
|
+
# From LEGACY
|
171
|
+
(EdgeMode.LEGACY, EdgeMode.ADJ_LIST): {'cost': 2, 'risk': 'low', 'ops': ['extract_edges', 'build_adj_list']},
|
172
|
+
(EdgeMode.LEGACY, EdgeMode.ADJ_MATRIX): {'cost': 3, 'risk': 'medium', 'ops': ['extract_edges', 'build_matrix']},
|
173
|
+
(EdgeMode.LEGACY, EdgeMode.CSR): {'cost': 3, 'risk': 'low', 'ops': ['extract_edges', 'build_csr']},
|
174
|
+
|
175
|
+
# Between modern formats
|
176
|
+
(EdgeMode.ADJ_LIST, EdgeMode.ADJ_MATRIX): {'cost': 2, 'risk': 'low', 'ops': ['list_to_matrix']},
|
177
|
+
(EdgeMode.ADJ_LIST, EdgeMode.CSR): {'cost': 2, 'risk': 'low', 'ops': ['list_to_csr']},
|
178
|
+
(EdgeMode.ADJ_MATRIX, EdgeMode.ADJ_LIST): {'cost': 2, 'risk': 'low', 'ops': ['matrix_to_list']},
|
179
|
+
(EdgeMode.ADJ_MATRIX, EdgeMode.CSR): {'cost': 2, 'risk': 'low', 'ops': ['matrix_to_csr']},
|
180
|
+
(EdgeMode.CSR, EdgeMode.ADJ_LIST): {'cost': 2, 'risk': 'low', 'ops': ['csr_to_list']},
|
181
|
+
(EdgeMode.CSR, EdgeMode.ADJ_MATRIX): {'cost': 3, 'risk': 'medium', 'ops': ['csr_to_matrix']},
|
182
|
+
}
|
183
|
+
|
184
|
+
migration_key = (source, target)
|
185
|
+
if migration_key in migration_matrix:
|
186
|
+
rules = migration_matrix[migration_key]
|
187
|
+
plan.cost_estimate = rules['cost']
|
188
|
+
plan.data_loss_risk = rules['risk']
|
189
|
+
for op in rules['ops']:
|
190
|
+
plan.add_operation(op)
|
191
|
+
else:
|
192
|
+
# Default fallback
|
193
|
+
plan.cost_estimate = 5
|
194
|
+
plan.data_loss_risk = 'high'
|
195
|
+
plan.add_operation('migrate_via_legacy')
|
196
|
+
plan.add_warning(f"No direct migration path from {source.name} to {target.name}")
|
197
|
+
|
198
|
+
# Graph density considerations
|
199
|
+
density = edge_count / max(1, vertex_count * (vertex_count - 1)) if vertex_count > 1 else 0
|
200
|
+
|
201
|
+
if target == EdgeMode.ADJ_MATRIX and density < 0.1:
|
202
|
+
plan.add_warning("Migrating sparse graph to dense matrix representation")
|
203
|
+
plan.cost_estimate += 1
|
204
|
+
elif target in [EdgeMode.ADJ_LIST, EdgeMode.CSR] and density > 0.7:
|
205
|
+
plan.add_warning("Migrating dense graph to sparse representation")
|
206
|
+
|
207
|
+
# Size-based adjustments
|
208
|
+
if edge_count > 100000:
|
209
|
+
plan.cost_estimate += 3
|
210
|
+
plan.estimated_time_ms = edge_count * 0.2
|
211
|
+
elif edge_count > 10000:
|
212
|
+
plan.cost_estimate += 2
|
213
|
+
plan.estimated_time_ms = edge_count * 0.1
|
214
|
+
else:
|
215
|
+
plan.estimated_time_ms = max(10, edge_count * 0.05)
|
216
|
+
|
217
|
+
# Trait compatibility
|
218
|
+
self._check_trait_compatibility(plan, source_traits, target_traits, 'edge')
|
219
|
+
|
220
|
+
def _check_trait_compatibility(self, plan: MigrationPlan, source_traits: Any,
|
221
|
+
target_traits: Any, strategy_type: str) -> None:
|
222
|
+
"""Check trait compatibility and add warnings."""
|
223
|
+
if source_traits == target_traits:
|
224
|
+
return
|
225
|
+
|
226
|
+
# Check for trait loss
|
227
|
+
lost_traits = source_traits & ~target_traits if source_traits else None
|
228
|
+
if lost_traits and lost_traits != 0:
|
229
|
+
plan.add_warning(f"Some {strategy_type} traits will be lost: {lost_traits}")
|
230
|
+
if plan.data_loss_risk == 'none':
|
231
|
+
plan.data_loss_risk = 'low'
|
232
|
+
|
233
|
+
# Check for new traits
|
234
|
+
new_traits = target_traits & ~source_traits if target_traits else None
|
235
|
+
if new_traits and new_traits != 0:
|
236
|
+
plan.add_operation(f"configure_{strategy_type}_traits")
|
237
|
+
|
238
|
+
# ============================================================================
|
239
|
+
# MIGRATION EXECUTION
|
240
|
+
# ============================================================================
|
241
|
+
|
242
|
+
def execute_node_migration(self, source_strategy: Any, target_mode: NodeMode,
|
243
|
+
target_traits: NodeTrait = NodeTrait.NONE,
|
244
|
+
**options) -> Any:
|
245
|
+
"""Execute migration from source node strategy to target."""
|
246
|
+
with self._migration_lock:
|
247
|
+
start_time = time.time()
|
248
|
+
|
249
|
+
# Extract data from source
|
250
|
+
source_data = source_strategy.to_native()
|
251
|
+
source_size = len(source_strategy) if hasattr(source_strategy, '__len__') else 0
|
252
|
+
|
253
|
+
# Create migration plan
|
254
|
+
source_mode = getattr(source_strategy, '_mode', NodeMode.LEGACY)
|
255
|
+
plan = self.plan_node_migration(source_mode, target_mode,
|
256
|
+
getattr(source_strategy, '_traits', NodeTrait.NONE),
|
257
|
+
target_traits, source_size)
|
258
|
+
|
259
|
+
logger.info(f"🔄 Executing node migration: {source_mode.name} → {target_mode.name}")
|
260
|
+
logger.info(f"📋 Operations: {', '.join(plan.required_operations)}")
|
261
|
+
|
262
|
+
# Create target strategy
|
263
|
+
target_strategy = self._registry.get_node_strategy(target_mode, traits=target_traits, **options)
|
264
|
+
|
265
|
+
# Migrate data based on type
|
266
|
+
try:
|
267
|
+
if isinstance(source_data, dict):
|
268
|
+
for key, value in source_data.items():
|
269
|
+
target_strategy.put(key, value)
|
270
|
+
elif isinstance(source_data, list):
|
271
|
+
for i, value in enumerate(source_data):
|
272
|
+
target_strategy.put(str(i), value)
|
273
|
+
else:
|
274
|
+
# Leaf node
|
275
|
+
target_strategy.put('value', source_data)
|
276
|
+
|
277
|
+
# Record migration
|
278
|
+
migration_record = {
|
279
|
+
'timestamp': time.time(),
|
280
|
+
'type': 'node',
|
281
|
+
'source_mode': source_mode.name,
|
282
|
+
'target_mode': target_mode.name,
|
283
|
+
'data_size': source_size,
|
284
|
+
'duration_ms': (time.time() - start_time) * 1000,
|
285
|
+
'success': True
|
286
|
+
}
|
287
|
+
self._migration_history.append(migration_record)
|
288
|
+
|
289
|
+
logger.info(f"✅ Node migration completed in {migration_record['duration_ms']:.1f}ms")
|
290
|
+
return target_strategy
|
291
|
+
|
292
|
+
except Exception as e:
|
293
|
+
logger.error(f"❌ Node migration failed: {e}")
|
294
|
+
migration_record = {
|
295
|
+
'timestamp': time.time(),
|
296
|
+
'type': 'node',
|
297
|
+
'source_mode': source_mode.name,
|
298
|
+
'target_mode': target_mode.name,
|
299
|
+
'data_size': source_size,
|
300
|
+
'duration_ms': (time.time() - start_time) * 1000,
|
301
|
+
'success': False,
|
302
|
+
'error': str(e)
|
303
|
+
}
|
304
|
+
self._migration_history.append(migration_record)
|
305
|
+
raise XWNodeStrategyError(f"Migration failed: {e}")
|
306
|
+
|
307
|
+
def execute_edge_migration(self, source_strategy: Any, target_mode: EdgeMode,
|
308
|
+
target_traits: EdgeTrait = EdgeTrait.NONE,
|
309
|
+
**options) -> Any:
|
310
|
+
"""Execute migration from source edge strategy to target."""
|
311
|
+
with self._migration_lock:
|
312
|
+
start_time = time.time()
|
313
|
+
|
314
|
+
# Extract data from source
|
315
|
+
edge_list = list(source_strategy.edges(data=True))
|
316
|
+
vertex_list = list(source_strategy.vertices())
|
317
|
+
|
318
|
+
# Create migration plan
|
319
|
+
source_mode = getattr(source_strategy, '_mode', EdgeMode.LEGACY)
|
320
|
+
plan = self.plan_edge_migration(source_mode, target_mode,
|
321
|
+
getattr(source_strategy, '_traits', EdgeTrait.NONE),
|
322
|
+
target_traits, len(edge_list), len(vertex_list))
|
323
|
+
|
324
|
+
logger.info(f"🔄 Executing edge migration: {source_mode.name} → {target_mode.name}")
|
325
|
+
logger.info(f"📊 Graph: {len(vertex_list)} vertices, {len(edge_list)} edges")
|
326
|
+
|
327
|
+
# Create target strategy
|
328
|
+
target_strategy = self._registry.get_edge_strategy(target_mode, traits=target_traits, **options)
|
329
|
+
|
330
|
+
# Migrate vertices and edges
|
331
|
+
try:
|
332
|
+
# Add vertices first
|
333
|
+
for vertex in vertex_list:
|
334
|
+
target_strategy.add_vertex(vertex)
|
335
|
+
|
336
|
+
# Add edges
|
337
|
+
for source, target, edge_data in edge_list:
|
338
|
+
properties = edge_data.get('properties', {})
|
339
|
+
target_strategy.add_edge(source, target, **properties)
|
340
|
+
|
341
|
+
# Record migration
|
342
|
+
migration_record = {
|
343
|
+
'timestamp': time.time(),
|
344
|
+
'type': 'edge',
|
345
|
+
'source_mode': source_mode.name,
|
346
|
+
'target_mode': target_mode.name,
|
347
|
+
'edge_count': len(edge_list),
|
348
|
+
'vertex_count': len(vertex_list),
|
349
|
+
'duration_ms': (time.time() - start_time) * 1000,
|
350
|
+
'success': True
|
351
|
+
}
|
352
|
+
self._migration_history.append(migration_record)
|
353
|
+
|
354
|
+
logger.info(f"✅ Edge migration completed in {migration_record['duration_ms']:.1f}ms")
|
355
|
+
return target_strategy
|
356
|
+
|
357
|
+
except Exception as e:
|
358
|
+
logger.error(f"❌ Edge migration failed: {e}")
|
359
|
+
migration_record = {
|
360
|
+
'timestamp': time.time(),
|
361
|
+
'type': 'edge',
|
362
|
+
'source_mode': source_mode.name,
|
363
|
+
'target_mode': target_mode.name,
|
364
|
+
'edge_count': len(edge_list),
|
365
|
+
'vertex_count': len(vertex_list),
|
366
|
+
'duration_ms': (time.time() - start_time) * 1000,
|
367
|
+
'success': False,
|
368
|
+
'error': str(e)
|
369
|
+
}
|
370
|
+
self._migration_history.append(migration_record)
|
371
|
+
raise XWNodeStrategyError(f"Edge migration failed: {e}")
|
372
|
+
|
373
|
+
# ============================================================================
|
374
|
+
# MIGRATION HISTORY AND ANALYSIS
|
375
|
+
# ============================================================================
|
376
|
+
|
377
|
+
def get_migration_history(self) -> List[Dict[str, Any]]:
|
378
|
+
"""Get the history of all migrations."""
|
379
|
+
return self._migration_history.copy()
|
380
|
+
|
381
|
+
def get_migration_stats(self) -> Dict[str, Any]:
|
382
|
+
"""Get statistics about migrations."""
|
383
|
+
if not self._migration_history:
|
384
|
+
return {'total_migrations': 0}
|
385
|
+
|
386
|
+
successful = [m for m in self._migration_history if m['success']]
|
387
|
+
failed = [m for m in self._migration_history if not m['success']]
|
388
|
+
|
389
|
+
avg_duration = sum(m['duration_ms'] for m in successful) / len(successful) if successful else 0
|
390
|
+
|
391
|
+
return {
|
392
|
+
'total_migrations': len(self._migration_history),
|
393
|
+
'successful': len(successful),
|
394
|
+
'failed': len(failed),
|
395
|
+
'success_rate': len(successful) / len(self._migration_history) * 100,
|
396
|
+
'average_duration_ms': round(avg_duration, 2),
|
397
|
+
'most_common_source': self._most_common_field('source_mode'),
|
398
|
+
'most_common_target': self._most_common_field('target_mode')
|
399
|
+
}
|
400
|
+
|
401
|
+
def _most_common_field(self, field: str) -> Optional[str]:
|
402
|
+
"""Find the most common value for a field in migration history."""
|
403
|
+
if not self._migration_history:
|
404
|
+
return None
|
405
|
+
|
406
|
+
from collections import Counter
|
407
|
+
values = [m.get(field) for m in self._migration_history if field in m]
|
408
|
+
if not values:
|
409
|
+
return None
|
410
|
+
|
411
|
+
counter = Counter(values)
|
412
|
+
return counter.most_common(1)[0][0]
|
413
|
+
|
414
|
+
def clear_migration_history(self) -> None:
|
415
|
+
"""Clear the migration history."""
|
416
|
+
self._migration_history.clear()
|
417
|
+
logger.info("🧹 Migration history cleared")
|
418
|
+
|
419
|
+
|
420
|
+
# Global migrator instance
|
421
|
+
_migrator = None
|
422
|
+
_migrator_lock = threading.Lock()
|
423
|
+
|
424
|
+
|
425
|
+
def get_migrator() -> xStrategyMigrator:
|
426
|
+
"""Get the global strategy migrator instance."""
|
427
|
+
global _migrator
|
428
|
+
if _migrator is None:
|
429
|
+
with _migrator_lock:
|
430
|
+
if _migrator is None:
|
431
|
+
_migrator = xStrategyMigrator()
|
432
|
+
return _migrator
|
@@ -0,0 +1,50 @@
|
|
1
|
+
"""
|
2
|
+
Node Strategies Package
|
3
|
+
|
4
|
+
This package contains all node strategy implementations organized by type:
|
5
|
+
- Linear strategies (arrays, lists, stacks, queues)
|
6
|
+
- Tree strategies (tries, heaps, BSTs)
|
7
|
+
- Graph strategies (union-find, neural graphs)
|
8
|
+
|
9
|
+
Company: eXonware.com
|
10
|
+
Author: Eng. Muhammad AlShehri
|
11
|
+
Email: connect@exonware.com
|
12
|
+
Version: 0.0.1.12
|
13
|
+
Generation Date: January 2, 2025
|
14
|
+
"""
|
15
|
+
|
16
|
+
from .base import ANodeStrategy, ANodeLinearStrategy, ANodeTreeStrategy, ANodeGraphStrategy
|
17
|
+
|
18
|
+
# Linear strategies
|
19
|
+
from .array_list import ArrayListStrategy
|
20
|
+
from .linked_list import LinkedListStrategy
|
21
|
+
|
22
|
+
# Tree strategies
|
23
|
+
from .trie import xTrieStrategy
|
24
|
+
from .heap import xHeapStrategy
|
25
|
+
from .aho_corasick import xAhoCorasickStrategy
|
26
|
+
|
27
|
+
# Graph strategies
|
28
|
+
from .hash_map import HashMapStrategy
|
29
|
+
from .union_find import xUnionFindStrategy
|
30
|
+
|
31
|
+
__all__ = [
|
32
|
+
# Base classes
|
33
|
+
'ANodeStrategy',
|
34
|
+
'ANodeLinearStrategy',
|
35
|
+
'ANodeTreeStrategy',
|
36
|
+
'ANodeGraphStrategy',
|
37
|
+
|
38
|
+
# Linear strategies
|
39
|
+
'ArrayListStrategy',
|
40
|
+
'LinkedListStrategy',
|
41
|
+
|
42
|
+
# Tree strategies
|
43
|
+
'xTrieStrategy',
|
44
|
+
'xHeapStrategy',
|
45
|
+
'xAhoCorasickStrategy',
|
46
|
+
|
47
|
+
# Graph strategies
|
48
|
+
'HashMapStrategy',
|
49
|
+
'xUnionFindStrategy'
|
50
|
+
]
|