exonware-xwnode 0.0.1.21__py3-none-any.whl → 0.0.1.23__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 +8 -1
- exonware/xwnode/__init__.py +18 -5
- exonware/xwnode/add_strategy_types.py +165 -0
- exonware/xwnode/base.py +7 -5
- exonware/xwnode/common/__init__.py +1 -1
- exonware/xwnode/common/graph/__init__.py +30 -0
- exonware/xwnode/common/graph/caching.py +131 -0
- exonware/xwnode/common/graph/contracts.py +100 -0
- exonware/xwnode/common/graph/errors.py +44 -0
- exonware/xwnode/common/graph/indexing.py +260 -0
- exonware/xwnode/common/graph/manager.py +568 -0
- exonware/xwnode/common/management/__init__.py +3 -5
- exonware/xwnode/common/management/manager.py +9 -9
- exonware/xwnode/common/management/migration.py +6 -6
- exonware/xwnode/common/monitoring/__init__.py +3 -5
- exonware/xwnode/common/monitoring/metrics.py +7 -3
- exonware/xwnode/common/monitoring/pattern_detector.py +2 -2
- exonware/xwnode/common/monitoring/performance_monitor.py +6 -2
- exonware/xwnode/common/patterns/__init__.py +3 -5
- exonware/xwnode/common/patterns/advisor.py +1 -1
- exonware/xwnode/common/patterns/flyweight.py +6 -2
- exonware/xwnode/common/patterns/registry.py +203 -184
- exonware/xwnode/common/utils/__init__.py +25 -11
- exonware/xwnode/common/utils/simple.py +1 -1
- exonware/xwnode/config.py +3 -8
- exonware/xwnode/contracts.py +4 -105
- exonware/xwnode/defs.py +413 -159
- exonware/xwnode/edges/strategies/__init__.py +86 -4
- exonware/xwnode/edges/strategies/_base_edge.py +2 -2
- exonware/xwnode/edges/strategies/adj_list.py +287 -121
- exonware/xwnode/edges/strategies/adj_matrix.py +316 -222
- exonware/xwnode/edges/strategies/base.py +1 -1
- exonware/xwnode/edges/strategies/{edge_bidir_wrapper.py → bidir_wrapper.py} +45 -4
- exonware/xwnode/edges/strategies/bitemporal.py +520 -0
- exonware/xwnode/edges/strategies/{edge_block_adj_matrix.py → block_adj_matrix.py} +77 -6
- exonware/xwnode/edges/strategies/bv_graph.py +664 -0
- exonware/xwnode/edges/strategies/compressed_graph.py +217 -0
- exonware/xwnode/edges/strategies/{edge_coo.py → coo.py} +46 -4
- exonware/xwnode/edges/strategies/{edge_csc.py → csc.py} +45 -4
- exonware/xwnode/edges/strategies/{edge_csr.py → csr.py} +94 -12
- exonware/xwnode/edges/strategies/{edge_dynamic_adj_list.py → dynamic_adj_list.py} +46 -4
- exonware/xwnode/edges/strategies/edge_list.py +168 -0
- exonware/xwnode/edges/strategies/edge_property_store.py +2 -2
- exonware/xwnode/edges/strategies/euler_tour.py +560 -0
- exonware/xwnode/edges/strategies/{edge_flow_network.py → flow_network.py} +2 -2
- exonware/xwnode/edges/strategies/graphblas.py +449 -0
- exonware/xwnode/edges/strategies/hnsw.py +637 -0
- exonware/xwnode/edges/strategies/hop2_labels.py +467 -0
- exonware/xwnode/edges/strategies/{edge_hyperedge_set.py → hyperedge_set.py} +2 -2
- exonware/xwnode/edges/strategies/incidence_matrix.py +250 -0
- exonware/xwnode/edges/strategies/k2_tree.py +613 -0
- exonware/xwnode/edges/strategies/link_cut.py +626 -0
- exonware/xwnode/edges/strategies/multiplex.py +532 -0
- exonware/xwnode/edges/strategies/{edge_neural_graph.py → neural_graph.py} +2 -2
- exonware/xwnode/edges/strategies/{edge_octree.py → octree.py} +69 -11
- exonware/xwnode/edges/strategies/{edge_quadtree.py → quadtree.py} +66 -10
- exonware/xwnode/edges/strategies/roaring_adj.py +438 -0
- exonware/xwnode/edges/strategies/{edge_rtree.py → rtree.py} +43 -5
- exonware/xwnode/edges/strategies/{edge_temporal_edgeset.py → temporal_edgeset.py} +24 -5
- exonware/xwnode/edges/strategies/{edge_tree_graph_basic.py → tree_graph_basic.py} +78 -7
- exonware/xwnode/edges/strategies/{edge_weighted_graph.py → weighted_graph.py} +188 -10
- exonware/xwnode/errors.py +3 -6
- exonware/xwnode/facade.py +20 -20
- exonware/xwnode/nodes/strategies/__init__.py +29 -9
- exonware/xwnode/nodes/strategies/adjacency_list.py +650 -177
- exonware/xwnode/nodes/strategies/aho_corasick.py +358 -183
- exonware/xwnode/nodes/strategies/array_list.py +36 -3
- exonware/xwnode/nodes/strategies/art.py +581 -0
- exonware/xwnode/nodes/strategies/{node_avl_tree.py → avl_tree.py} +77 -6
- exonware/xwnode/nodes/strategies/{node_b_plus_tree.py → b_plus_tree.py} +81 -40
- exonware/xwnode/nodes/strategies/{node_btree.py → b_tree.py} +79 -9
- exonware/xwnode/nodes/strategies/base.py +469 -98
- exonware/xwnode/nodes/strategies/{node_bitmap.py → bitmap.py} +12 -12
- exonware/xwnode/nodes/strategies/{node_bitset_dynamic.py → bitset_dynamic.py} +11 -11
- exonware/xwnode/nodes/strategies/{node_bloom_filter.py → bloom_filter.py} +15 -2
- exonware/xwnode/nodes/strategies/bloomier_filter.py +519 -0
- exonware/xwnode/nodes/strategies/bw_tree.py +531 -0
- exonware/xwnode/nodes/strategies/contracts.py +1 -1
- exonware/xwnode/nodes/strategies/{node_count_min_sketch.py → count_min_sketch.py} +3 -2
- exonware/xwnode/nodes/strategies/{node_cow_tree.py → cow_tree.py} +135 -13
- exonware/xwnode/nodes/strategies/crdt_map.py +629 -0
- exonware/xwnode/nodes/strategies/{node_cuckoo_hash.py → cuckoo_hash.py} +2 -2
- exonware/xwnode/nodes/strategies/{node_xdata_optimized.py → data_interchange_optimized.py} +21 -4
- exonware/xwnode/nodes/strategies/dawg.py +876 -0
- exonware/xwnode/nodes/strategies/deque.py +321 -153
- exonware/xwnode/nodes/strategies/extendible_hash.py +93 -0
- exonware/xwnode/nodes/strategies/{node_fenwick_tree.py → fenwick_tree.py} +111 -19
- exonware/xwnode/nodes/strategies/hamt.py +403 -0
- exonware/xwnode/nodes/strategies/hash_map.py +354 -67
- exonware/xwnode/nodes/strategies/heap.py +105 -5
- exonware/xwnode/nodes/strategies/hopscotch_hash.py +525 -0
- exonware/xwnode/nodes/strategies/{node_hyperloglog.py → hyperloglog.py} +6 -5
- exonware/xwnode/nodes/strategies/interval_tree.py +742 -0
- exonware/xwnode/nodes/strategies/kd_tree.py +703 -0
- exonware/xwnode/nodes/strategies/learned_index.py +533 -0
- exonware/xwnode/nodes/strategies/linear_hash.py +93 -0
- exonware/xwnode/nodes/strategies/linked_list.py +316 -119
- exonware/xwnode/nodes/strategies/{node_lsm_tree.py → lsm_tree.py} +219 -15
- exonware/xwnode/nodes/strategies/masstree.py +130 -0
- exonware/xwnode/nodes/strategies/{node_persistent_tree.py → persistent_tree.py} +149 -9
- exonware/xwnode/nodes/strategies/priority_queue.py +544 -132
- exonware/xwnode/nodes/strategies/queue.py +249 -120
- exonware/xwnode/nodes/strategies/{node_red_black_tree.py → red_black_tree.py} +183 -72
- exonware/xwnode/nodes/strategies/{node_roaring_bitmap.py → roaring_bitmap.py} +19 -6
- exonware/xwnode/nodes/strategies/rope.py +717 -0
- exonware/xwnode/nodes/strategies/{node_segment_tree.py → segment_tree.py} +106 -106
- exonware/xwnode/nodes/strategies/{node_set_hash.py → set_hash.py} +30 -29
- exonware/xwnode/nodes/strategies/{node_skip_list.py → skip_list.py} +74 -6
- exonware/xwnode/nodes/strategies/sparse_matrix.py +427 -131
- exonware/xwnode/nodes/strategies/{node_splay_tree.py → splay_tree.py} +55 -6
- exonware/xwnode/nodes/strategies/stack.py +244 -112
- exonware/xwnode/nodes/strategies/{node_suffix_array.py → suffix_array.py} +5 -1
- exonware/xwnode/nodes/strategies/t_tree.py +94 -0
- exonware/xwnode/nodes/strategies/{node_treap.py → treap.py} +75 -6
- exonware/xwnode/nodes/strategies/{node_tree_graph_hybrid.py → tree_graph_hybrid.py} +46 -5
- exonware/xwnode/nodes/strategies/trie.py +153 -9
- exonware/xwnode/nodes/strategies/union_find.py +111 -5
- exonware/xwnode/nodes/strategies/veb_tree.py +856 -0
- exonware/xwnode/strategies/__init__.py +5 -51
- exonware/xwnode/version.py +3 -3
- {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/METADATA +23 -3
- exonware_xwnode-0.0.1.23.dist-info/RECORD +130 -0
- exonware/xwnode/edges/strategies/edge_adj_list.py +0 -353
- exonware/xwnode/edges/strategies/edge_adj_matrix.py +0 -445
- exonware/xwnode/nodes/strategies/_base_node.py +0 -307
- exonware/xwnode/nodes/strategies/node_aho_corasick.py +0 -525
- exonware/xwnode/nodes/strategies/node_array_list.py +0 -179
- exonware/xwnode/nodes/strategies/node_hash_map.py +0 -273
- exonware/xwnode/nodes/strategies/node_heap.py +0 -196
- exonware/xwnode/nodes/strategies/node_linked_list.py +0 -413
- exonware/xwnode/nodes/strategies/node_trie.py +0 -257
- exonware/xwnode/nodes/strategies/node_union_find.py +0 -192
- exonware/xwnode/queries/executors/__init__.py +0 -47
- exonware/xwnode/queries/executors/advanced/__init__.py +0 -37
- exonware/xwnode/queries/executors/advanced/aggregate_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/ask_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/construct_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/describe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/for_loop_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/foreach_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/join_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/let_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/mutation_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/options_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/pipe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/subscribe_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/subscription_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/union_executor.py +0 -50
- exonware/xwnode/queries/executors/advanced/window_executor.py +0 -51
- exonware/xwnode/queries/executors/advanced/with_cte_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/__init__.py +0 -21
- exonware/xwnode/queries/executors/aggregation/avg_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/count_executor.py +0 -38
- exonware/xwnode/queries/executors/aggregation/distinct_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/group_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/having_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/max_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/min_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/sum_executor.py +0 -50
- exonware/xwnode/queries/executors/aggregation/summarize_executor.py +0 -50
- exonware/xwnode/queries/executors/array/__init__.py +0 -9
- exonware/xwnode/queries/executors/array/indexing_executor.py +0 -51
- exonware/xwnode/queries/executors/array/slicing_executor.py +0 -51
- exonware/xwnode/queries/executors/base.py +0 -257
- exonware/xwnode/queries/executors/capability_checker.py +0 -204
- exonware/xwnode/queries/executors/contracts.py +0 -166
- exonware/xwnode/queries/executors/core/__init__.py +0 -17
- exonware/xwnode/queries/executors/core/create_executor.py +0 -96
- exonware/xwnode/queries/executors/core/delete_executor.py +0 -99
- exonware/xwnode/queries/executors/core/drop_executor.py +0 -100
- exonware/xwnode/queries/executors/core/insert_executor.py +0 -39
- exonware/xwnode/queries/executors/core/select_executor.py +0 -152
- exonware/xwnode/queries/executors/core/update_executor.py +0 -102
- exonware/xwnode/queries/executors/data/__init__.py +0 -13
- exonware/xwnode/queries/executors/data/alter_executor.py +0 -50
- exonware/xwnode/queries/executors/data/load_executor.py +0 -50
- exonware/xwnode/queries/executors/data/merge_executor.py +0 -50
- exonware/xwnode/queries/executors/data/store_executor.py +0 -50
- exonware/xwnode/queries/executors/defs.py +0 -93
- exonware/xwnode/queries/executors/engine.py +0 -221
- exonware/xwnode/queries/executors/errors.py +0 -68
- exonware/xwnode/queries/executors/filtering/__init__.py +0 -25
- exonware/xwnode/queries/executors/filtering/between_executor.py +0 -80
- exonware/xwnode/queries/executors/filtering/filter_executor.py +0 -79
- exonware/xwnode/queries/executors/filtering/has_executor.py +0 -70
- exonware/xwnode/queries/executors/filtering/in_executor.py +0 -70
- exonware/xwnode/queries/executors/filtering/like_executor.py +0 -76
- exonware/xwnode/queries/executors/filtering/optional_executor.py +0 -76
- exonware/xwnode/queries/executors/filtering/range_executor.py +0 -80
- exonware/xwnode/queries/executors/filtering/term_executor.py +0 -77
- exonware/xwnode/queries/executors/filtering/values_executor.py +0 -71
- exonware/xwnode/queries/executors/filtering/where_executor.py +0 -44
- exonware/xwnode/queries/executors/graph/__init__.py +0 -15
- exonware/xwnode/queries/executors/graph/in_traverse_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/match_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/out_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/path_executor.py +0 -51
- exonware/xwnode/queries/executors/graph/return_executor.py +0 -51
- exonware/xwnode/queries/executors/ordering/__init__.py +0 -9
- exonware/xwnode/queries/executors/ordering/by_executor.py +0 -50
- exonware/xwnode/queries/executors/ordering/order_executor.py +0 -51
- exonware/xwnode/queries/executors/projection/__init__.py +0 -9
- exonware/xwnode/queries/executors/projection/extend_executor.py +0 -50
- exonware/xwnode/queries/executors/projection/project_executor.py +0 -50
- exonware/xwnode/queries/executors/registry.py +0 -173
- exonware/xwnode/queries/parsers/__init__.py +0 -26
- exonware/xwnode/queries/parsers/base.py +0 -86
- exonware/xwnode/queries/parsers/contracts.py +0 -46
- exonware/xwnode/queries/parsers/errors.py +0 -53
- exonware/xwnode/queries/parsers/sql_param_extractor.py +0 -318
- exonware/xwnode/queries/strategies/__init__.py +0 -24
- exonware/xwnode/queries/strategies/base.py +0 -236
- exonware/xwnode/queries/strategies/cql.py +0 -201
- exonware/xwnode/queries/strategies/cypher.py +0 -181
- exonware/xwnode/queries/strategies/datalog.py +0 -70
- exonware/xwnode/queries/strategies/elastic_dsl.py +0 -70
- exonware/xwnode/queries/strategies/eql.py +0 -70
- exonware/xwnode/queries/strategies/flux.py +0 -70
- exonware/xwnode/queries/strategies/gql.py +0 -70
- exonware/xwnode/queries/strategies/graphql.py +0 -240
- exonware/xwnode/queries/strategies/gremlin.py +0 -181
- exonware/xwnode/queries/strategies/hiveql.py +0 -214
- exonware/xwnode/queries/strategies/hql.py +0 -70
- exonware/xwnode/queries/strategies/jmespath.py +0 -219
- exonware/xwnode/queries/strategies/jq.py +0 -66
- exonware/xwnode/queries/strategies/json_query.py +0 -66
- exonware/xwnode/queries/strategies/jsoniq.py +0 -248
- exonware/xwnode/queries/strategies/kql.py +0 -70
- exonware/xwnode/queries/strategies/linq.py +0 -238
- exonware/xwnode/queries/strategies/logql.py +0 -70
- exonware/xwnode/queries/strategies/mql.py +0 -68
- exonware/xwnode/queries/strategies/n1ql.py +0 -210
- exonware/xwnode/queries/strategies/partiql.py +0 -70
- exonware/xwnode/queries/strategies/pig.py +0 -215
- exonware/xwnode/queries/strategies/promql.py +0 -70
- exonware/xwnode/queries/strategies/sparql.py +0 -220
- exonware/xwnode/queries/strategies/sql.py +0 -275
- exonware/xwnode/queries/strategies/xml_query.py +0 -66
- exonware/xwnode/queries/strategies/xpath.py +0 -223
- exonware/xwnode/queries/strategies/xquery.py +0 -258
- exonware/xwnode/queries/strategies/xwnode_executor.py +0 -332
- exonware/xwnode/queries/strategies/xwquery.py +0 -456
- exonware_xwnode-0.0.1.21.dist-info/RECORD +0 -214
- /exonware/xwnode/nodes/strategies/{node_ordered_map.py → ordered_map.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_ordered_map_balanced.py → ordered_map_balanced.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_patricia.py → patricia.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_radix_trie.py → radix_trie.py} +0 -0
- /exonware/xwnode/nodes/strategies/{node_set_tree.py → set_tree.py} +0 -0
- {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/WHEEL +0 -0
- {exonware_xwnode-0.0.1.21.dist-info → exonware_xwnode-0.0.1.23.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,260 @@
|
|
1
|
+
"""
|
2
|
+
#exonware/xwnode/src/exonware/xwnode/common/graph/indexing.py
|
3
|
+
|
4
|
+
Multi-index manager for O(1) relationship lookups.
|
5
|
+
|
6
|
+
Company: eXonware.com
|
7
|
+
Author: Eng. Muhammad AlShehri
|
8
|
+
Email: connect@exonware.com
|
9
|
+
Version: 0.0.1.23
|
10
|
+
Generation Date: 11-Oct-2025
|
11
|
+
"""
|
12
|
+
|
13
|
+
import threading
|
14
|
+
from typing import Dict, List, Optional, Any
|
15
|
+
from collections import defaultdict
|
16
|
+
|
17
|
+
|
18
|
+
class IndexManager:
|
19
|
+
"""
|
20
|
+
Maintains multiple indexes for fast relationship queries.
|
21
|
+
|
22
|
+
Provides O(1) average case lookups for:
|
23
|
+
- Outgoing relationships by source entity
|
24
|
+
- Incoming relationships by target entity
|
25
|
+
- Relationships by type
|
26
|
+
"""
|
27
|
+
|
28
|
+
def __init__(self):
|
29
|
+
"""Initialize index manager with empty indexes."""
|
30
|
+
# Index 1: Outgoing relationships by source
|
31
|
+
# source_id -> {relationship_type -> [relationship_data]}
|
32
|
+
self._outgoing_index: Dict[str, Dict[str, List[Dict[str, Any]]]] = defaultdict(lambda: defaultdict(list))
|
33
|
+
|
34
|
+
# Index 2: Incoming relationships by target
|
35
|
+
# target_id -> {relationship_type -> [relationship_data]}
|
36
|
+
self._incoming_index: Dict[str, Dict[str, List[Dict[str, Any]]]] = defaultdict(lambda: defaultdict(list))
|
37
|
+
|
38
|
+
# Index 3: All relationships by ID
|
39
|
+
# relationship_id -> relationship_data
|
40
|
+
self._relationships: Dict[str, Dict[str, Any]] = {}
|
41
|
+
|
42
|
+
# Relationship ID counter
|
43
|
+
self._rel_id_counter = 0
|
44
|
+
self._lock = threading.RLock()
|
45
|
+
|
46
|
+
def add_relationship(
|
47
|
+
self,
|
48
|
+
source: str,
|
49
|
+
target: str,
|
50
|
+
relationship_type: str,
|
51
|
+
**properties
|
52
|
+
) -> str:
|
53
|
+
"""
|
54
|
+
Add relationship to all indexes.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
source: Source entity ID
|
58
|
+
target: Target entity ID
|
59
|
+
relationship_type: Type of relationship
|
60
|
+
**properties: Additional relationship properties
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
Relationship ID
|
64
|
+
|
65
|
+
Time Complexity: O(1) amortized
|
66
|
+
"""
|
67
|
+
with self._lock:
|
68
|
+
# Generate unique relationship ID
|
69
|
+
rel_id = f"rel_{self._rel_id_counter}"
|
70
|
+
self._rel_id_counter += 1
|
71
|
+
|
72
|
+
# Create relationship data
|
73
|
+
rel_data = {
|
74
|
+
'id': rel_id,
|
75
|
+
'source': source,
|
76
|
+
'target': target,
|
77
|
+
'type': relationship_type,
|
78
|
+
**properties
|
79
|
+
}
|
80
|
+
|
81
|
+
# Store in main dict
|
82
|
+
self._relationships[rel_id] = rel_data
|
83
|
+
|
84
|
+
# Index by source (outgoing)
|
85
|
+
self._outgoing_index[source][relationship_type].append(rel_data)
|
86
|
+
|
87
|
+
# Index by target (incoming)
|
88
|
+
self._incoming_index[target][relationship_type].append(rel_data)
|
89
|
+
|
90
|
+
return rel_id
|
91
|
+
|
92
|
+
def remove_relationship(
|
93
|
+
self,
|
94
|
+
source: str,
|
95
|
+
target: str,
|
96
|
+
relationship_type: Optional[str] = None
|
97
|
+
) -> bool:
|
98
|
+
"""
|
99
|
+
Remove relationship(s) between entities.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
source: Source entity ID
|
103
|
+
target: Target entity ID
|
104
|
+
relationship_type: Optional type filter
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
True if removed, False if not found
|
108
|
+
|
109
|
+
Time Complexity: O(degree) where degree is number of relationships for entity
|
110
|
+
"""
|
111
|
+
with self._lock:
|
112
|
+
removed = False
|
113
|
+
|
114
|
+
# Find matching relationships
|
115
|
+
to_remove = []
|
116
|
+
for rel_id, rel_data in self._relationships.items():
|
117
|
+
if rel_data['source'] == source and rel_data['target'] == target:
|
118
|
+
if relationship_type is None or rel_data['type'] == relationship_type:
|
119
|
+
to_remove.append(rel_id)
|
120
|
+
|
121
|
+
# Remove from all indexes
|
122
|
+
for rel_id in to_remove:
|
123
|
+
rel_data = self._relationships.pop(rel_id)
|
124
|
+
|
125
|
+
# Remove from outgoing index
|
126
|
+
source_rels = self._outgoing_index[source][rel_data['type']]
|
127
|
+
self._outgoing_index[source][rel_data['type']] = [
|
128
|
+
r for r in source_rels if r['id'] != rel_id
|
129
|
+
]
|
130
|
+
|
131
|
+
# Remove from incoming index
|
132
|
+
target_rels = self._incoming_index[target][rel_data['type']]
|
133
|
+
self._incoming_index[target][rel_data['type']] = [
|
134
|
+
r for r in target_rels if r['id'] != rel_id
|
135
|
+
]
|
136
|
+
|
137
|
+
removed = True
|
138
|
+
|
139
|
+
return removed
|
140
|
+
|
141
|
+
def query_outgoing(
|
142
|
+
self,
|
143
|
+
entity_id: str,
|
144
|
+
relationship_type: Optional[str] = None
|
145
|
+
) -> List[Dict[str, Any]]:
|
146
|
+
"""
|
147
|
+
Query outgoing relationships for entity.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
entity_id: Entity to query
|
151
|
+
relationship_type: Optional type filter
|
152
|
+
|
153
|
+
Returns:
|
154
|
+
List of relationship data dictionaries
|
155
|
+
|
156
|
+
Time Complexity: O(1) average case
|
157
|
+
"""
|
158
|
+
with self._lock:
|
159
|
+
if entity_id not in self._outgoing_index:
|
160
|
+
return []
|
161
|
+
|
162
|
+
if relationship_type:
|
163
|
+
# Return specific type
|
164
|
+
return self._outgoing_index[entity_id].get(relationship_type, []).copy()
|
165
|
+
|
166
|
+
# Return all types
|
167
|
+
results = []
|
168
|
+
for rels in self._outgoing_index[entity_id].values():
|
169
|
+
results.extend(rels)
|
170
|
+
return results
|
171
|
+
|
172
|
+
def query_incoming(
|
173
|
+
self,
|
174
|
+
entity_id: str,
|
175
|
+
relationship_type: Optional[str] = None
|
176
|
+
) -> List[Dict[str, Any]]:
|
177
|
+
"""
|
178
|
+
Query incoming relationships for entity.
|
179
|
+
|
180
|
+
Args:
|
181
|
+
entity_id: Entity to query
|
182
|
+
relationship_type: Optional type filter
|
183
|
+
|
184
|
+
Returns:
|
185
|
+
List of relationship data dictionaries
|
186
|
+
|
187
|
+
Time Complexity: O(1) average case
|
188
|
+
"""
|
189
|
+
with self._lock:
|
190
|
+
if entity_id not in self._incoming_index:
|
191
|
+
return []
|
192
|
+
|
193
|
+
if relationship_type:
|
194
|
+
# Return specific type
|
195
|
+
return self._incoming_index[entity_id].get(relationship_type, []).copy()
|
196
|
+
|
197
|
+
# Return all types
|
198
|
+
results = []
|
199
|
+
for rels in self._incoming_index[entity_id].values():
|
200
|
+
results.extend(rels)
|
201
|
+
return results
|
202
|
+
|
203
|
+
def has_relationship(
|
204
|
+
self,
|
205
|
+
source: str,
|
206
|
+
target: str,
|
207
|
+
relationship_type: Optional[str] = None
|
208
|
+
) -> bool:
|
209
|
+
"""
|
210
|
+
Check if relationship exists.
|
211
|
+
|
212
|
+
Args:
|
213
|
+
source: Source entity ID
|
214
|
+
target: Target entity ID
|
215
|
+
relationship_type: Optional type filter
|
216
|
+
|
217
|
+
Returns:
|
218
|
+
True if exists, False otherwise
|
219
|
+
|
220
|
+
Time Complexity: O(degree) where degree is relationships for source
|
221
|
+
"""
|
222
|
+
with self._lock:
|
223
|
+
if source not in self._outgoing_index:
|
224
|
+
return False
|
225
|
+
|
226
|
+
# Check outgoing relationships
|
227
|
+
if relationship_type:
|
228
|
+
rels = self._outgoing_index[source].get(relationship_type, [])
|
229
|
+
else:
|
230
|
+
rels = []
|
231
|
+
for type_rels in self._outgoing_index[source].values():
|
232
|
+
rels.extend(type_rels)
|
233
|
+
|
234
|
+
# Check if any match target
|
235
|
+
return any(r['target'] == target for r in rels)
|
236
|
+
|
237
|
+
def get_stats(self) -> Dict[str, Any]:
|
238
|
+
"""
|
239
|
+
Get index statistics.
|
240
|
+
|
241
|
+
Returns:
|
242
|
+
Dictionary with index metrics
|
243
|
+
"""
|
244
|
+
with self._lock:
|
245
|
+
return {
|
246
|
+
'total_relationships': len(self._relationships),
|
247
|
+
'indexed_sources': len(self._outgoing_index),
|
248
|
+
'indexed_targets': len(self._incoming_index),
|
249
|
+
'relationship_types': len(set(
|
250
|
+
r['type'] for r in self._relationships.values()
|
251
|
+
))
|
252
|
+
}
|
253
|
+
|
254
|
+
def clear(self):
|
255
|
+
"""Clear all indexes."""
|
256
|
+
with self._lock:
|
257
|
+
self._outgoing_index.clear()
|
258
|
+
self._incoming_index.clear()
|
259
|
+
self._relationships.clear()
|
260
|
+
|