Skip to content

Commit 2c7e99f

Browse files
authored
Fix Gremlin visualizations failing to render with UUID type IDs (#475)
* Fix Gremlin visualizations with UUID type IDs * remove dev comment * update changelog --------- Co-authored-by: Michael Chin <chnmch@amazon.com>
1 parent 573fb7b commit 2c7e99f

File tree

4 files changed

+219
-14
lines changed

4 files changed

+219
-14
lines changed

ChangeLog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ Starting with v1.31.6, this file will contain a record of major features and upd
44

55
## Upcoming
66
- Added support for Python 3.10 ([Link to PR](https://github.com/aws/graph-notebook/pull/476))
7+
- Deprecated Python 3.7 support ([PR #1](https://github.com/aws/graph-notebook/pull/453)) ([PR #2](https://github.com/aws/graph-notebook/pull/473))
78
- Fixed Dockerfile builds breaking with AL2023 ([Link to PR](https://github.com/aws/graph-notebook/pull/466))
89
- Fixed `--store-to` option for several magics ([Link to PR](https://github.com/aws/graph-notebook/pull/463))
910
- Fixed broken documentation links in Neptune ML notebooks ([PR #1](https://github.com/aws/graph-notebook/pull/467)) ([PR #2](https://github.com/aws/graph-notebook/pull/468))
10-
- Deprecated Python 3.7 support ([PR #1](https://github.com/aws/graph-notebook/pull/453)) ([PR #2](https://github.com/aws/graph-notebook/pull/473))
11+
- Fixed Gremlin graph tab not rendering with UUID type IDs ([Link to PR](https://github.com/aws/graph-notebook/pull/475))
1112

1213
## Release 3.7.3 (March 14, 2023)
1314
- Fixed detailed mode output for graph summary requests ([Link to PR](https://github.com/aws/graph-notebook/pull/461))

src/graph_notebook/magics/graph_magic.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,7 @@ def gremlin(self, line, cell, local_ns: dict = None):
936936
query_time=query_time)
937937
titles.append('Console')
938938

939+
gremlin_network = None
939940
try:
940941
logger.debug(f'groupby: {args.group_by}')
941942
logger.debug(f'display_property: {args.display_property}')
@@ -957,18 +958,24 @@ def gremlin(self, line, cell, local_ns: dict = None):
957958
else:
958959
pattern = parse_pattern_list_str(args.path_pattern)
959960
gn.add_results_with_pattern(query_res, pattern)
961+
gremlin_network = gn
960962
logger.debug(f'number of nodes is {len(gn.graph.nodes)}')
961-
if len(gn.graph.nodes) > 0:
963+
except ValueError as value_error:
964+
logger.debug(
965+
f'Unable to create graph network from result due to error: {value_error}. '
966+
f'Skipping from result set.')
967+
if gremlin_network and len(gremlin_network.graph.nodes) > 0:
968+
try:
962969
self.graph_notebook_vis_options['physics']['disablePhysicsAfterInitialSimulation'] \
963970
= args.stop_physics
964971
self.graph_notebook_vis_options['physics']['simulationDuration'] = args.simulation_duration
965-
f = Force(network=gn, options=self.graph_notebook_vis_options)
972+
f = Force(network=gremlin_network, options=self.graph_notebook_vis_options)
966973
titles.append('Graph')
967974
children.append(f)
968975
logger.debug('added gremlin network to tabs')
969-
except ValueError as value_error:
970-
logger.debug(
971-
f'unable to create gremlin network from result. Skipping from result set: {value_error}')
976+
except Exception as force_error:
977+
logger.debug(
978+
f'Unable to render visualization from graph network due to error: {force_error}. Skipping.')
972979

973980
# Check if we can access the CDNs required by itables library.
974981
# If not, then render our own HTML template.

src/graph_notebook/network/gremlin/GremlinNetwork.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,21 @@ def get_id(element):
7878
extract id from a given element for use in the GremlinNetwork
7979
"""
8080
if isinstance(element, Vertex):
81-
return str(element.id)
81+
element_id = str(element.id)
8282
elif isinstance(element, Edge):
83-
return str(element.label)
83+
element_id = str(element.label)
8484
elif isinstance(element, dict):
8585
if T.id in element:
86-
return element[T.id]
86+
element_id = element[T.id]
8787
else:
88-
return generate_id_from_dict(element)
88+
element_id = generate_id_from_dict(element)
8989
else:
90-
return str(element)
90+
element_id = str(element)
91+
92+
if isinstance(element_id, uuid.UUID):
93+
element_id = str(element_id)
94+
95+
return element_id
9196

9297

9398
class GremlinNetwork(EventfulNetwork):
@@ -342,7 +347,10 @@ def add_vertex(self, v, path_index: int = -1):
342347
depth_group = "__DEPTH-" + str(path_index) + "__"
343348
node_id = ''
344349
if type(v) is Vertex:
345-
node_id = v.id
350+
if isinstance(v.id, uuid.UUID):
351+
node_id = str(v.id)
352+
else:
353+
node_id = v.id
346354
title = v.label
347355
label_full = ''
348356
tooltip_display_is_set = True
@@ -447,6 +455,8 @@ def add_vertex(self, v, path_index: int = -1):
447455
properties[k] = copy_val
448456
elif isinstance(v[k], Decimal):
449457
properties[k] = float(v[k])
458+
elif isinstance(v[k], uuid.UUID):
459+
properties[k] = str(v[k])
450460
else:
451461
properties[k] = v[k]
452462

@@ -529,15 +539,20 @@ def add_path_edge(self, edge, from_id='', to_id='', data=None):
529539

530540
if type(edge) is Edge:
531541
from_id = from_id if from_id != '' else edge.outV.id
542+
if isinstance(from_id, uuid.UUID):
543+
from_id = str(from_id)
532544
to_id = to_id if to_id != '' else edge.inV.id
545+
if isinstance(to_id, uuid.UUID):
546+
to_id = str(to_id)
547+
edge_id = str(edge.id) if isinstance(edge.id, uuid.UUID) else edge.id
533548
edge_label_full = ''
534549
using_custom_tooltip = False
535550
tooltip_display_is_set = True
536551
edge_title = edge.label
537552
if self.edge_tooltip_property and self.edge_tooltip_property != self.edge_display_property:
538553
using_custom_tooltip = True
539554
tooltip_display_is_set = False
540-
data['properties'] = {'id': edge.id, 'label': edge.label, 'outV': str(edge.outV), 'inV': str(edge.inV)}
555+
data['properties'] = {'id': edge_id, 'label': edge.label, 'outV': str(edge.outV), 'inV': str(edge.inV)}
541556

542557
edge_label = edge_title if len(edge_title) <= self.edge_label_max_length \
543558
else edge_title[:self.edge_label_max_length - 3] + '...'
@@ -564,7 +579,7 @@ def add_path_edge(self, edge, from_id='', to_id='', data=None):
564579
self.get_explicit_edge_property_value(data, edge, self.edge_tooltip_property)
565580

566581
data['title'] = edge_title
567-
self.add_edge(from_id=from_id, to_id=to_id, edge_id=edge.id, label=edge_label, title=edge_title,
582+
self.add_edge(from_id=from_id, to_id=to_id, edge_id=edge_id, label=edge_label, title=edge_title,
568583
data=data)
569584
elif type(edge) is dict:
570585
properties = {}
@@ -592,6 +607,8 @@ def add_path_edge(self, edge, from_id='', to_id='', data=None):
592607
properties[k] = get_id(edge[k])
593608
elif isinstance(edge[k], Decimal):
594609
properties[k] = float(edge[k])
610+
elif isinstance(edge[k], uuid.UUID):
611+
properties[k] = str(edge[k])
595612
else:
596613
properties[k] = edge[k]
597614

test/unit/network/gremlin/test_gremlin_network.py

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import unittest
77
from decimal import Decimal
8+
from uuid import uuid4
89
from gremlin_python.structure.graph import Path, Edge, Vertex
910
from gremlin_python.process.traversal import T, Direction
1011
from graph_notebook.network.EventfulNetwork import EVENT_ADD_NODE
@@ -191,6 +192,75 @@ def test_add_explicit_type_vertex_with_valid_label_and_invalid_tooltip(self):
191192
self.assertEqual(node1['label'], '1')
192193
self.assertEqual(node1['title'], '1')
193194

195+
def test_add_explicit_type_vertex_with_string_id(self):
196+
v_id = 'a_id'
197+
vertex = Vertex(id=v_id)
198+
199+
gn = GremlinNetwork()
200+
gn.add_vertex(vertex)
201+
node = gn.graph.nodes.get(v_id)
202+
self.assertIsNotNone(node)
203+
self.assertEqual(node['properties']['id'], v_id)
204+
205+
def test_add_explicit_type_vertex_with_uuid_id(self):
206+
v_id = uuid4()
207+
vertex = Vertex(id=v_id)
208+
209+
gn = GremlinNetwork()
210+
gn.add_vertex(vertex)
211+
node = gn.graph.nodes.get(str(v_id))
212+
self.assertIsNotNone(node)
213+
self.assertEqual(node['properties']['id'], str(v_id))
214+
215+
def test_add_explicit_type_vertex_with_integer_id(self):
216+
v_id = 1
217+
vertex = Vertex(id=v_id)
218+
219+
gn = GremlinNetwork()
220+
gn.add_vertex(vertex)
221+
node = gn.graph.nodes.get(v_id)
222+
self.assertIsNotNone(node)
223+
self.assertEqual(node['properties']['id'], v_id)
224+
225+
def test_add_vertex_with_string_id(self):
226+
v_id = 'a_id'
227+
vertex = {
228+
T.id: v_id,
229+
T.label: 'label'
230+
}
231+
232+
gn = GremlinNetwork()
233+
gn.add_vertex(vertex)
234+
node = gn.graph.nodes.get(v_id)
235+
self.assertIsNotNone(node)
236+
self.assertEqual(node['properties'][T.id], v_id)
237+
238+
def test_add_vertex_with_uuid_id(self):
239+
v_id = uuid4()
240+
vertex = {
241+
T.id: v_id,
242+
T.label: 'label'
243+
}
244+
245+
gn = GremlinNetwork()
246+
gn.add_vertex(vertex)
247+
node = gn.graph.nodes.get(str(v_id))
248+
self.assertIsNotNone(node)
249+
self.assertEqual(node['properties'][T.id], str(v_id))
250+
251+
def test_add_vertex_with_integer_id(self):
252+
v_id = 99
253+
vertex = {
254+
T.id: v_id,
255+
T.label: 'label'
256+
}
257+
258+
gn = GremlinNetwork()
259+
gn.add_vertex(vertex)
260+
node = gn.graph.nodes.get(str(v_id))
261+
self.assertIsNotNone(node)
262+
self.assertEqual(node['properties'][T.id], v_id)
263+
194264
def test_add_vertex_without_node_property(self):
195265
vertex = {
196266
T.id: '1234',
@@ -753,6 +823,51 @@ def test_add_vertex_with_Decimal_type_property_in_list(self):
753823
self.assertIsInstance(final_lon_value, float)
754824
self.assertIsInstance(final_lat_value, float)
755825

826+
def test_add_explicit_type_single_edge_with_string_id(self):
827+
vertex1 = Vertex(id='1')
828+
vertex2 = Vertex(id='2')
829+
e_id = '1'
830+
831+
edge1 = Edge(id=e_id, outV=vertex1, inV=vertex2, label='route')
832+
833+
gn = GremlinNetwork()
834+
gn.add_vertex(vertex1)
835+
gn.add_vertex(vertex2)
836+
gn.add_path_edge(edge1)
837+
edge = gn.graph.get_edge_data('1', '2')
838+
self.assertIsNotNone(edge)
839+
self.assertEqual(edge[e_id]['properties']['id'], e_id)
840+
841+
def test_add_explicit_type_single_edge_with_uuid_id(self):
842+
vertex1 = Vertex(id='1')
843+
vertex2 = Vertex(id='2')
844+
e_id = uuid4()
845+
846+
edge1 = Edge(id=e_id, outV=vertex1, inV=vertex2, label='route')
847+
848+
gn = GremlinNetwork()
849+
gn.add_vertex(vertex1)
850+
gn.add_vertex(vertex2)
851+
gn.add_path_edge(edge1)
852+
edge = gn.graph.get_edge_data('1', '2')
853+
self.assertIsNotNone(edge)
854+
self.assertEqual(edge[str(e_id)]['properties']['id'], str(e_id))
855+
856+
def test_add_explicit_type_single_edge_with_integer_id(self):
857+
vertex1 = Vertex(id='1')
858+
vertex2 = Vertex(id='2')
859+
e_id = 1
860+
861+
edge1 = Edge(id=e_id, outV=vertex1, inV=vertex2, label='route')
862+
863+
gn = GremlinNetwork()
864+
gn.add_vertex(vertex1)
865+
gn.add_vertex(vertex2)
866+
gn.add_path_edge(edge1)
867+
edge = gn.graph.get_edge_data('1', '2')
868+
self.assertIsNotNone(edge)
869+
self.assertEqual(edge[e_id]['properties']['id'], e_id)
870+
756871
def test_add_explicit_type_single_edge_without_edge_property(self):
757872
vertex1 = Vertex(id='1')
758873
vertex2 = Vertex(id='2')
@@ -981,6 +1096,51 @@ def test_add_explicit_type_single_edge_with_valid_label_and_invalid_tooltip(self
9811096
self.assertEqual(edge['1']['label'], 'v[2]')
9821097
self.assertEqual(edge['1']['title'], 'v[2]')
9831098

1099+
def test_add_single_edge_with_string_id(self):
1100+
vertex1 = Vertex(id='1')
1101+
vertex2 = Vertex(id='2')
1102+
e_id = '1'
1103+
1104+
edge1 = {T.id: e_id, T.label: 'route', 'outV': 'v[1]', 'inV': 'v[2]'}
1105+
1106+
gn = GremlinNetwork()
1107+
gn.add_vertex(vertex1)
1108+
gn.add_vertex(vertex2)
1109+
gn.add_path_edge(edge1, from_id='1', to_id='2')
1110+
edge = gn.graph.get_edge_data('1', '2')
1111+
self.assertIsNotNone(edge)
1112+
self.assertEqual(edge[e_id]['properties'][T.id], e_id)
1113+
1114+
def test_add_single_edge_with_uuid_id(self):
1115+
vertex1 = Vertex(id='1')
1116+
vertex2 = Vertex(id='2')
1117+
e_id = uuid4()
1118+
1119+
edge1 = {T.id: e_id, T.label: 'route', 'outV': 'v[1]', 'inV': 'v[2]'}
1120+
1121+
gn = GremlinNetwork()
1122+
gn.add_vertex(vertex1)
1123+
gn.add_vertex(vertex2)
1124+
gn.add_path_edge(edge1, from_id='1', to_id='2')
1125+
edge = gn.graph.get_edge_data('1', '2')
1126+
self.assertIsNotNone(edge)
1127+
self.assertEqual(edge[str(e_id)]['properties'][T.id], str(e_id))
1128+
1129+
def test_add_single_edge_with_integer_id(self):
1130+
vertex1 = Vertex(id='1')
1131+
vertex2 = Vertex(id='2')
1132+
e_id = 1
1133+
1134+
edge1 = {T.id: e_id, T.label: 'route', 'outV': 'v[1]', 'inV': 'v[2]'}
1135+
1136+
gn = GremlinNetwork()
1137+
gn.add_vertex(vertex1)
1138+
gn.add_vertex(vertex2)
1139+
gn.add_path_edge(edge1, from_id='1', to_id='2')
1140+
edge = gn.graph.get_edge_data('1', '2')
1141+
self.assertIsNotNone(edge)
1142+
self.assertEqual(edge[str(e_id)]['properties'][T.id], e_id)
1143+
9841144
def test_add_single_edge_without_edge_property(self):
9851145
vertex1 = Vertex(id='1')
9861146
vertex2 = Vertex(id='2')
@@ -2213,6 +2373,26 @@ def test_add_elementmap_edge(self):
22132373
self.assertEqual(inv_data['properties'], edge_map[Direction.IN])
22142374
self.assertEqual(outv_data['properties'], edge_map[Direction.OUT])
22152375

2376+
def test_add_elementmap_edge_with_direction_uuid_ids(self):
2377+
in_node_id = uuid4()
2378+
out_node_id = uuid4()
2379+
edge_map = {
2380+
T.id: '5298',
2381+
T.label: 'route',
2382+
Direction.IN: {T.id: in_node_id, T.label: 'airport'},
2383+
Direction.OUT: {T.id: out_node_id, T.label: 'airport'},
2384+
'dist': 763
2385+
}
2386+
2387+
gn = GremlinNetwork()
2388+
gn.insert_elementmap(edge_map, index=1)
2389+
inv_data = gn.graph.nodes.get(str(in_node_id))
2390+
outv_data = gn.graph.nodes.get(str(out_node_id))
2391+
self.assertIsNotNone(inv_data)
2392+
self.assertIsNotNone(outv_data)
2393+
self.assertEqual(inv_data['properties'][T.id], str(edge_map[Direction.IN][T.id]))
2394+
self.assertEqual(outv_data['properties'][T.id], str(edge_map[Direction.OUT][T.id]))
2395+
22162396
def test_add_elementmap_edge_groupby_depth(self):
22172397
edge_map = {
22182398
T.id: '5298',

0 commit comments

Comments
 (0)