Skip to content

Commit

Permalink
Add all group-less nodes to a default visualization group (aws#175)
Browse files Browse the repository at this point in the history
* Add all group-less nodes to a default visualization group

* Update Changelog

Co-authored-by: Michael Chin <[email protected]>
  • Loading branch information
michaelnchin and michaelnchin authored Aug 9, 2021
1 parent 3b0469d commit c20cee8
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 53 deletions.
6 changes: 4 additions & 2 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
Starting with v1.31.6, this file will contain a record of major features and updates made in each release of graph-notebook.

## Upcoming
- Gremlin visualization bugfixes ([PR #1](https://github.com/aws/graph-notebook/pull/166)) ([PR #2](https://github.com/aws/graph-notebook/pull/174))
- Gremlin visualization bugfixes ([PR #1](https://github.com/aws/graph-notebook/pull/166)) ([PR #2](https://github.com/aws/graph-notebook/pull/174)) ([PR #3](https://github.com/aws/graph-notebook/pull/175))
- Updated the airport data loadable via %seed to the latest version ([Link to PR](https://github.com/aws/graph-notebook/pull/172))
- Added support for Gremlin Profile API parameters ([Link to PR](https://github.com/aws/graph-notebook/pull/171))
- Improved %seed so the progress bar is seen to complete ([Link to PR](https://github.com/aws/graph-notebook/pull/173))
- Improved %seed so that the progress bar is seen to complete ([Link to PR](https://github.com/aws/graph-notebook/pull/173))
- Added helper functions to neptune_ml utils to get node embeddings, model predictions and performance metrics ([Link to PR](https://github.com/aws/graph-notebook/pull/170))
- Changed visualization behavior to add all group-less nodes to a default group ([Link to PR](https://github.com/aws/graph-notebook/pull/175))

## Release 3.0.2 (July 29, 2021)

Expand Down
29 changes: 19 additions & 10 deletions src/graph_notebook/network/gremlin/GremlinNetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

T_LABEL = 'T.label'
T_ID = 'T.id'
DEFAULT_GRP = 'DEFAULT_GROUP'

INVALID_PATH_ERROR = ValueError("results must be a path with the pattern Vertex -> Edge -> Vertex.")
INVALID_VERTEX_ERROR = ValueError("when adding a vertex, object must be of type Vertex or Dict")
Expand Down Expand Up @@ -265,12 +266,11 @@ def add_results(self, results):
is_elementmap = True
break
if is_elementmap:
self.insert_elementmap(path[i])
self.insert_elementmap(path[i], check_emap=True, path_element=path, index=i)
else:
self.insert_path_element(path, i)
else:
self.insert_path_element(path, i)

elif isinstance(path, dict) and T.id in path.keys() and T.label in path.keys():
self.insert_elementmap(path)
else:
Expand Down Expand Up @@ -298,7 +298,7 @@ def add_vertex(self, v):
elif str(self.group_by_property) in [T_ID, 'id']:
group = v.id
else:
group = ''
group = DEFAULT_GRP
else: # handle dict format group_by
try:
if str(v.label) in self.group_by_property:
Expand All @@ -309,9 +309,9 @@ def add_vertex(self, v):
else:
group = vertex_dict[self.group_by_property[str(v.label)]]
else:
group = ''
group = DEFAULT_GRP
except KeyError:
group = ''
group = DEFAULT_GRP

label = title if len(title) <= self.label_max_length else title[:self.label_max_length - 3] + '...'

Expand All @@ -338,6 +338,8 @@ def add_vertex(self, v):
if T.label in v.keys():
title = str(v[T.label])
title_plc, label = self.strip_and_truncate_label_and_title(title, self.label_max_length)
else:
group = DEFAULT_GRP
for k in v:
if str(k) == T_ID:
node_id = str(v[k])
Expand Down Expand Up @@ -376,10 +378,10 @@ def add_vertex(self, v):
node_id = str(v)
title = str(v)
label = title if len(title) <= self.label_max_length else title[:self.label_max_length - 3] + '...'
data = {'title': title, 'label': label, 'group': ''}
data = {'title': title, 'label': label, 'group': DEFAULT_GRP}

if self.ignore_groups:
data['group'] = ''
data['group'] = DEFAULT_GRP
self.add_node(node_id, data)

def add_path_edge(self, edge, from_id='', to_id='', data=None):
Expand Down Expand Up @@ -480,9 +482,13 @@ def insert_path_element(self, path, i):

self.add_vertex(path[i])
if type(path[i - 1]) is not Edge:
self.add_blank_edge(from_id, get_id(path[i]))
if type(path[i - 1]) is dict:
if Direction.IN not in path[i-1]:
self.add_blank_edge(from_id, get_id(path[i]))
else:
self.add_blank_edge(from_id, get_id(path[i]))

def insert_elementmap(self, e_map):
def insert_elementmap(self, e_map, check_emap=False, path_element=None, index=None):
"""
Add a vertex or edge that has been returned by an elementMap query step.
Expand All @@ -505,4 +511,7 @@ def insert_elementmap(self, e_map):
# Handle vertex elementMap
else:
# Overwrite the the default node created by edge elementMap, if it exists already.
self.add_vertex(e_map)
if check_emap:
self.insert_path_element(path_element, index)
else:
self.add_vertex(e_map)
51 changes: 28 additions & 23 deletions src/graph_notebook/network/opencypher/OCNetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
LABEL_KEY = "~labels"
NODE_ENTITY_TYPE = 'node'
REL_ENTITY_TYPE = 'relationship'
DEFAULT_GRP = 'DEFAULT_GROUP'


class OCNetwork(EventfulNetwork):
Expand Down Expand Up @@ -69,14 +70,17 @@ def parse_node(self, node: dict):
title += str(node[key])

if not isinstance(self.group_by_property, dict): # Handle string format group_by
if self.group_by_property in [LABEL_KEY, 'labels'] and len(node[LABEL_KEY]) > 0:
group = node[LABEL_KEY][0]
elif self.group_by_property in [ID_KEY, 'id']:
group = node[ID_KEY]
elif self.group_by_property in node[PROPERTIES_KEY]:
group = node[PROPERTIES_KEY][self.group_by_property]
else:
group = ''
try:
if self.group_by_property in [LABEL_KEY, 'labels'] and len(node[LABEL_KEY]) > 0:
group = node[LABEL_KEY][0]
elif self.group_by_property in [ID_KEY, 'id']:
group = node[ID_KEY]
elif self.group_by_property in node[PROPERTIES_KEY]:
group = node[PROPERTIES_KEY][self.group_by_property]
else:
group = DEFAULT_GRP
except KeyError:
group = DEFAULT_GRP
else: # handle dict format group_by
try:
if str(node[LABEL_KEY][0]) in self.group_by_property and len(node[LABEL_KEY]) > 0:
Expand All @@ -88,35 +92,36 @@ def parse_node(self, node: dict):
elif ID_KEY in self.group_by_property:
group = node[ID_KEY]
else:
group = ''
group = DEFAULT_GRP
except KeyError:
group = ''
group = DEFAULT_GRP

props = self.flatten(node)
if isinstance(self.display_property, dict):
try:
try:
if isinstance(self.display_property, dict):
if self.display_property[title] in props:
label = str(props[self.display_property[title]])
elif LABEL_KEY in props:
label = props[LABEL_KEY]
else:
label = str(props)
except KeyError:
elif self.display_property in [ID_KEY, 'id']:
label = str(node[ID_KEY])
elif self.display_property in [LABEL_KEY, 'label']:
label = str(node[LABEL_KEY])
elif self.display_property in [VERTEX_TYPE_KEY, 'type']:
label = str(node[VERTEX_TYPE_KEY])
elif self.display_property in props:
label = props[self.display_property]
else:
label = title
pass
elif self.display_property in [ID_KEY, 'id']:
label = str(node[ID_KEY])
elif self.display_property in [LABEL_KEY, 'label']:
label = str(node[LABEL_KEY])
elif self.display_property in [VERTEX_TYPE_KEY, 'type']:
label = str(node[VERTEX_TYPE_KEY])
elif self.display_property in props:
label = props[self.display_property]
else:
except KeyError:
label = title

title, label = self.strip_and_truncate_label_and_title(label, self.label_max_length)
data = {'properties': props, 'label': label, 'title': title, 'group': group}
if self.ignore_groups:
data['group'] = DEFAULT_GRP
self.add_node(node[ID_KEY], data)

def parse_rel(self, rel):
Expand Down
34 changes: 16 additions & 18 deletions test/unit/network/gremlin/test_gremlin_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -1033,7 +1033,7 @@ def test_group_notokens_without_groupby(self):
gn = GremlinNetwork()
gn.add_vertex(vertex)
node = gn.graph.nodes.get('graph_notebook-ed8fddedf251d3d5745dccfd53edf51d')
self.assertEqual(node['group'], '')
self.assertEqual(node['group'], 'DEFAULT_GROUP')

def test_add_path_with_edge_property_string(self):
vertex1 = Vertex(id='1')
Expand Down Expand Up @@ -1141,12 +1141,12 @@ def test_ignore_group(self):
gn = GremlinNetwork(ignore_groups=True)
gn.add_vertex(vertex)
node = gn.graph.nodes.get(vertex[T.id])
self.assertEqual(node['group'], '')
self.assertEqual(node['group'], 'DEFAULT_GROUP')

gn = GremlinNetwork(group_by_property="code", ignore_groups=True)
gn.add_vertex(vertex)
node = gn.graph.nodes.get(vertex[T.id])
self.assertEqual(node['group'], '')
self.assertEqual(node['group'], 'DEFAULT_GROUP')

def test_ignore_group_properties_json(self):
vertex1 = {
Expand All @@ -1170,17 +1170,17 @@ def test_ignore_group_properties_json(self):
gn.add_vertex(vertex2)
node1 = gn.graph.nodes.get(vertex1[T.id])
node2 = gn.graph.nodes.get(vertex2[T.id])
self.assertEqual(node1['group'], '')
self.assertEqual(node2['group'], '')
self.assertEqual(node1['group'], 'DEFAULT_GROUP')
self.assertEqual(node2['group'], 'DEFAULT_GROUP')

gn = GremlinNetwork(group_by_property='{"airport":"code","country":"continent"}',
ignore_groups=True)
gn.add_vertex(vertex1)
gn.add_vertex(vertex2)
node1 = gn.graph.nodes.get(vertex1[T.id])
node2 = gn.graph.nodes.get(vertex2[T.id])
self.assertEqual(node1['group'], '')
self.assertEqual(node2['group'], '')
self.assertEqual(node1['group'], 'DEFAULT_GROUP')
self.assertEqual(node2['group'], 'DEFAULT_GROUP')

def test_group_returnvertex_groupby_notspecified(self):
vertex = Vertex(id='1')
Expand Down Expand Up @@ -1501,16 +1501,14 @@ def test_add_results_as_path_containing_elementmaps(self):
}

edge_expected = {
'5298': {
'properties': {
T.id: '5298',
T.label: 'route',
Direction.IN: '1112',
Direction.OUT: '2',
'dist': 763
},
'label': 'route'
}
'properties': {
T.id: '5298',
T.label: 'route',
Direction.IN: '1112',
Direction.OUT: '2',
'dist': 763
},
'label': 'route'
}

path = Path([], [edge_map, out_vertex, in_vertex])
Expand All @@ -1521,7 +1519,7 @@ def test_add_results_as_path_containing_elementmaps(self):
inv_data = gn.graph.nodes.get('1112')
self.assertEqual(outv_data['properties'], out_vertex)
self.assertEqual(inv_data['properties'], in_vertex)
self.assertEqual(edge_data, edge_expected)
self.assertEqual(edge_data['5298'], edge_expected)

def test_add_results_as_path_containing_valuemaps(self):

Expand Down
55 changes: 55 additions & 0 deletions test/unit/network/opencypher/test_opencypher_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,61 @@ def test_path(self):
self.assertEqual(2, len(gn.graph.nodes))
self.assertEqual(1, len(gn.graph.edges))

def test_ignore_group(self):
res = {
"results": [
{
"a": {
"~id": "22",
"~entityType": "node",
"~labels": [
"airport"
],
"~properties": {
"runways": 3,
"code": "SEA"
}
}
}
]
}

gn = OCNetwork(ignore_groups=True)
gn.add_results(res)
node = gn.graph.nodes.get('22')
self.assertEqual(node['group'], 'DEFAULT_GROUP')

gn = OCNetwork(group_by_property="code", ignore_groups=True)
gn.add_results(res)
node = gn.graph.nodes.get('22')
self.assertEqual(node['group'], 'DEFAULT_GROUP')

def test_default_groups_no_label(self):
res = {
"results": [
{
"a": {
"~id": "22",
"~entityType": "node",
"~properties": {
"runways": 3,
"code": "SEA"
}
}
}
]
}

gn = OCNetwork(ignore_groups=True)
gn.add_results(res)
node = gn.graph.nodes.get('22')
self.assertEqual(node['group'], 'DEFAULT_GROUP')

gn = OCNetwork(group_by_property="code", ignore_groups=True)
gn.add_results(res)
node = gn.graph.nodes.get('22')
self.assertEqual(node['group'], 'DEFAULT_GROUP')

def test_group_with_groupby(self):
path = {
"results": [
Expand Down

0 comments on commit c20cee8

Please sign in to comment.