Skip to content

Commit

Permalink
Add path groups (#37)
Browse files Browse the repository at this point in the history
* Add path groups #26, #35

* Remove unnecessary enumerate

* Update CHANGELOG and bump version in setup.py
  • Loading branch information
davidmezzetti authored Feb 15, 2024
1 parent 31d7f7d commit 31001b7
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 6 deletions.
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
# CHANGELOG

### **0.5.0** (Unreleased)
### **0.6.0** (February 15 2024)

> New path group operator
#### Features

- Support for path group operators (#37)

### **0.5.0** (February 13 2024)

> Lots of language support for new query operators.
Expand Down
30 changes: 26 additions & 4 deletions grandcypher/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
many_match_clause : (match_clause)+
match_clause : "match"i node_match (edge_match node_match)*
match_clause : "match"i path_clause? node_match (edge_match node_match)*
path_clause : CNAME EQUAL
where_clause : "where"i compound_condition
Expand Down Expand Up @@ -111,6 +113,7 @@
LEFT_ANGLE : "<"
RIGHT_ANGLE : ">"
EQUAL : "="
MIN_HOP : INT
MAX_HOP : INT
TYPE : CNAME
Expand Down Expand Up @@ -309,6 +312,7 @@ def _data_path_to_entity_name_attribute(data_path):
class _GrandCypherTransformer(Transformer):
def __init__(self, target_graph: nx.Graph, limit=None):
self._target_graph = target_graph
self._paths = []
self._where_condition: CONDITION = None
self._motif = nx.DiGraph()
self._matches = None
Expand All @@ -327,7 +331,7 @@ def _lookup(self, data_paths: List[str], offset_limit) -> Dict[str, List]:

for data_path in data_paths:
entity_name, _ = _data_path_to_entity_name_attribute(data_path)
if entity_name not in motif_nodes and entity_name not in self._return_edges:
if entity_name not in motif_nodes and entity_name not in self._return_edges and entity_name not in self._paths:
raise NotImplementedError(f"Unknown entity name: {data_path}")

result = {}
Expand All @@ -352,6 +356,20 @@ def _lookup(self, data_paths: List[str], offset_limit) -> Dict[str, List]:
for node in ret
)

elif entity_name in self._paths:
ret = []
for mapping, _ in true_matches:
path, nodes = [], list(mapping.values())
for x, node in enumerate(nodes):
# Edge
if x > 0:
path.append(self._target_graph.get_edge_data(nodes[x - 1], node))

# Node
path.append(node)

ret.append(path)

else:
mapping_u, mapping_v = self._return_edges[data_path]
# We are looking for an edge mapping in the target graph:
Expand Down Expand Up @@ -460,8 +478,7 @@ def _matches_iter(self, motif):

# Single match clause iterator
if iterators and len(iterators) == 1:
for x, match in enumerate(iterators[0]):
yield match
yield from iterators[0]

# Multi match clause, requires a cartesian join
else:
Expand Down Expand Up @@ -603,6 +620,8 @@ def match_clause(self, match_clause: Tuple):
u, ut, js = match_clause[0]
self._motif.add_node(u.value, __labels__=ut, **js)
return

match_clause = match_clause[1:] if not match_clause[0] else match_clause
for start in range(0, len(match_clause) - 2, 2):
((u, ut, ujs), (g, t, d, minh, maxh), (v, vt, vjs)) = match_clause[
start : start + 3
Expand Down Expand Up @@ -633,6 +652,9 @@ def match_clause(self, match_clause: Tuple):
self._motif.add_node(u, __labels__=ut, **ujs)
self._motif.add_node(v, __labels__=vt, **vjs)

def path_clause(self, path_clause: tuple):
self._paths.append(path_clause[0])

def where_clause(self, where_clause: tuple):
self._where_condition = where_clause[0]

Expand Down
20 changes: 20 additions & 0 deletions grandcypher/test_queries.py
Original file line number Diff line number Diff line change
Expand Up @@ -1157,3 +1157,23 @@ def test_nested_nots_in_statements(self):

res = GrandCypher(host).run(qry)
assert len(res["Instrument"]) == 1


class TestPath:
def test_path(self):
host = nx.DiGraph()
host.add_node("x", name="x")
host.add_node("y", name="y")
host.add_node("z", name="z")

host.add_edge("x", "y", foo="bar")
host.add_edge("y", "z",)

qry = """
MATCH P = ()-[r*2]->()
RETURN P
LIMIT 1
"""

res = GrandCypher(host).run(qry)
assert len(res["P"][0]) == 5
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="grand-cypher",
version="0.5.0",
version="0.6.0",
author="Jordan Matelsky",
author_email="opensource@matelsky.com",
description="Query Grand graphs using Cypher",
Expand Down

0 comments on commit 31001b7

Please sign in to comment.