graflo 1.3.7__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.
Potentially problematic release.
This version of graflo might be problematic. Click here for more details.
- graflo/README.md +18 -0
- graflo/__init__.py +70 -0
- graflo/architecture/__init__.py +38 -0
- graflo/architecture/actor.py +1276 -0
- graflo/architecture/actor_util.py +450 -0
- graflo/architecture/edge.py +418 -0
- graflo/architecture/onto.py +376 -0
- graflo/architecture/onto_sql.py +54 -0
- graflo/architecture/resource.py +163 -0
- graflo/architecture/schema.py +135 -0
- graflo/architecture/transform.py +292 -0
- graflo/architecture/util.py +89 -0
- graflo/architecture/vertex.py +562 -0
- graflo/caster.py +736 -0
- graflo/cli/__init__.py +14 -0
- graflo/cli/ingest.py +203 -0
- graflo/cli/manage_dbs.py +197 -0
- graflo/cli/plot_schema.py +132 -0
- graflo/cli/xml2json.py +93 -0
- graflo/data_source/__init__.py +48 -0
- graflo/data_source/api.py +339 -0
- graflo/data_source/base.py +95 -0
- graflo/data_source/factory.py +304 -0
- graflo/data_source/file.py +148 -0
- graflo/data_source/memory.py +70 -0
- graflo/data_source/registry.py +82 -0
- graflo/data_source/sql.py +183 -0
- graflo/db/__init__.py +44 -0
- graflo/db/arango/__init__.py +22 -0
- graflo/db/arango/conn.py +1025 -0
- graflo/db/arango/query.py +180 -0
- graflo/db/arango/util.py +88 -0
- graflo/db/conn.py +377 -0
- graflo/db/connection/__init__.py +6 -0
- graflo/db/connection/config_mapping.py +18 -0
- graflo/db/connection/onto.py +717 -0
- graflo/db/connection/wsgi.py +29 -0
- graflo/db/manager.py +119 -0
- graflo/db/neo4j/__init__.py +16 -0
- graflo/db/neo4j/conn.py +639 -0
- graflo/db/postgres/__init__.py +37 -0
- graflo/db/postgres/conn.py +948 -0
- graflo/db/postgres/fuzzy_matcher.py +281 -0
- graflo/db/postgres/heuristics.py +133 -0
- graflo/db/postgres/inference_utils.py +428 -0
- graflo/db/postgres/resource_mapping.py +273 -0
- graflo/db/postgres/schema_inference.py +372 -0
- graflo/db/postgres/types.py +148 -0
- graflo/db/postgres/util.py +87 -0
- graflo/db/tigergraph/__init__.py +9 -0
- graflo/db/tigergraph/conn.py +2365 -0
- graflo/db/tigergraph/onto.py +26 -0
- graflo/db/util.py +49 -0
- graflo/filter/__init__.py +21 -0
- graflo/filter/onto.py +525 -0
- graflo/logging.conf +22 -0
- graflo/onto.py +312 -0
- graflo/plot/__init__.py +17 -0
- graflo/plot/plotter.py +616 -0
- graflo/util/__init__.py +23 -0
- graflo/util/chunker.py +807 -0
- graflo/util/merge.py +150 -0
- graflo/util/misc.py +37 -0
- graflo/util/onto.py +422 -0
- graflo/util/transform.py +454 -0
- graflo-1.3.7.dist-info/METADATA +243 -0
- graflo-1.3.7.dist-info/RECORD +70 -0
- graflo-1.3.7.dist-info/WHEEL +4 -0
- graflo-1.3.7.dist-info/entry_points.txt +5 -0
- graflo-1.3.7.dist-info/licenses/LICENSE +126 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Graph database schema management and configuration.
|
|
2
|
+
|
|
3
|
+
This module provides the core schema management functionality for graph databases.
|
|
4
|
+
It defines the structure and configuration of vertices, edges, and resources
|
|
5
|
+
that make up the graph database schema.
|
|
6
|
+
|
|
7
|
+
Key Components:
|
|
8
|
+
- Schema: Main schema container with metadata and configurations
|
|
9
|
+
- SchemaMetadata: Schema versioning and naming information
|
|
10
|
+
- Resource: Resource definitions for data processing
|
|
11
|
+
- VertexConfig: Vertex collection configurations
|
|
12
|
+
- EdgeConfig: Edge collection configurations
|
|
13
|
+
|
|
14
|
+
The schema system provides:
|
|
15
|
+
- Schema versioning and metadata
|
|
16
|
+
- Resource management and validation
|
|
17
|
+
- Vertex and edge configuration
|
|
18
|
+
- Transform registration and management
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> schema = Schema(
|
|
22
|
+
... general=SchemaMetadata(name="social_network", version="1.0"),
|
|
23
|
+
... vertex_config=VertexConfig(...),
|
|
24
|
+
... edge_config=EdgeConfig(...),
|
|
25
|
+
... resources=[Resource(...)]
|
|
26
|
+
... )
|
|
27
|
+
>>> resource = schema.fetch_resource("users")
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
import dataclasses
|
|
31
|
+
import logging
|
|
32
|
+
from collections import Counter
|
|
33
|
+
|
|
34
|
+
from graflo.architecture.edge import EdgeConfig
|
|
35
|
+
from graflo.architecture.resource import Resource
|
|
36
|
+
from graflo.architecture.transform import ProtoTransform
|
|
37
|
+
from graflo.architecture.vertex import VertexConfig
|
|
38
|
+
from graflo.onto import BaseDataclass
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclasses.dataclass
|
|
44
|
+
class SchemaMetadata(BaseDataclass):
|
|
45
|
+
"""Schema metadata and versioning information.
|
|
46
|
+
|
|
47
|
+
This class holds metadata about the schema, including its name and version.
|
|
48
|
+
It's used for schema identification and versioning.
|
|
49
|
+
|
|
50
|
+
Attributes:
|
|
51
|
+
name: Name of the schema
|
|
52
|
+
version: Optional version string of the schema
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
name: str
|
|
56
|
+
version: str | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclasses.dataclass
|
|
60
|
+
class Schema(BaseDataclass):
|
|
61
|
+
"""Graph database schema configuration.
|
|
62
|
+
|
|
63
|
+
This class represents the complete schema configuration for a graph database.
|
|
64
|
+
It manages resources, vertex configurations, edge configurations, and transforms.
|
|
65
|
+
|
|
66
|
+
Attributes:
|
|
67
|
+
general: Schema metadata and versioning information
|
|
68
|
+
vertex_config: Configuration for vertex collections
|
|
69
|
+
edge_config: Configuration for edge collections
|
|
70
|
+
resources: List of resource definitions
|
|
71
|
+
transforms: Dictionary of available transforms
|
|
72
|
+
_resources: Internal mapping of resource names to resources
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
general: SchemaMetadata
|
|
76
|
+
vertex_config: VertexConfig
|
|
77
|
+
edge_config: EdgeConfig
|
|
78
|
+
resources: list[Resource]
|
|
79
|
+
transforms: dict[str, ProtoTransform] = dataclasses.field(default_factory=dict)
|
|
80
|
+
|
|
81
|
+
def __post_init__(self):
|
|
82
|
+
"""Initialize the schema after dataclass initialization.
|
|
83
|
+
|
|
84
|
+
Sets up transforms, initializes edge configuration, and validates
|
|
85
|
+
resource names for uniqueness.
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
ValueError: If duplicate resource names are found
|
|
89
|
+
"""
|
|
90
|
+
for name, t in self.transforms.items():
|
|
91
|
+
t.name = name
|
|
92
|
+
|
|
93
|
+
self.edge_config.finish_init(self.vertex_config)
|
|
94
|
+
|
|
95
|
+
for r in self.resources:
|
|
96
|
+
r.finish_init(
|
|
97
|
+
vertex_config=self.vertex_config,
|
|
98
|
+
edge_config=self.edge_config,
|
|
99
|
+
transforms=self.transforms,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
names = [r.name for r in self.resources]
|
|
103
|
+
c = Counter(names)
|
|
104
|
+
for k, v in c.items():
|
|
105
|
+
if v > 1:
|
|
106
|
+
raise ValueError(f"resource name {k} used {v} times")
|
|
107
|
+
self._resources: dict[str, Resource] = {}
|
|
108
|
+
for r in self.resources:
|
|
109
|
+
self._resources[r.name] = r
|
|
110
|
+
|
|
111
|
+
def fetch_resource(self, name: str | None = None) -> Resource:
|
|
112
|
+
"""Fetch a resource by name or get the first available resource.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
name: Optional name of the resource to fetch
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Resource: The requested resource
|
|
119
|
+
|
|
120
|
+
Raises:
|
|
121
|
+
ValueError: If the requested resource is not found or if no resources exist
|
|
122
|
+
"""
|
|
123
|
+
_current_resource = None
|
|
124
|
+
|
|
125
|
+
if name is not None:
|
|
126
|
+
if name in self._resources:
|
|
127
|
+
_current_resource = self._resources[name]
|
|
128
|
+
else:
|
|
129
|
+
raise ValueError(f"Resource {name} not found")
|
|
130
|
+
else:
|
|
131
|
+
if self._resources:
|
|
132
|
+
_current_resource = self.resources[0]
|
|
133
|
+
else:
|
|
134
|
+
raise ValueError("Empty resource container 😕")
|
|
135
|
+
return _current_resource
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"""Data transformation and mapping system for graph databases.
|
|
2
|
+
|
|
3
|
+
This module provides a flexible system for transforming and mapping data in graph
|
|
4
|
+
databases. It supports both functional transformations and declarative mappings,
|
|
5
|
+
with support for field switching and parameter configuration.
|
|
6
|
+
|
|
7
|
+
Key Components:
|
|
8
|
+
- ProtoTransform: Base class for transform definitions
|
|
9
|
+
- Transform: Concrete transform implementation
|
|
10
|
+
- TransformException: Custom exception for transform errors
|
|
11
|
+
|
|
12
|
+
The transform system supports:
|
|
13
|
+
- Functional transformations through imported modules
|
|
14
|
+
- Field mapping and switching
|
|
15
|
+
- Parameter configuration
|
|
16
|
+
- Input/output field specification
|
|
17
|
+
- Transform composition and inheritance
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
>>> transform = Transform(
|
|
21
|
+
... module="my_module",
|
|
22
|
+
... foo="process_data",
|
|
23
|
+
... input=("field1", "field2"),
|
|
24
|
+
... output=("result1", "result2")
|
|
25
|
+
... )
|
|
26
|
+
>>> result = transform({"field1": 1, "field2": 2})
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import dataclasses
|
|
32
|
+
import importlib
|
|
33
|
+
import logging
|
|
34
|
+
from copy import deepcopy
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
from graflo.onto import BaseDataclass
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TransformException(BaseException):
|
|
43
|
+
"""Base exception for transform-related errors."""
|
|
44
|
+
|
|
45
|
+
pass
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclasses.dataclass
|
|
49
|
+
class ProtoTransform(BaseDataclass):
|
|
50
|
+
"""Base class for transform definitions.
|
|
51
|
+
|
|
52
|
+
This class provides the foundation for data transformations, supporting both
|
|
53
|
+
functional transformations and declarative mappings.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
name: Optional name of the transform
|
|
57
|
+
module: Optional module containing the transform function
|
|
58
|
+
params: Dictionary of transform parameters
|
|
59
|
+
foo: Optional name of the transform function
|
|
60
|
+
input: Tuple of input field names
|
|
61
|
+
output: Tuple of output field names
|
|
62
|
+
_foo: Internal reference to the transform function
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
name: str | None = None
|
|
66
|
+
module: str | None = None
|
|
67
|
+
params: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
68
|
+
foo: str | None = None
|
|
69
|
+
input: str | list[str] | tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
|
70
|
+
output: str | list[str] | tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
|
71
|
+
|
|
72
|
+
def __post_init__(self):
|
|
73
|
+
"""Initialize the transform after dataclass initialization.
|
|
74
|
+
|
|
75
|
+
Sets up the transform function and input/output field specifications.
|
|
76
|
+
"""
|
|
77
|
+
self._foo = None
|
|
78
|
+
self._init_foo()
|
|
79
|
+
|
|
80
|
+
self.input = self._tuple_it(self.input)
|
|
81
|
+
|
|
82
|
+
if not self.output:
|
|
83
|
+
self.output = self.input
|
|
84
|
+
self.output = self._tuple_it(self.output)
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def _tuple_it(x):
|
|
88
|
+
"""Convert input to tuple format.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
x: Input to convert (string, list, or tuple)
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
tuple: Converted tuple
|
|
95
|
+
"""
|
|
96
|
+
if isinstance(x, str):
|
|
97
|
+
x = [x]
|
|
98
|
+
if isinstance(x, list):
|
|
99
|
+
x = tuple(x)
|
|
100
|
+
return x
|
|
101
|
+
|
|
102
|
+
def _init_foo(self):
|
|
103
|
+
"""Initialize the transform function from module.
|
|
104
|
+
|
|
105
|
+
Imports the specified module and gets the transform function.
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
TypeError: If module import fails
|
|
109
|
+
ValueError: If function lookup fails
|
|
110
|
+
"""
|
|
111
|
+
if self.module is not None and self.foo is not None:
|
|
112
|
+
try:
|
|
113
|
+
_module = importlib.import_module(self.module)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
raise TypeError(f"Provided module {self.module} is not valid: {e}")
|
|
116
|
+
try:
|
|
117
|
+
self._foo = getattr(_module, self.foo)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"Could not instantiate transform function. Exception: {e}"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def __lt__(self, other):
|
|
124
|
+
"""Compare transforms for ordering.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
other: Other transform to compare with
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
bool: True if this transform should be ordered before other
|
|
131
|
+
"""
|
|
132
|
+
if self._foo is None and other._foo is not None:
|
|
133
|
+
return True
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclasses.dataclass(kw_only=True)
|
|
138
|
+
class Transform(ProtoTransform):
|
|
139
|
+
"""Concrete transform implementation.
|
|
140
|
+
|
|
141
|
+
This class extends ProtoTransform with additional functionality for
|
|
142
|
+
field mapping, switching, and transform composition.
|
|
143
|
+
|
|
144
|
+
Attributes:
|
|
145
|
+
fields: Tuple of fields to transform
|
|
146
|
+
map: Dictionary mapping input fields to output fields
|
|
147
|
+
switch: Dictionary for field switching logic
|
|
148
|
+
functional_transform: Whether this is a functional transform
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
fields: str | list[str] | tuple[str, ...] = dataclasses.field(default_factory=tuple)
|
|
152
|
+
map: dict[str, str] = dataclasses.field(default_factory=dict)
|
|
153
|
+
switch: dict[str, Any] = dataclasses.field(default_factory=dict)
|
|
154
|
+
|
|
155
|
+
def __post_init__(self):
|
|
156
|
+
"""Initialize the transform after dataclass initialization.
|
|
157
|
+
|
|
158
|
+
Sets up field specifications and validates transform configuration.
|
|
159
|
+
|
|
160
|
+
Raises:
|
|
161
|
+
ValueError: If transform configuration is invalid
|
|
162
|
+
"""
|
|
163
|
+
super().__post_init__()
|
|
164
|
+
self.functional_transform = False
|
|
165
|
+
if self._foo is not None:
|
|
166
|
+
self.functional_transform = True
|
|
167
|
+
|
|
168
|
+
self.input = self._tuple_it(self.input)
|
|
169
|
+
|
|
170
|
+
self.fields = self._tuple_it(self.fields)
|
|
171
|
+
|
|
172
|
+
self.input = self.fields if self.fields and not self.input else self.input
|
|
173
|
+
if not self.output:
|
|
174
|
+
self.output = self.input
|
|
175
|
+
self.output = self._tuple_it(self.output)
|
|
176
|
+
|
|
177
|
+
if not self.input and not self.output:
|
|
178
|
+
if self.map:
|
|
179
|
+
items = list(self.map.items())
|
|
180
|
+
self.input = tuple(x for x, _ in items)
|
|
181
|
+
self.output = tuple(x for _, x in items)
|
|
182
|
+
elif self.switch:
|
|
183
|
+
self.input = tuple([k for k in self.switch])
|
|
184
|
+
self.output = tuple(self.switch[self.input[0]])
|
|
185
|
+
elif not self.name:
|
|
186
|
+
raise ValueError(
|
|
187
|
+
"Either input and output, fields, map or name should be"
|
|
188
|
+
" provided in Transform constructor."
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
def __call__(self, *nargs, **kwargs):
|
|
192
|
+
"""Execute the transform.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
*nargs: Positional arguments for the transform
|
|
196
|
+
**kwargs: Keyword arguments for the transform
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
dict: Transformed data
|
|
200
|
+
"""
|
|
201
|
+
is_mapping = self._foo is None
|
|
202
|
+
|
|
203
|
+
if is_mapping:
|
|
204
|
+
input_doc = nargs[0]
|
|
205
|
+
if isinstance(input_doc, dict):
|
|
206
|
+
output_values = [input_doc[k] for k in self.input]
|
|
207
|
+
else:
|
|
208
|
+
output_values = nargs
|
|
209
|
+
else:
|
|
210
|
+
if nargs and isinstance(input_doc := nargs[0], dict):
|
|
211
|
+
new_args = [input_doc[k] for k in self.input]
|
|
212
|
+
output_values = self._foo(*new_args, **kwargs, **self.params)
|
|
213
|
+
else:
|
|
214
|
+
output_values = self._foo(*nargs, **kwargs, **self.params)
|
|
215
|
+
|
|
216
|
+
if self.output:
|
|
217
|
+
r = self._dress_as_dict(output_values)
|
|
218
|
+
else:
|
|
219
|
+
r = output_values
|
|
220
|
+
return r
|
|
221
|
+
|
|
222
|
+
def _dress_as_dict(self, transform_result):
|
|
223
|
+
"""Convert transform result to dictionary format.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
transform_result: Result of the transform
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
dict: Dictionary representation of the result
|
|
230
|
+
"""
|
|
231
|
+
if isinstance(transform_result, (list, tuple)) and not self.switch:
|
|
232
|
+
upd = {k: v for k, v in zip(self.output, transform_result)}
|
|
233
|
+
else:
|
|
234
|
+
# TODO : temporary solution works only there is one switch clause
|
|
235
|
+
upd = {self.output[-1]: transform_result}
|
|
236
|
+
for k0, (q, qq) in self.switch.items():
|
|
237
|
+
upd.update({q: k0})
|
|
238
|
+
return upd
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def is_dummy(self):
|
|
242
|
+
"""Check if this is a dummy transform.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
bool: True if this is a dummy transform
|
|
246
|
+
"""
|
|
247
|
+
return (self.name is not None) and (not self.map and self._foo is None)
|
|
248
|
+
|
|
249
|
+
def update(self, t: Transform):
|
|
250
|
+
"""Update this transform with another transform's configuration.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
t: Transform to update from
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Transform: Updated transform
|
|
257
|
+
"""
|
|
258
|
+
t_copy = deepcopy(t)
|
|
259
|
+
if self.input:
|
|
260
|
+
t_copy.input = self.input
|
|
261
|
+
if self.output:
|
|
262
|
+
t_copy.output = self.output
|
|
263
|
+
if self.params:
|
|
264
|
+
t_copy.params.update(self.params)
|
|
265
|
+
t_copy.__post_init__()
|
|
266
|
+
return t_copy
|
|
267
|
+
|
|
268
|
+
def get_barebone(
|
|
269
|
+
self, other: Transform | None
|
|
270
|
+
) -> tuple[Transform | None, Transform | None]:
|
|
271
|
+
"""Get the barebone transform configuration.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
other: Optional transform to use as base
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
tuple[Transform | None, Transform | None]: Updated self transform
|
|
278
|
+
and transform to store in library
|
|
279
|
+
"""
|
|
280
|
+
self_param = self.to_dict(skip_defaults=True)
|
|
281
|
+
if self.foo is not None:
|
|
282
|
+
# self will be the lib transform
|
|
283
|
+
return None, self
|
|
284
|
+
elif other is not None and other.foo is not None:
|
|
285
|
+
# init self from other
|
|
286
|
+
self_param.pop("foo", None)
|
|
287
|
+
self_param.pop("module", None)
|
|
288
|
+
other_param = other.to_dict(skip_defaults=True)
|
|
289
|
+
other_param.update(self_param)
|
|
290
|
+
return Transform(**other_param), None
|
|
291
|
+
else:
|
|
292
|
+
return None, None
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Utility functions for graph architecture operations.
|
|
2
|
+
|
|
3
|
+
This module provides utility functions for working with graph data structures
|
|
4
|
+
and transformations. It includes functions for dictionary projection and
|
|
5
|
+
graph entity name formatting.
|
|
6
|
+
|
|
7
|
+
Key Functions:
|
|
8
|
+
- project_dict: Project dictionary fields based on inclusion/exclusion
|
|
9
|
+
- cast_graph_name_to_triple: Convert graph names to standardized triple format
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> data = {"a": 1, "b": 2, "c": 3}
|
|
13
|
+
>>> project_dict(data, ["a", "b"], how="include")
|
|
14
|
+
{'a': 1, 'b': 2}
|
|
15
|
+
>>> cast_graph_name_to_triple("user_post_graph")
|
|
16
|
+
('user', 'post', None)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from graflo.architecture.onto import GraphEntity
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def project_dict(item, keys, how="include"):
|
|
23
|
+
"""Project dictionary fields based on inclusion or exclusion.
|
|
24
|
+
|
|
25
|
+
This function filters a dictionary based on a list of keys, either including
|
|
26
|
+
or excluding the specified keys.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
item: Dictionary to project
|
|
30
|
+
keys: List of keys to include or exclude
|
|
31
|
+
how: Projection mode - "include" or "exclude" (default: "include")
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
dict: Projected dictionary containing only the specified fields
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
>>> data = {"a": 1, "b": 2, "c": 3}
|
|
38
|
+
>>> project_dict(data, ["a", "b"], how="include")
|
|
39
|
+
{'a': 1, 'b': 2}
|
|
40
|
+
>>> project_dict(data, ["a"], how="exclude")
|
|
41
|
+
{'b': 2, 'c': 3}
|
|
42
|
+
"""
|
|
43
|
+
if how == "include":
|
|
44
|
+
return {k: v for k, v in item.items() if k in keys}
|
|
45
|
+
elif how == "exclude":
|
|
46
|
+
return {k: v for k, v in item.items() if k not in keys}
|
|
47
|
+
else:
|
|
48
|
+
return {}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def cast_graph_name_to_triple(s: GraphEntity) -> str | tuple:
|
|
52
|
+
"""Convert a graph name string to a triple format.
|
|
53
|
+
|
|
54
|
+
This function parses graph entity names into a standardized triple format
|
|
55
|
+
(source, target, type). It handles various naming patterns and special
|
|
56
|
+
suffixes like "graph" or "edges".
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
s: Graph entity name or ID
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
str | tuple: Either a string for simple names or a tuple
|
|
63
|
+
representing (source, target, type) for complex names
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
ValueError: If the graph name cannot be cast to a valid format
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
>>> cast_graph_name_to_triple("user_post_graph")
|
|
70
|
+
('user', 'post', None)
|
|
71
|
+
>>> cast_graph_name_to_triple("simple_vertex")
|
|
72
|
+
('simple', None)
|
|
73
|
+
"""
|
|
74
|
+
if isinstance(s, str):
|
|
75
|
+
s2 = s.split("_")
|
|
76
|
+
if len(s2) < 2:
|
|
77
|
+
return s2[0]
|
|
78
|
+
elif len(s2) == 2:
|
|
79
|
+
return *s2[:-1], None
|
|
80
|
+
elif len(s2) == 3:
|
|
81
|
+
if s2[-1] in ["graph", "edges"]:
|
|
82
|
+
return *s2[:-1], None
|
|
83
|
+
else:
|
|
84
|
+
return tuple(s2)
|
|
85
|
+
elif len(s2) == 4 and s2[-1] in ["graph", "edges"]:
|
|
86
|
+
return tuple(s2[:-1])
|
|
87
|
+
raise ValueError(f"Invalid graph_name {s} : can not be cast to GraphEntity")
|
|
88
|
+
else:
|
|
89
|
+
return s
|