@@ -63,7 +63,48 @@ def __init__(self, base_model: Type[Document]):
63
63
64
64
@last_out_stage_check
65
65
def project (self , ** kwargs : QueryParams ) -> "Aggify" :
66
+ """
67
+ Adjusts the base model's fields based on the given keyword arguments.
68
+
69
+ Fields to be retained are set to 1 in kwargs.
70
+ Fields to be deleted are set to 0 in kwargs, except for _id which is controlled by the delete_id flag.
71
+
72
+ Args:
73
+ **kwargs: Fields to be retained or removed.
74
+ For example: {"field1": 1, "field2": 0}
75
+ _id field behavior: {"_id": 0} means delete _id.
76
+
77
+ Returns:
78
+ Aggify: Returns an instance of the Aggify class for potential method chaining.
79
+ """
80
+
81
+ # Extract fields to keep and check if _id should be deleted
82
+ to_keep_values = ["id" ]
83
+ delete_id = kwargs .get ("_id" ) == 0
84
+
85
+ # Add missing fields to the base model
86
+ for key , value in kwargs .items ():
87
+ if value == 1 :
88
+ to_keep_values .append (key )
89
+ elif key not in self .base_model ._fields and isinstance ( # noqa
90
+ kwargs [key ], str
91
+ ): # noqa
92
+ to_keep_values .append (key )
93
+ self .base_model ._fields [key ] = fields .IntField () # noqa
94
+
95
+ # Remove fields from the base model, except the ones in to_keep_values and possibly _id
96
+ keys_for_deletion = set (self .base_model ._fields .keys ()) - set ( # noqa
97
+ to_keep_values
98
+ ) # noqa
99
+ if delete_id :
100
+ keys_for_deletion .add ("id" )
101
+ for key in keys_for_deletion :
102
+ del self .base_model ._fields [key ] # noqa
103
+
104
+ # Append the projection stage to the pipelines
66
105
self .pipelines .append ({"$project" : kwargs })
106
+
107
+ # Return the instance for method chaining
67
108
return self
68
109
69
110
@last_out_stage_check
@@ -87,7 +128,7 @@ def raw(self, raw_query: dict) -> "Aggify":
87
128
return self
88
129
89
130
@last_out_stage_check
90
- def add_fields (self , ** fields ) -> "Aggify" : # noqa
131
+ def add_fields (self , ** _fields ) -> "Aggify" : # noqa
91
132
"""
92
133
Generates a MongoDB addFields pipeline stage.
93
134
@@ -99,7 +140,8 @@ def add_fields(self, **fields) -> "Aggify": # noqa
99
140
"""
100
141
add_fields_stage = {"$addFields" : {}}
101
142
102
- for field , expression in fields .items ():
143
+ for field , expression in _fields .items ():
144
+ field = field .replace ("__" , "." )
103
145
if isinstance (expression , str ):
104
146
add_fields_stage ["$addFields" ][field ] = {"$literal" : expression }
105
147
elif isinstance (expression , F ):
@@ -108,6 +150,8 @@ def add_fields(self, **fields) -> "Aggify": # noqa
108
150
add_fields_stage ["$addFields" ][field ] = dict (expression )
109
151
else :
110
152
raise AggifyValueError ([str , F ], type (expression ))
153
+ # TODO: Should be checked if new field is embedded, create embedded field.
154
+ self .base_model ._fields [field .replace ("$" , "" )] = fields .IntField () # noqa
111
155
112
156
self .pipelines .append (add_fields_stage )
113
157
return self
0 commit comments