-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathquery_engine.py
More file actions
58 lines (51 loc) · 1.84 KB
/
query_engine.py
File metadata and controls
58 lines (51 loc) · 1.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class QueryEngine:
VALID_KEYS = ['filter', 'groupedBy']
def __init__(self, interface):
self.interface = interface
self.index = self.interface.index
def is_valid(self, q):
for key in q:
if key not in self.VALID_KEYS:
return False
return True
def query(self, q):
"""
1. Apply filters to queryset
2. Apply groupby to queryset
:param q: query as a dict
:return: {totolCounts: <int>, groups: {<str>: <int>}}
"""
if not self.is_valid(q):
raise Exception('Invalid Query')
result = {'totalCount': 0}
queryset = self.process_filters(q)
result['totalCount'] = len(queryset)
if groups := q.get('groupedBy'):
result['groups'] = self.process_groupby(queryset, groups)
return result
def process_filters(self, q):
filters = q.get('filter', {})
queryset = None
for k, v in filters.items():
if not queryset:
queryset = self.index.filter(k, v)
else:
queryset = queryset.intersection(self.index.filter(k, v))
return queryset
def process_groupby(self, queryset, groups):
return self.group_attr(queryset, groups)
def group_attr(self, queryset, groups, pos=0):
res = {}
if pos == len(groups):
return len(queryset)
group = groups[pos]
inner_group = self.explode_set_by_group(queryset, group)
for k, v in inner_group.items():
res[k] = self.group_attr(v, groups, pos+1)
return res
def explode_set_by_group(self, s, group):
from collections import defaultdict
d = defaultdict(set)
for item in s:
d[getattr(self.interface.inventory.inventory[item], group, None)].add(item)
return d