Skip to content

Commit e64b807

Browse files
committed
add the addFields feature
1 parent 59e9c6c commit e64b807

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

README.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pip install aggify
4444
Here's a code snippet that demonstrates how to use Aggify to construct a MongoDB aggregation pipeline:
4545

4646
```python
47-
from aggify import Aggify, Q
47+
from aggify import Aggify, Q, F
4848
from mongoengine import Document, fields
4949
from pprint import pprint
5050

@@ -131,6 +131,19 @@ pprint(
131131
# output:
132132
# [{'$match': {'caption': 'hello'}}, {'$sort': {'_id': -1}}]
133133

134+
pprint(
135+
query.addFields({
136+
"new_field_1": "some_string",
137+
"new_field_2": F("existing_field") + 10,
138+
"new_field_3": F("field_a") * F("field_b")}
139+
).pipelines
140+
)
141+
# output :
142+
# [{'$addFields': {'new_field_1': {'$literal': 'some_string'},
143+
# 'new_field_2': {'$add': ['$existing_field', 10]},
144+
# 'new_field_3': {'$multiply': ['$field_a', '$field_b']}}}]
145+
146+
134147
```
135148

136149
In the sample usage above, you can see how Aggify simplifies the construction of MongoDB aggregation pipelines by allowing you to chain filters, projections, and other operations to build complex queries. The pprint(query.pipelines) line demonstrates how you can inspect the generated aggregation pipeline for debugging or analysis.

aggify/aggify.py

+23
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,29 @@ def raw(self, raw_query):
263263
self.pipelines.append(raw_query)
264264
return self
265265

266+
def addFields(self, fields): # noqa
267+
"""
268+
Generates a MongoDB addFields pipeline stage.
269+
270+
Args:
271+
fields: A dictionary of field expressions and values.
272+
273+
Returns:
274+
A MongoDB addFields pipeline stage.
275+
"""
276+
add_fields_stage = {"$addFields": {}}
277+
278+
for field, expression in fields.items():
279+
if isinstance(expression, str):
280+
add_fields_stage["$addFields"][field] = {"$literal": expression}
281+
elif isinstance(expression, F):
282+
add_fields_stage["$addFields"][field] = expression.to_dict()
283+
else:
284+
raise ValueError("Invalid field expression")
285+
286+
self.pipelines.append(add_fields_stage)
287+
return self
288+
266289
def aggregate(self):
267290
"""
268291
Returns the aggregated results.

tests/test_aggify.py

+33
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,36 @@ def test_filter_with_not_operator(self):
131131
aggify.filter(~Q(name="John"))
132132
assert len(aggify.pipelines) == 1
133133
assert aggify.pipelines[0]["$match"]["$not"][0]["name"] == "John"
134+
135+
def test_add_fields_string_literal(self):
136+
aggify = Aggify(BaseModel)
137+
fields = {
138+
"new_field_1": "some_string",
139+
"new_field_2": "another_string"
140+
}
141+
add_fields_stage = aggify.addFields(fields)
142+
143+
expected_stage = {
144+
"$addFields": {
145+
"new_field_1": {"$literal": "some_string"},
146+
"new_field_2": {"$literal": "another_string"}
147+
}
148+
}
149+
150+
assert add_fields_stage.pipelines[0] == expected_stage
151+
152+
def test_add_fields_with_f_expression(self):
153+
aggify = Aggify(BaseModel)
154+
fields = {
155+
"new_field_1": F("existing_field") + 10,
156+
"new_field_2": F("field_a") * F("field_b")
157+
}
158+
add_fields_stage = aggify.addFields(fields)
159+
160+
expected_stage = {
161+
"$addFields": {
162+
"new_field_1": {"$add": ["$existing_field", 10]},
163+
"new_field_2": {"$multiply": ["$field_a", "$field_b"]}
164+
}
165+
}
166+
assert add_fields_stage.pipelines[0] == expected_stage

0 commit comments

Comments
 (0)