1
- from typing import List , Optional , Union , cast
1
+ from typing import Dict , List , Optional , Union , cast
2
2
3
3
from forestadmin .agent_toolkit .utils .context import User
4
4
from forestadmin .datasource_toolkit .decorators .collection_decorator import CollectionDecorator
@@ -18,7 +18,7 @@ async def list(self, caller: User, filter_: PaginatedFilter, projection: Project
18
18
refined_filter = cast (PaginatedFilter , await self ._refine_filter (caller , filter_ ))
19
19
ret = await self .child_collection .list (caller , refined_filter , simplified_projection )
20
20
21
- return self ._apply_joins_on_records (projection , simplified_projection , ret )
21
+ return self ._apply_joins_on_simplified_records (projection , simplified_projection , ret )
22
22
23
23
async def _refine_filter (
24
24
self , caller : User , _filter : Union [Filter , PaginatedFilter , None ]
@@ -29,11 +29,11 @@ async def _refine_filter(
29
29
_filter .condition_tree = _filter .condition_tree .replace (
30
30
lambda leaf : (
31
31
ConditionTreeLeaf (
32
- self ._get_fk_field_for_projection (leaf .field ),
32
+ self ._get_fk_field_for_many_to_one_projection (leaf .field ),
33
33
leaf .operator ,
34
34
leaf .value ,
35
35
)
36
- if self ._is_useless_join (leaf .field .split (":" )[0 ], _filter .condition_tree .projection )
36
+ if self ._is_useless_join_for_projection (leaf .field .split (":" )[0 ], _filter .condition_tree .projection )
37
37
else leaf
38
38
)
39
39
)
@@ -43,36 +43,25 @@ async def _refine_filter(
43
43
async def aggregate (
44
44
self , caller : User , filter_ : Union [Filter , None ], aggregation : Aggregation , limit : Optional [int ] = None
45
45
) -> List [AggregateResult ]:
46
- replaced = {}
46
+ replaced = {} # new_name -> old_name; for a simpler reconciliation
47
47
48
48
def replacer (field_name : str ) -> str :
49
- if self ._is_useless_join (field_name .split (":" )[0 ], aggregation .projection ):
50
- new_field_name = self ._get_fk_field_for_projection (field_name )
49
+ if self ._is_useless_join_for_projection (field_name .split (":" )[0 ], aggregation .projection ):
50
+ new_field_name = self ._get_fk_field_for_many_to_one_projection (field_name )
51
51
replaced [new_field_name ] = field_name
52
52
return new_field_name
53
- else :
54
- return field_name
53
+ return field_name
55
54
56
55
new_aggregation = aggregation .replace_fields (replacer )
57
56
58
- aggregate_result = await self .child_collection .aggregate (
57
+ aggregate_results = await self .child_collection .aggregate (
59
58
caller , cast (Filter , await self ._refine_filter (caller , filter_ )), new_aggregation , limit
60
59
)
61
60
if aggregation == new_aggregation :
62
- return aggregate_result
61
+ return aggregate_results
62
+ return self ._replace_fields_in_aggregate_group (aggregate_results , replaced )
63
63
64
- for result in aggregate_result :
65
- group = {}
66
- for field , value in result ["group" ].items ():
67
- if field in replaced :
68
- group [replaced [field ]] = value
69
- else :
70
- group [field ] = value
71
- result ["group" ] = group
72
-
73
- return aggregate_result
74
-
75
- def _is_useless_join (self , relation : str , projection : Projection ) -> bool :
64
+ def _is_useless_join_for_projection (self , relation : str , projection : Projection ) -> bool :
76
65
relation_schema = self .schema ["fields" ][relation ]
77
66
sub_projections = projection .relations [relation ]
78
67
@@ -82,7 +71,7 @@ def _is_useless_join(self, relation: str, projection: Projection) -> bool:
82
71
and sub_projections [0 ] == relation_schema ["foreign_key_target" ]
83
72
)
84
73
85
- def _get_fk_field_for_projection (self , projection : str ) -> str :
74
+ def _get_fk_field_for_many_to_one_projection (self , projection : str ) -> str :
86
75
relation_name = projection .split (":" )[0 ]
87
76
relation_schema = cast (ManyToOne , self .schema ["fields" ][relation_name ])
88
77
@@ -91,18 +80,18 @@ def _get_fk_field_for_projection(self, projection: str) -> str:
91
80
def _get_projection_without_useless_joins (self , projection : Projection ) -> Projection :
92
81
returned_projection = Projection (* projection )
93
82
for relation , relation_projections in projection .relations .items ():
94
- if self ._is_useless_join (relation , projection ):
83
+ if self ._is_useless_join_for_projection (relation , projection ):
95
84
# remove foreign key target from projection
96
85
returned_projection .remove (f"{ relation } :{ relation_projections [0 ]} " )
97
86
98
87
# add foreign keys to projection
99
- fk_field = self ._get_fk_field_for_projection (f"{ relation } :{ relation_projections [0 ]} " )
88
+ fk_field = self ._get_fk_field_for_many_to_one_projection (f"{ relation } :{ relation_projections [0 ]} " )
100
89
if fk_field not in returned_projection :
101
90
returned_projection .append (fk_field )
102
91
103
92
return returned_projection
104
93
105
- def _apply_joins_on_records (
94
+ def _apply_joins_on_simplified_records (
106
95
self , initial_projection : Projection , requested_projection : Projection , records : List [RecordsDataAlias ]
107
96
) -> List [RecordsDataAlias ]:
108
97
if requested_projection == initial_projection :
@@ -117,11 +106,27 @@ def _apply_joins_on_records(
117
106
relation_schema = self .schema ["fields" ][relation ]
118
107
119
108
if is_many_to_one (relation_schema ):
120
- fk_value = record [self ._get_fk_field_for_projection (f"{ relation } :{ relation_projections [0 ]} " )]
109
+ fk_value = record [
110
+ self ._get_fk_field_for_many_to_one_projection (f"{ relation } :{ relation_projections [0 ]} " )
111
+ ]
121
112
record [relation ] = {relation_projections [0 ]: fk_value } if fk_value else None
122
113
123
114
# remove foreign keys
124
115
for projection in projections_to_rm :
125
116
del record [projection ]
126
117
127
118
return records
119
+
120
+ def _replace_fields_in_aggregate_group (
121
+ self , aggregate_results : List [AggregateResult ], field_to_replace : Dict [str , str ]
122
+ ) -> List [AggregateResult ]:
123
+ for aggregate_result in aggregate_results :
124
+ group = {}
125
+ for field , value in aggregate_result ["group" ].items ():
126
+ if field in field_to_replace :
127
+ group [field_to_replace [field ]] = value
128
+ else :
129
+ group [field ] = value
130
+ aggregate_result ["group" ] = group
131
+
132
+ return aggregate_results
0 commit comments