topologicpy 0.7.55__py3-none-any.whl → 0.7.57__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.
- topologicpy/Dictionary.py +7 -3
- topologicpy/Graph.py +89 -89
- topologicpy/Neo4j.py +285 -341
- topologicpy/Plotly.py +13 -10
- topologicpy/version.py +1 -1
- {topologicpy-0.7.55.dist-info → topologicpy-0.7.57.dist-info}/METADATA +1 -1
- {topologicpy-0.7.55.dist-info → topologicpy-0.7.57.dist-info}/RECORD +10 -10
- {topologicpy-0.7.55.dist-info → topologicpy-0.7.57.dist-info}/WHEEL +1 -1
- {topologicpy-0.7.55.dist-info → topologicpy-0.7.57.dist-info}/LICENSE +0 -0
- {topologicpy-0.7.55.dist-info → topologicpy-0.7.57.dist-info}/top_level.txt +0 -0
topologicpy/Neo4j.py
CHANGED
@@ -20,340 +20,134 @@ import os
|
|
20
20
|
import warnings
|
21
21
|
|
22
22
|
try:
|
23
|
-
import
|
24
|
-
from
|
25
|
-
from py2neo.data import spatial as sp
|
23
|
+
import neo4j
|
24
|
+
from neo4j import GraphDatabase
|
26
25
|
except:
|
27
|
-
print("Neo4j - Installing required
|
26
|
+
print("Neo4j - Installing required neo4j library.")
|
28
27
|
try:
|
29
|
-
os.system("pip install
|
28
|
+
os.system("pip install neo4j")
|
30
29
|
except:
|
31
|
-
os.system("pip install
|
30
|
+
os.system("pip install neo4j --user")
|
32
31
|
try:
|
33
|
-
import
|
34
|
-
from
|
35
|
-
from py2neo.data import spatial as sp
|
32
|
+
import neo4j
|
33
|
+
from neo4j import GraphDatabase
|
36
34
|
except:
|
37
|
-
warnings.warn("Neo4j - Error: Could not import
|
38
|
-
|
39
|
-
class Neo4j:
|
40
|
-
|
41
|
-
@staticmethod
|
42
|
-
def NodeToVertex(node):
|
43
|
-
"""
|
44
|
-
Converts the input neo4j node to a topologic vertex.
|
45
|
-
|
46
|
-
Parameters
|
47
|
-
----------
|
48
|
-
node : Neo4j.Node
|
49
|
-
The input neo4j node.
|
50
|
-
|
51
|
-
Returns
|
52
|
-
-------
|
53
|
-
topologic_core.Vertex
|
54
|
-
The output topologic vertex.
|
55
|
-
|
56
|
-
"""
|
57
|
-
from topologicpy.Vertex import Vertex
|
58
|
-
from topologicpy.Topology import Topology
|
59
|
-
from topologicpy.Dictionary import Dictionary
|
60
|
-
|
61
|
-
if ('x' in node.keys()) and ('y' in node.keys()) and ('z' in node.keys()) or ('X' in node.keys()) and ('Y' in node.keys()) and ('Z' in node.keys()):
|
62
|
-
x = node['x']
|
63
|
-
y = node['y']
|
64
|
-
z = node['z']
|
65
|
-
vertex = Vertex.ByCoordinates(x, y, z)
|
66
|
-
else:
|
67
|
-
x = random.uniform(0, 1000)
|
68
|
-
y = random.uniform(0, 1000)
|
69
|
-
z = random.uniform(0, 1000)
|
70
|
-
vertex = Vertex.ByCoordinates(x, y, z)
|
71
|
-
keys = list(node.keys())
|
72
|
-
values = list(node.values())
|
73
|
-
d = Dictionary.ByKeysValues(keys, values)
|
74
|
-
vertex = Topology.SetDictionary(vertex, d)
|
75
|
-
return vertex
|
76
|
-
|
77
|
-
|
78
|
-
@staticmethod
|
79
|
-
def NodesByCypher(neo4jGraph, cypher):
|
80
|
-
dataList = neo4jGraph.run(cypher).data()
|
81
|
-
nodes = []
|
82
|
-
for data in dataList:
|
83
|
-
path = data['p']
|
84
|
-
nodes += list(path.nodes)
|
85
|
-
return nodes
|
35
|
+
warnings.warn("Neo4j - Error: Could not import neo4j")
|
86
36
|
|
37
|
+
class Neo4j:
|
87
38
|
@staticmethod
|
88
|
-
def
|
89
|
-
data = subGraph.data()
|
90
|
-
nodes = []
|
91
|
-
for data in subGraph:
|
92
|
-
path = data['p']
|
93
|
-
nodes += list(path.nodes)
|
94
|
-
return nodes
|
95
|
-
|
96
|
-
@staticmethod
|
97
|
-
def SubGraphByCypher(neo4jGraph, cypher):
|
98
|
-
return neo4jGraph.run(cypher).to_subgraph()
|
99
|
-
|
100
|
-
@staticmethod
|
101
|
-
def SubGraphExportToGraph(subGraph, tolerance=0.0001):
|
39
|
+
def ExportToGraph(neo4jGraph, cypher=None, xMin=-0.5, yMin=-0.5, zMin=-0.5, xMax=0.5, yMax=0.5, zMax=0.5, tolerance=0.0001, silent=False):
|
102
40
|
"""
|
103
41
|
Exports the input neo4j graph to a topologic graph.
|
104
42
|
|
105
43
|
Parameters
|
106
44
|
----------
|
107
|
-
|
108
|
-
The input neo4j
|
109
|
-
|
45
|
+
neo4jGraph : neo4j._sync.driver.BoltDriver
|
46
|
+
The input neo4j bolt driver.
|
47
|
+
cypher : str, optional
|
48
|
+
If set to a non-empty string, a Cypher query will be run on the neo4j graph database to return a sub-graph. Default is None.
|
49
|
+
xMin : float, optional
|
50
|
+
The desired minimum value to assign for a vertex's X coordinate. The default is -0.5.
|
51
|
+
yMin : float, optional
|
52
|
+
The desired minimum value to assign for a vertex's Y coordinate. The default is -0.5.
|
53
|
+
zMin : float, optional
|
54
|
+
The desired minimum value to assign for a vertex's Z coordinate. The default is -0.5.
|
55
|
+
xMax : float, optional
|
56
|
+
The desired maximum value to assign for a vertex's X coordinate. The default is 0.5.
|
57
|
+
yMax : float, optional
|
58
|
+
The desired maximum value to assign for a vertex's Y coordinate. The default is 0.5.
|
59
|
+
zMax : float, optional
|
60
|
+
The desired maximum value to assign for a vertex's Z coordinate. The default is 0.5.
|
61
|
+
silent : bool, optional
|
62
|
+
If set to True, no warnings or error messages are displayed. The default is False.
|
63
|
+
tolerance : float, optional
|
110
64
|
The desired tolerance. The default is 0.0001.
|
111
65
|
|
112
66
|
Returns
|
113
67
|
-------
|
114
68
|
topologic_core.Graph
|
115
|
-
The output
|
116
|
-
|
69
|
+
The output Topologic graph.
|
117
70
|
"""
|
71
|
+
import random
|
118
72
|
from topologicpy.Vertex import Vertex
|
119
73
|
from topologicpy.Edge import Edge
|
120
74
|
from topologicpy.Topology import Topology
|
121
|
-
from topologicpy.Dictionary import Dictionary
|
122
75
|
from topologicpy.Graph import Graph
|
123
|
-
|
124
|
-
def randomVertex(vertices, minDistance):
|
125
|
-
flag = True
|
126
|
-
while flag:
|
127
|
-
x = random.uniform(0, 1000)
|
128
|
-
y = random.uniform(0, 1000)
|
129
|
-
z = random.uniform(0, 1000)
|
130
|
-
v = Vertex.ByCoordinates(x, y, z)
|
131
|
-
test = False
|
132
|
-
if len(vertices) < 1:
|
133
|
-
return v
|
134
|
-
for vertex in vertices:
|
135
|
-
d = Vertex.Distance(v, vertex)
|
136
|
-
if d < minDistance:
|
137
|
-
test = True
|
138
|
-
break
|
139
|
-
if test == False:
|
140
|
-
return v
|
141
|
-
else:
|
142
|
-
continue
|
143
|
-
|
144
|
-
nodes = subGraph.nodes
|
145
|
-
relationships = list(subGraph.relationships)
|
146
|
-
vertices = []
|
147
|
-
edges = []
|
148
|
-
for node in nodes:
|
149
|
-
#Check if they have X, Y, Z coordinates
|
150
|
-
if ('x' in node.keys()) and ('y' in node.keys()) and ('z' in node.keys()) or ('X' in node.keys()) and ('Y' in node.keys()) and ('Z' in node.keys()):
|
151
|
-
x = node['x']
|
152
|
-
y = node['y']
|
153
|
-
z = node['z']
|
154
|
-
vertex = Vertex.ByCoordinates(x, y, z)
|
155
|
-
else:
|
156
|
-
vertex = randomVertex(vertices, 1)
|
157
|
-
keys = list(node.keys())
|
158
|
-
keys.append("identity")
|
159
|
-
values = [node.identity]
|
160
|
-
for key in keys:
|
161
|
-
values.append(node[key])
|
162
|
-
d = Dictionary.ByKeysValues(keys, values)
|
163
|
-
vertex = Topology.SetDictionary(vertex, d)
|
164
|
-
vertices.append(vertex)
|
165
|
-
for relationship in relationships:
|
166
|
-
keys = list(relationship.keys())
|
167
|
-
keys.append("identity")
|
168
|
-
values = [relationship.identity]
|
169
|
-
for key in keys:
|
170
|
-
values.append(node[key])
|
171
|
-
sv = vertices[nodes.index(relationship.start_node)]
|
172
|
-
ev = vertices[nodes.index(relationship.end_node)]
|
173
|
-
edge = Edge.ByVertices([sv, ev], tolerance=tolerance)
|
174
|
-
if relationship.start_node['name']:
|
175
|
-
sv_name = relationship.start_node['name']
|
176
|
-
else:
|
177
|
-
sv_name = 'None'
|
178
|
-
if relationship.end_node['name']:
|
179
|
-
ev_name = relationship.end_node['name']
|
180
|
-
else:
|
181
|
-
ev_name = 'None'
|
182
|
-
d = Dictionary.ByKeysValues(["relationship_type", "from", "to"], [relationship.__class__.__name__, sv_name, ev_name])
|
183
|
-
if d:
|
184
|
-
_ = Topology.SetDictionary(edge, d)
|
185
|
-
edges.append(edge)
|
186
|
-
return Graph.ByVerticesEdges(vertices,edges)
|
187
|
-
|
188
|
-
@staticmethod
|
189
|
-
def ExportToGraph(neo4jGraph, tolerance=0.0001):
|
190
|
-
"""
|
191
|
-
Exports the input neo4j graph to a topologic graph.
|
192
|
-
|
193
|
-
Parameters
|
194
|
-
----------
|
195
|
-
neo4jGraph : Neo4j.Graph
|
196
|
-
The input neo4j graph.
|
197
|
-
tolerance : float , optional
|
198
|
-
The desired tolerance. The default is 0.0001.
|
199
|
-
|
200
|
-
Returns
|
201
|
-
-------
|
202
|
-
topologic_core.Graph
|
203
|
-
The output topologic graph.
|
204
|
-
|
205
|
-
"""
|
206
|
-
from topologicpy.Vertex import Vertex
|
207
|
-
from topologicpy.Edge import Edge
|
208
|
-
from topologicpy.Topology import Topology
|
209
76
|
from topologicpy.Dictionary import Dictionary
|
210
|
-
from topologicpy.Graph import Graph
|
211
77
|
|
212
|
-
def randomVertex(vertices, minDistance):
|
213
|
-
flag = True
|
214
|
-
while flag:
|
215
|
-
x = random.uniform(0, 1000)
|
216
|
-
y = random.uniform(0, 1000)
|
217
|
-
z = random.uniform(0, 1000)
|
218
|
-
v = Vertex.ByCoordinates(x, y, z)
|
219
|
-
test = False
|
220
|
-
if len(vertices) < 1:
|
221
|
-
return v
|
222
|
-
for vertex in vertices:
|
223
|
-
d = Vertex.Distance(v, vertex)
|
224
|
-
if d < minDistance:
|
225
|
-
test = True
|
226
|
-
break
|
227
|
-
if test == False:
|
228
|
-
return v
|
229
|
-
else:
|
230
|
-
continue
|
231
|
-
|
232
|
-
node_labels = neo4jGraph.schema.node_labels
|
233
|
-
relationship_types = neo4jGraph.schema.relationship_types
|
234
|
-
node_matcher = NodeMatcher(neo4jGraph)
|
235
|
-
relationship_matcher = RelationshipMatcher(neo4jGraph)
|
236
78
|
vertices = []
|
237
79
|
edges = []
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
#
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
80
|
+
all_vertices = []
|
81
|
+
|
82
|
+
with neo4jGraph.session() as session:
|
83
|
+
nodes_result = session.run("MATCH (n) RETURN n")
|
84
|
+
# Process nodes
|
85
|
+
nodes = [record.get('n') for record in nodes_result]
|
86
|
+
for node in nodes:
|
87
|
+
if node:
|
88
|
+
properties = dict(node.items())
|
89
|
+
x = properties.get('x', random.uniform(xMin, xMax))
|
90
|
+
y = properties.get('y', random.uniform(yMin, yMax))
|
91
|
+
z = properties.get('z', random.uniform(zMin, zMax))
|
92
|
+
vertex = Vertex.ByCoordinates(x, y, z) # Create Topologic vertex
|
93
|
+
d = Dictionary.ByPythonDictionary(properties)
|
94
|
+
vertex = Topology.SetDictionary(vertex, d)
|
95
|
+
all_vertices.append(vertex)
|
96
|
+
|
97
|
+
if cypher:
|
98
|
+
# Run the provided Cypher query
|
99
|
+
nodes_result = session.run(cypher)
|
248
100
|
else:
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
101
|
+
# Fetch all nodes and relationships
|
102
|
+
nodes_result = session.run("MATCH (n) RETURN n")
|
103
|
+
relationships_result = session.run("MATCH (a)-[r]->(b) RETURN a, r, b")
|
104
|
+
|
105
|
+
# Process nodes
|
106
|
+
nodes = [record.get('n') for record in nodes_result]
|
107
|
+
for node in nodes:
|
108
|
+
if node:
|
109
|
+
properties = dict(node.items())
|
110
|
+
x = properties.get('x', random.uniform(xMin, xMax))
|
111
|
+
y = properties.get('y', random.uniform(yMin, yMax))
|
112
|
+
z = properties.get('z', random.uniform(zMin, zMax))
|
113
|
+
vertex = Vertex.ByCoordinates(x, y, z) # Create Topologic vertex
|
114
|
+
d = Dictionary.ByPythonDictionary(properties)
|
115
|
+
vertex = Topology.SetDictionary(vertex, d)
|
116
|
+
vertices.append(vertex)
|
117
|
+
|
118
|
+
# If a Cypher query was provided, process edges
|
119
|
+
if cypher:
|
120
|
+
relationships_result = session.run(cypher)
|
121
|
+
|
122
|
+
# Process relationships
|
123
|
+
for record in relationships_result:
|
124
|
+
start_node = record.get('a')
|
125
|
+
end_node = record.get('b')
|
126
|
+
relationship = record.get('r')
|
127
|
+
|
128
|
+
if start_node and end_node:
|
129
|
+
# Find corresponding vertices
|
130
|
+
#start_vertex = next((v for v in vertices if v.id == start_node.id), None)
|
131
|
+
#end_vertex = next((v for v in vertices if v.id == end_node.id), None)
|
132
|
+
start_filter = Topology.Filter(all_vertices, searchType="equal to", key="id", value=start_node['id'])['filtered']
|
133
|
+
if len(start_filter) > 0:
|
134
|
+
start_vertex = start_filter[0]
|
266
135
|
else:
|
267
|
-
|
268
|
-
|
269
|
-
|
136
|
+
start_vertex = NotImplemented
|
137
|
+
end_filter = Topology.Filter(all_vertices, searchType="equal to", key="id", value=end_node['id'])['filtered']
|
138
|
+
if len(end_filter) > 0:
|
139
|
+
end_vertex = end_filter[0]
|
270
140
|
else:
|
271
|
-
|
272
|
-
d = Dictionary.ByKeysValues(["relationship_type", "from", "to"], [relationship_type, sv_name, ev_name])
|
273
|
-
if d:
|
274
|
-
_ = Topology.SetDictionary(edge, d)
|
275
|
-
edges.append(edge)
|
276
|
-
return Graph.ByVerticesEdges(vertices,edges)
|
277
|
-
|
278
|
-
@staticmethod
|
279
|
-
def AddGraph(neo4jGraph, graph, labelKey=None, relationshipKey=None, mantissa: int = 6, tolerance: float = 0.0001):
|
280
|
-
"""
|
281
|
-
Adds the input topologic graph to the input neo4j graph
|
282
|
-
|
283
|
-
Parameters
|
284
|
-
----------
|
285
|
-
neo4jGraph : Neo4j.Graph
|
286
|
-
The input neo4j graph.
|
287
|
-
graph : topologic_core.Graph
|
288
|
-
The input topologic graph.
|
289
|
-
labelKey : str , optional
|
290
|
-
The label key in the dictionary under which to look for the label value.
|
291
|
-
relationshipKey: str , optional
|
292
|
-
The relationship key in the dictionary under which to look for the relationship value.
|
293
|
-
mantissa : int, optional
|
294
|
-
The desired length of the mantissa. The default is 6.
|
295
|
-
tolerance : float , optional
|
296
|
-
The desired tolerance. The default is 0.0001.
|
141
|
+
end_vertex = None
|
297
142
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
143
|
+
if not start_vertex == None and not end_vertex == None:
|
144
|
+
edge = Edge.ByVertices(start_vertex, end_vertex)
|
145
|
+
relationship_props = dict(relationship.items())
|
146
|
+
d = Dictionary.ByPythonDictionary(relationship_props)
|
147
|
+
edge = Topology.SetDictionary(edge, d)
|
148
|
+
edges.append(edge)
|
149
|
+
return Graph.ByVerticesEdges(vertices, edges)
|
302
150
|
|
303
|
-
"""
|
304
|
-
from topologicpy.Vertex import Vertex
|
305
|
-
from topologicpy.Topology import Topology
|
306
|
-
from topologicpy.Graph import Graph
|
307
|
-
from topologicpy.Dictionary import Dictionary
|
308
|
-
|
309
|
-
gmt = time.gmtime()
|
310
|
-
timestamp = str(gmt.tm_zone)+"_"+str(gmt.tm_year)+"_"+str(gmt.tm_mon)+"_"+str(gmt.tm_wday)+"_"+str(gmt.tm_hour)+"_"+str(gmt.tm_min)+"_"+str(gmt.tm_sec)
|
311
|
-
vertices = Graph.Vertices(graph)
|
312
|
-
edges = Graph.Edges(graph)
|
313
|
-
tx = neo4jGraph.begin()
|
314
|
-
nodes = []
|
315
|
-
for i in range(len(vertices)):
|
316
|
-
vDict = Topology.Dictionary(vertices[i])
|
317
|
-
keys = Dictionary.Keyus(vDict)
|
318
|
-
values = Dictionary.Values(vDict)
|
319
|
-
keys.append("x")
|
320
|
-
keys.append("y")
|
321
|
-
keys.append("z")
|
322
|
-
keys.append("timestamp")
|
323
|
-
keys.append("location")
|
324
|
-
values.append(Vertex.X(vertices[i], mantissa=mantissa))
|
325
|
-
values.append(Vertex.Y(vertices[i], mantissa=mantissa))
|
326
|
-
values.append(Vertex.Z(vertices[i], mantissa=mantissa))
|
327
|
-
values.append(timestamp)
|
328
|
-
values.append(sp.CartesianPoint([Vertex.X(vertices[i], mantissa=mantissa), Vertex.Y(vertices[i], mantissa=mantissa), Vertex.Z(vertices[i], mantissa=mantissa)]))
|
329
|
-
zip_iterator = zip(keys, values)
|
330
|
-
pydict = dict(zip_iterator)
|
331
|
-
if labelKey == 'None':
|
332
|
-
nodeName = "TopologicGraphVertex"
|
333
|
-
else:
|
334
|
-
nodeName = str(values[keys.index(labelKey)])
|
335
|
-
n = py2neo.Node(nodeName, **pydict)
|
336
|
-
neo4jGraph.cypher.execute("CREATE INDEX FOR (n:%s) on (n.name)" %
|
337
|
-
n.nodelabel)
|
338
|
-
tx.create(n)
|
339
|
-
nodes.append(n)
|
340
|
-
for i in range(len(edges)):
|
341
|
-
e = edges[i]
|
342
|
-
sv = e.StartVertex()
|
343
|
-
ev = e.EndVertex()
|
344
|
-
sn = nodes[Vertex.Index(sv, vertices, tolerance=tolerance)]
|
345
|
-
en = nodes[Vertex.Index(ev, vertices, tolerance=tolerance)]
|
346
|
-
relationshipType = Dictionary.ValueAtKey(e, relationshipKey)
|
347
|
-
if not (relationshipType):
|
348
|
-
relationshipType = "Connected To"
|
349
|
-
snen = py2neo.Relationship(sn, relationshipType, en)
|
350
|
-
tx.create(snen)
|
351
|
-
snen = py2neo.Relationship(en, relationshipType, sn)
|
352
|
-
tx.create(snen)
|
353
|
-
neo4jGraph.commit(tx)
|
354
|
-
return neo4jGraph
|
355
|
-
|
356
|
-
|
357
151
|
@staticmethod
|
358
152
|
def ByParameters(url, username, password):
|
359
153
|
"""
|
@@ -370,67 +164,217 @@ class Neo4j:
|
|
370
164
|
|
371
165
|
Returns
|
372
166
|
-------
|
373
|
-
|
374
|
-
The returned
|
167
|
+
neo4j._sync.driver.BoltDriver
|
168
|
+
The returned neo4j bolt driver.
|
375
169
|
|
376
170
|
"""
|
377
|
-
return
|
171
|
+
return GraphDatabase.driver(url, auth=(username, password))
|
378
172
|
|
379
173
|
@staticmethod
|
380
|
-
def
|
174
|
+
def Reset(neo4jGraph):
|
381
175
|
"""
|
382
|
-
|
176
|
+
Resets the database completely.
|
383
177
|
|
384
178
|
Parameters
|
385
179
|
----------
|
386
|
-
neo4jGraph :
|
387
|
-
The input
|
180
|
+
neo4jGraph : neo4j._sync.driver.BoltDriver
|
181
|
+
The input neo4j bolt driver.
|
388
182
|
|
389
183
|
Returns
|
390
184
|
-------
|
391
|
-
|
392
|
-
The returned
|
185
|
+
neo4j._sync.driver.BoltDriver
|
186
|
+
The returned neo4j bolt driver.
|
393
187
|
|
394
188
|
"""
|
395
|
-
neo4jGraph.
|
189
|
+
with neo4jGraph.session() as session:
|
190
|
+
# Delete all nodes and relationships
|
191
|
+
session.run("MATCH (n) DETACH DELETE n")
|
192
|
+
|
193
|
+
# Drop all indexes
|
194
|
+
indexes = session.run("SHOW INDEXES").data()
|
195
|
+
for index in indexes:
|
196
|
+
index_name = index['name']
|
197
|
+
session.run(f"DROP INDEX {index_name}")
|
198
|
+
|
199
|
+
# Drop all constraints
|
200
|
+
constraints = session.run("SHOW CONSTRAINTS").data()
|
201
|
+
for constraint in constraints:
|
202
|
+
constraint_name = constraint['name']
|
203
|
+
session.run(f"DROP CONSTRAINT {constraint_name}")
|
204
|
+
|
396
205
|
return neo4jGraph
|
397
|
-
|
398
|
-
|
399
|
-
|
206
|
+
|
207
|
+
def ByGraph(neo4jGraph,
|
208
|
+
graph,
|
209
|
+
vertexLabelKey: str = "label",
|
210
|
+
defaultVertexLabel: str = "NODE",
|
211
|
+
vertexCategoryKey: str = "category",
|
212
|
+
defaultVertexCategory: str = None,
|
213
|
+
edgeLabelKey: str = "label",
|
214
|
+
defaultEdgeLabel: str = "CONNECTED_TO",
|
215
|
+
edgeCategoryKey: str = "category",
|
216
|
+
defaultEdgeCategory: str = None,
|
217
|
+
bidirectional: bool = True,
|
218
|
+
mantissa: int = 6,
|
219
|
+
tolerance: float = 0.0001,
|
220
|
+
silent: bool = False):
|
400
221
|
"""
|
401
|
-
|
402
|
-
|
222
|
+
Converts a Topologic graph to a Neo4j graph.
|
223
|
+
|
403
224
|
Parameters
|
404
225
|
----------
|
405
|
-
neo4jGraph :
|
406
|
-
The input neo4j
|
407
|
-
|
226
|
+
neo4jGraph : neo4j._sync.driver.BoltDriver
|
227
|
+
The input neo4j bolt driver.
|
228
|
+
vertexLabelKey : str , optional
|
229
|
+
The returned vertices are labelled according to the dictionary values stored under this key.
|
230
|
+
If the vertexLabelKey does not exist, it will be created and the vertices are labelled numerically using the format defaultVertexLabel_XXX. The default is "label".
|
231
|
+
defaultVertexLabel : str , optional
|
232
|
+
The default vertex label to use if no value is found under the vertexLabelKey. The default is "NODE".
|
233
|
+
vertexCategoryKey : str , optional
|
234
|
+
The returned vertices are categorized according to the dictionary values stored under this key. The dfefault is "category".
|
235
|
+
defaultVertexCategory : str , optional
|
236
|
+
The default vertex category to use if no value is found under the vertexCategoryKey. The default is None.
|
237
|
+
edgeLabelKey : str , optional
|
238
|
+
The returned edges are labelled according to the dictionary values stored under this key.
|
239
|
+
If the edgeLabelKey does not exist, it will be created and the edges are labelled numerically using the format defaultEdgeLabel_XXX. The default is "label".
|
240
|
+
defaultEdgeLabel : str , optional
|
241
|
+
The default edge label to use if no value is found under the edgeLabelKey. The default is "CONNECTED_TO".
|
242
|
+
edgeCategoryKey : str , optional
|
243
|
+
The returned edges are categorized according to the dictionary values stored under this key. The dfefault is "category".
|
244
|
+
defaultEdgeCategory : str , optional
|
245
|
+
The default edge category to use if no value is found under the edgeCategoryKey. The default is None.
|
246
|
+
bidirectional : bool , optional
|
247
|
+
If set to True, the output Neo4j graph is forced to be bidirectional. The defaul is True.
|
248
|
+
mantissa : int , optional
|
249
|
+
The desired length of the mantissa. The default is 6.
|
250
|
+
tolerance : float , optional
|
251
|
+
The desired tolerance. The default is 0.0001.
|
252
|
+
silent : bool , optional
|
253
|
+
If set to True, no error and warning messages are printed. Otherwise, they are. The default is False.
|
254
|
+
|
408
255
|
Returns
|
409
256
|
-------
|
410
|
-
|
411
|
-
The
|
412
|
-
|
413
|
-
"""
|
414
|
-
return list(neo4jGraph.schema.node_labels)
|
415
|
-
|
416
|
-
@staticmethod
|
417
|
-
def RelationshipTypes(neo4jGraph):
|
257
|
+
neo4j._sync.driver.BoltDriver
|
258
|
+
The returned neo4j bolt driver.
|
259
|
+
|
418
260
|
"""
|
419
|
-
|
261
|
+
from topologicpy.Vertex import Vertex
|
262
|
+
from topologicpy.Edge import Edge
|
263
|
+
from topologicpy.Graph import Graph
|
264
|
+
from topologicpy.Dictionary import Dictionary
|
265
|
+
from topologicpy.Topology import Topology
|
266
|
+
|
267
|
+
def sanitize_for_neo4j(identifier):
|
268
|
+
"""
|
269
|
+
Replaces illegal characters in Neo4j labels or relationship types with an underscore ('_').
|
270
|
+
Ensures the identifier starts with an alphabetic character and contains only valid characters.
|
271
|
+
"""
|
272
|
+
import re
|
273
|
+
# Replace any non-alphanumeric characters with underscores
|
274
|
+
sanitized = re.sub(r'[^a-zA-Z0-9]', '_', identifier)
|
275
|
+
|
276
|
+
# Ensure the identifier starts with an alphabetic character
|
277
|
+
if not sanitized[0].isalpha():
|
278
|
+
sanitized = f"_{sanitized}"
|
279
|
+
|
280
|
+
return sanitized
|
420
281
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
282
|
+
if not isinstance(neo4jGraph, neo4j._sync.driver.BoltDriver):
|
283
|
+
if not silent:
|
284
|
+
print("Neo4j.ByGraph - Error: The input neo4jGraph is not a valid neo4j graph. Returning None.")
|
285
|
+
return None
|
286
|
+
if not Topology.IsInstance(graph, "Graph"):
|
287
|
+
if not silent:
|
288
|
+
print("Neo4j.ByGraph - Error: The input graph is not a valid topologic graph. Returning None.")
|
289
|
+
return None
|
290
|
+
# if not isinstance(vertexLabelKey, str):
|
291
|
+
# if not silent:
|
292
|
+
# print("Neo4j.ByGraph - Error: The input vertexLabelKey is not a valid string. Returning None.")
|
293
|
+
# return None
|
294
|
+
# if not isinstance(defaultVertexLabel, str):
|
295
|
+
# if not silent:
|
296
|
+
# print("Neo4j.ByGraph - Error: The input defaultVertexLabel is not a valid string. Returning None.")
|
297
|
+
# return None
|
298
|
+
# if not isinstance(vertexCategoryKey, str):
|
299
|
+
# if not silent:
|
300
|
+
# print("Neo4j.ByGraph - Error: The input vertexCategoryKey is not a valid string. Returning None.")
|
301
|
+
# return None
|
302
|
+
# if not isinstance(edgeLabelKey, str):
|
303
|
+
# if not silent:
|
304
|
+
# print("Neo4j.ByGraph - Error: The input vertexLabelKey is not a valid string. Returning None.")
|
305
|
+
# return None
|
306
|
+
# if not isinstance(defaultEdgeLabel, str):
|
307
|
+
# if not silent:
|
308
|
+
# print("Neo4j.ByGraph - Error: The input defaultEdgeLabel is not a valid string. Returning None.")
|
309
|
+
# return None
|
310
|
+
vertices = Graph.Vertices(graph)
|
311
|
+
edges = Graph.Edges(graph)
|
312
|
+
|
313
|
+
with neo4jGraph.session() as session:
|
314
|
+
# Create vertices (nodes in Neo4j)
|
315
|
+
n = max(len(str(len(vertices))), 3)
|
316
|
+
for i, vertex in enumerate(vertices):
|
317
|
+
vertex_props = Dictionary.PythonDictionary(Topology.Dictionary(vertex)) # Get the dictionary of vertex attributes
|
318
|
+
|
319
|
+
# Extract label and category, remove them from the properties
|
320
|
+
value = defaultVertexLabel+"_"+str(i+1).zfill(n)
|
321
|
+
vertex_label = vertex_props.pop(vertexLabelKey, value)
|
322
|
+
vertex_label = sanitize_for_neo4j(vertex_label)
|
323
|
+
vertex_category = vertex_props.pop(vertexCategoryKey, defaultVertexCategory) # Extract category if it exists
|
324
|
+
# Add coordinates to the vertex properties
|
325
|
+
vertex_props.update({
|
326
|
+
'x': Vertex.X(vertex, mantissa=mantissa), # X coordinate
|
327
|
+
'y': Vertex.Y(vertex, mantissa=mantissa), # Y coordinate
|
328
|
+
'z': Vertex.Z(vertex, mantissa=mantissa), # Z coordinate
|
329
|
+
})
|
330
|
+
|
331
|
+
if not vertex_category == None:
|
332
|
+
vertex_props['category'] = vertex_category # Add category to properties if it exists
|
333
|
+
if not vertex_label == None:
|
334
|
+
vertex_props[vertexLabelKey] = vertex_label # Add label to properties if it exists
|
335
|
+
|
336
|
+
vertex_props['id'] = i
|
337
|
+
|
338
|
+
# Create a node with dynamic label and properties
|
339
|
+
session.run(f"""
|
340
|
+
CREATE (n:{vertex_label} $properties)
|
341
|
+
""", properties=vertex_props)
|
342
|
+
|
343
|
+
# Create edges (relationships in Neo4j)
|
344
|
+
for edge in edges:
|
345
|
+
edge_props = Dictionary.PythonDictionary(Topology.Dictionary(edge)) # Get the dictionary of edge attributes
|
346
|
+
|
347
|
+
# Extract label and category for the relationship
|
348
|
+
edge_label = edge_props.pop(edgeLabelKey, defaultEdgeLabel) # Default label is 'CONNECTED_TO'
|
349
|
+
edge_label = sanitize_for_neo4j(edge_label)
|
350
|
+
edge_category = edge_props.pop(edgeCategoryKey, defaultEdgeCategory) # Extract category if it exists
|
351
|
+
|
352
|
+
start_vertex = Edge.StartVertex(edge) # Get the starting vertex of the edge
|
353
|
+
start_id = Vertex.Index(vertex=start_vertex, vertices=vertices, strict=False, tolerance=tolerance)
|
354
|
+
end_vertex = Edge.EndVertex(edge) # Get the ending vertex of the edge
|
355
|
+
end_id = Vertex.Index(vertex=end_vertex, vertices=vertices, strict=False, tolerance=tolerance)
|
356
|
+
|
357
|
+
# Add category to edge properties if it exists
|
358
|
+
if not edge_category == None:
|
359
|
+
edge_props['category'] = edge_category
|
360
|
+
if not edge_label == None:
|
361
|
+
edge_props[edgeLabelKey] = edge_label # Add label to properties if it exists
|
362
|
+
# Create the relationship with dynamic label and properties
|
363
|
+
session.run(f"""
|
364
|
+
MATCH (a {{id: $start_id}}), (b {{id: $end_id}})
|
365
|
+
CREATE (a)-[r:{edge_label} $properties]->(b)
|
366
|
+
""", start_id=start_id, end_id=end_id, properties=edge_props)
|
367
|
+
|
368
|
+
# If the graph is bi-directional, add the reverse edge as well
|
369
|
+
if bidirectional:
|
370
|
+
session.run(f"""
|
371
|
+
MATCH (a {{id: $end_id}}), (b {{id: $start_id}})
|
372
|
+
CREATE (a)-[r:{edge_label} $properties]->(b)
|
373
|
+
""", start_id=start_id, end_id=end_id, properties=edge_props)
|
374
|
+
|
375
|
+
return neo4jGraph
|
425
376
|
|
426
|
-
Returns
|
427
|
-
-------
|
428
|
-
list
|
429
|
-
The list of relationship types used in the input neo4j graph.
|
430
377
|
|
431
|
-
"""
|
432
|
-
return list(neo4jGraph.schema.relationship_types)
|
433
|
-
|
434
378
|
@staticmethod
|
435
379
|
def SetGraph(neo4jGraph,
|
436
380
|
graph,
|