Skip to content

Commit 528017f

Browse files
theneelshahNeel Shah
andauthored
Show graph visualization for bolt queries (#761)
* Add graph visualization support for bolt protocol requests Fixes #557 by converting bolt responses to compatible format for graph tab display. Transforms nodes and relationships to standardized format with ~id, ~entityType, ~labels/~type, and ~properties field. * Add graph visualization support for bolt protocol requests Fixes #557 by converting bolt responses to compatible format for graph tab display. Transforms nodes and relationships to standardized format with ~id, ~entityType, ~labels/~type, and ~properties field. * Update graph_magic.py --------- Co-authored-by: Neel Shah <neeljs@amazon.com>
1 parent 6fc775c commit 528017f

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

src/graph_notebook/magics/graph_magic.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3764,7 +3764,33 @@ def handle_opencypher_query(self, line, cell, local_ns):
37643764
results_df, has_results = oc_results_df(res, res_format)
37653765
if has_results:
37663766
titles.append('Console')
3767-
# Need to eventually add code to parse and display a network for the bolt format here
3767+
# Create graph visualization for bolt response
3768+
try:
3769+
# Wrap bolt response in expected format
3770+
# Required because the graph visualizer need the data to be present in a certain format
3771+
transformed_res = {"results": res} if isinstance(res, list) else {"results": []}
3772+
3773+
gn = OCNetwork(group_by_property=args.group_by, display_property=args.display_property,
3774+
group_by_raw=args.group_by_raw,
3775+
group_by_depth=args.group_by_depth,
3776+
edge_display_property=args.edge_display_property,
3777+
tooltip_property=args.tooltip_property,
3778+
edge_tooltip_property=args.edge_tooltip_property,
3779+
label_max_length=args.label_max_length,
3780+
edge_label_max_length=args.rel_label_max_length,
3781+
ignore_groups=args.ignore_groups)
3782+
gn.add_results(transformed_res)
3783+
logger.debug(f'number of nodes is {len(gn.graph.nodes)}')
3784+
if len(gn.graph.nodes) > 0:
3785+
self.graph_notebook_vis_options['physics']['disablePhysicsAfterInitialSimulation'] \
3786+
= args.stop_physics
3787+
self.graph_notebook_vis_options['physics']['simulationDuration'] = args.simulation_duration
3788+
force_graph_output = Force(network=gn, options=self.graph_notebook_vis_options)
3789+
titles.append('Graph')
3790+
children.append(force_graph_output)
3791+
except (TypeError, ValueError) as network_creation_error:
3792+
logger.debug(f'Unable to create network from bolt result. Skipping from result set: {res}')
3793+
logger.debug(f'Error: {network_creation_error}')
37683794

37693795
if not args.silent:
37703796
if args.mode != 'explain':

src/graph_notebook/neptune/client.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,34 @@ def opencyper_bolt(self, query: str, **kwargs):
606606
with driver.session(database=self.neo4j_database) as session:
607607
try:
608608
res = session.run(query, kwargs)
609-
data = res.data()
609+
data = []
610+
for record in res:
611+
record_dict = {}
612+
for key in record.keys():
613+
value = record[key]
614+
if hasattr(value, 'id') and (hasattr(value, 'labels') or hasattr(value, 'type')): # Node or Relationship
615+
if hasattr(value, 'labels'): # Node
616+
record_dict[key] = {
617+
'~id': str(value.id),
618+
'~entityType': 'node',
619+
'~labels': list(value.labels),
620+
'~properties': dict(value)
621+
}
622+
elif hasattr(value, 'type'): # Relationship
623+
624+
start_node = value.nodes[0] if value.nodes else None
625+
end_node = value.nodes[1] if len(value.nodes) > 1 else None
626+
record_dict[key] = {
627+
'~id': str(value.id),
628+
'~entityType': 'relationship',
629+
'~start': str(start_node.id) if start_node else '',
630+
'~end': str(end_node.id) if end_node else '',
631+
'~type': value.type,
632+
'~properties': dict(value)
633+
}
634+
else:
635+
record_dict[key] = value
636+
data.append(record_dict)
610637
except AuthError:
611638
print("Neo4J Bolt request failed with an authentication error. Please ensure that the 'neo4j' section "
612639
"of your %graph_notebook_config contains the correct credentials and auth setting.")

src/graph_notebook/network/opencypher/OCNetwork.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@
2626

2727
class OCNetwork(EventfulNetwork):
2828
"""OCNetwork extends the EventfulNetwork class and uses the add_results method to parse any response that returns
29-
nodes/relationships as part (or all) of the response. Currently this only works with HTTPS response format but in
30-
the future, we will work to support Bolt based responses as well.
29+
nodes/relationships as part (or all) of the response.
3130
"""
3231

3332
def __init__(self, graph: MultiDiGraph = None, callbacks=None, label_max_length=DEFAULT_LABEL_MAX_LENGTH,

0 commit comments

Comments
 (0)