Skip to content

Commit 95b0748

Browse files
committed
v0.9.0
1 parent f1ca6f7 commit 95b0748

File tree

3 files changed

+54
-16
lines changed

3 files changed

+54
-16
lines changed

CHANGELOG.md

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# CHANGELOG
22

3+
### **0.9.0** (June 11 2024)
4+
5+
> Support for aggregate functions like `COUNT`, `SUM`, `MIN`, `MAX`, and `AVG`.
6+
7+
#### Features
8+
9+
- Support for aggregate functions like `COUNT`, `SUM`, `MIN`, `MAX`, and `AVG` (#45, thanks @jackboyla!)
10+
- Logical `OR` support in relationship matches (#44, thanks @jackboyla!)
11+
12+
#### Testing
13+
14+
- Combine tests for digraphs and multidigraphs (#43, thanks @jackboyla!)
15+
316
### **0.8.0** (May 14 2024)
417

518
> Support for MultiDiGraphs.

grandcypher/__init__.py

+40-15
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@
161161
start="start",
162162
)
163163

164-
__version__ = "0.8.0"
164+
__version__ = "0.9.0"
165165

166166

167167
_ALPHABET = string.ascii_lowercase + string.digits
@@ -235,8 +235,8 @@ def _is_edge_attr_match(
235235
motif_edges = _aggregate_edge_labels(motif_edges)
236236
host_edges = _aggregate_edge_labels(host_edges)
237237

238-
motif_types = motif_edges.get('__labels__', set())
239-
host_types = host_edges.get('__labels__', set())
238+
motif_types = motif_edges.get("__labels__", set())
239+
host_types = host_edges.get("__labels__", set())
240240

241241
if motif_types and not motif_types.intersection(host_types):
242242
return False
@@ -246,7 +246,7 @@ def _is_edge_attr_match(
246246
continue
247247
if host_edges.get(attr) != val:
248248
return False
249-
249+
250250
return True
251251

252252

@@ -271,6 +271,7 @@ def _aggregate_edge_labels(edges: Dict) -> Dict:
271271
aggregated[edge_id] = attrs
272272
return aggregated
273273

274+
274275
def _get_entity_from_host(
275276
host: Union[nx.DiGraph, nx.MultiDiGraph], entity_name, entity_attribute=None
276277
):
@@ -288,7 +289,7 @@ def _get_entity_from_host(
288289
edge_data = host.get_edge_data(*entity_name)
289290
if not edge_data:
290291
return None # print(f"Nothing found for {entity_name} {entity_attribute}")
291-
292+
292293
if entity_attribute:
293294
# looking for edge attribute:
294295
if isinstance(host, nx.MultiDiGraph):
@@ -491,15 +492,16 @@ def _lookup(self, data_paths: List[str], offset_limit) -> Dict[str, List]:
491492
for r in ret:
492493
r_attr = {}
493494
for i, v in r.items():
494-
r_attr[(i, list(v.get('__labels__'))[0])] = v.get(entity_attribute, None)
495+
r_attr[(i, list(v.get("__labels__"))[0])] = v.get(
496+
entity_attribute, None
497+
)
495498
# eg, [{(0, 'paid'): 70, (1, 'paid'): 90}, {(0, 'paid'): 400, (1, 'friend'): None, (2, 'paid'): 650}]
496499
ret_with_attr.append(r_attr)
497-
500+
498501
ret = ret_with_attr
499502

500503
result[data_path] = list(ret)[offset_limit]
501504

502-
503505
return result
504506

505507
def return_clause(self, clause):
@@ -519,7 +521,6 @@ def return_clause(self, clause):
519521
item = str(item.value)
520522
self._return_requests.append(item)
521523

522-
523524
def order_clause(self, order_clause):
524525
self._order_by = []
525526
for item in order_clause[0].children:
@@ -544,7 +545,6 @@ def skip_clause(self, skip):
544545
skip = int(skip[-1])
545546
self._skip = skip
546547

547-
548548
def aggregate(self, func, results, entity, group_keys):
549549
# Collect data based on group keys
550550
grouped_data = {}
@@ -558,12 +558,24 @@ def _collate_data(data, unique_labels, func):
558558
# for ["COUNT", "SUM", "AVG"], we treat None as 0
559559
if func in ["COUNT", "SUM", "AVG"]:
560560
collated_data = {
561-
label: [(v or 0) for rel in data for k, v in rel.items() if k[1] == label] for label in unique_labels
561+
label: [
562+
(v or 0)
563+
for rel in data
564+
for k, v in rel.items()
565+
if k[1] == label
566+
]
567+
for label in unique_labels
562568
}
563569
# for ["MAX", "MIN"], we treat None as non-existent
564570
elif func in ["MAX", "MIN"]:
565571
collated_data = {
566-
label: [v for rel in data for k, v in rel.items() if (k[1] == label and v is not None)] for label in unique_labels
572+
label: [
573+
v
574+
for rel in data
575+
for k, v in rel.items()
576+
if (k[1] == label and v is not None)
577+
]
578+
for label in unique_labels
567579
}
568580

569581
return collated_data
@@ -583,7 +595,14 @@ def _collate_data(data, unique_labels, func):
583595
elif func == "AVG":
584596
sum_data = {label: sum(data) for label, data in collated_data.items()}
585597
count_data = {label: len(data) for label, data in collated_data.items()}
586-
avg_data = {label: sum_data[label] / count_data[label] if count_data[label] > 0 else 0 for label in sum_data}
598+
avg_data = {
599+
label: (
600+
sum_data[label] / count_data[label]
601+
if count_data[label] > 0
602+
else 0
603+
)
604+
for label in sum_data
605+
}
587606
aggregate_results[group] = avg_data
588607
elif func == "MAX":
589608
max_data = {label: max(data) for label, data in collated_data.items()}
@@ -602,7 +621,11 @@ def returns(self, ignore_limit=False):
602621
offset_limit=slice(0, None),
603622
)
604623
if len(self._aggregate_functions) > 0:
605-
group_keys = [key for key in results.keys() if not any(key.endswith(func[1]) for func in self._aggregate_functions)]
624+
group_keys = [
625+
key
626+
for key in results.keys()
627+
if not any(key.endswith(func[1]) for func in self._aggregate_functions)
628+
]
606629

607630
aggregated_results = {}
608631
for func, entity in self._aggregate_functions:
@@ -865,7 +888,9 @@ def flatten_tokens(edge_tokens):
865888
flat_tokens = []
866889
for token in edge_tokens:
867890
if isinstance(token, Tree):
868-
flat_tokens.extend(flatten_tokens(token.children)) # Recursively flatten the tree
891+
flat_tokens.extend(
892+
flatten_tokens(token.children)
893+
) # Recursively flatten the tree
869894
else:
870895
flat_tokens.append(token)
871896
return flat_tokens

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
setuptools.setup(
77
name="grand-cypher",
8-
version="0.8.0",
8+
version="0.9.0",
99
author="Jordan Matelsky",
1010
author_email="opensource@matelsky.com",
1111
description="Query Grand graphs using Cypher",

0 commit comments

Comments
 (0)