Skip to content

Commit

Permalink
Merge pull request #3 from mahdihaghverdi/remove-to-dict
Browse files Browse the repository at this point in the history
Remove `to_dict`
  • Loading branch information
seyed-dev authored Oct 27, 2023
2 parents 12df6a9 + 97b77d8 commit 6438131
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 29 deletions.
33 changes: 22 additions & 11 deletions aggify/aggify.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def __getitem__(self, index):
def filter(self, arg=None, **kwargs):
if arg:
if isinstance(arg, Q):
self.pipelines.append(arg.to_dict())
self.pipelines.append(dict(arg))
else:
raise ValueError(f"Invalid Q object")
else:
Expand Down Expand Up @@ -277,23 +277,23 @@ class Q:
def __init__(self, **conditions):
self.conditions = Aggify(None).match(matches=conditions.items()).get('$match')

def to_dict(self):
return {"$match": self.conditions}
def __iter__(self):
yield '$match', self.conditions

def __or__(self, other):
if self.conditions.get("$or", None):
self.conditions["$or"].append(other.to_dict()["$match"])
self.conditions["$or"].append(dict(other)["$match"])
combined_conditions = self.conditions
else:
combined_conditions = {"$or": [self.conditions, other.to_dict()["$match"]]}
combined_conditions = {"$or": [self.conditions, dict(other)["$match"]]}
return Q(**combined_conditions)

def __and__(self, other):
if self.conditions.get("$and", None):
self.conditions["$and"].append(other.to_dict()["$match"])
self.conditions["$and"].append(dict(other)["$match"])
combined_conditions = self.conditions
else:
combined_conditions = {"$and": [self.conditions, other.to_dict()["$match"]]}
combined_conditions = {"$and": [self.conditions, dict(other)["$match"]]}
return Q(**combined_conditions)

def __invert__(self):
Expand Down Expand Up @@ -382,11 +382,22 @@ def _map_condition(self, condition):
return self.OPERATOR_MAPPING[condition]
raise ValueError("Unsupported operator")

def to_dict(self):
return {
"$cond": {
def __iter__(self):
"""Iterator used by `dict` to create a dictionary from a `Cond` object
With this method we are now able to do this:
c = Cond(...)
dict_of_c = dict(c)
instead of c.to_dict()
Returns:
A tuple of '$cond' and its value
"""
yield (
"$cond", {
"if": {self.condition: [self.value1, self.value2]},
"then": self.then_value,
"else": self.else_value
}
}
)
2 changes: 1 addition & 1 deletion tests/test_aggify.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def test_order_by_multiple_fields(self):
# Test complex conditional expression in projection
def test_complex_conditional_expression_in_projection(self):
aggify = Aggify(BaseModel)
aggify.project(name=1, age=1, custom_field=Cond(F("age").to_dict(), '>', 30, "Adult", "Child").to_dict())
aggify.project(name=1, age=1, custom_field=dict(Cond(F("age").to_dict(), '>', 30, "Adult", "Child")))
assert len(aggify.pipelines) == 1
assert "custom_field" in aggify.pipelines[0]["$project"]
assert aggify.pipelines[0]["$project"]["custom_field"]["$cond"]["if"] == {"$gt": ["$age", 30]}
Expand Down
18 changes: 9 additions & 9 deletions tests/test_cond.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,40 @@ class TestCond:
# Test condition with greater than operator
def test_greater_than_operator(self):
cond = Cond(25, '>', 20, 'High', 'Low')
assert cond.to_dict() == {"$cond": {"if": {"$gt": [25, 20]}, "then": "High", "else": "Low"}}
assert dict(cond) == {"$cond": {"if": {"$gt": [25, 20]}, "then": "High", "else": "Low"}}

# Test condition with less than operator
def test_less_than_operator(self):
cond = Cond(15, '<', 20, 'Low', 'High')
assert cond.to_dict() == {"$cond": {"if": {"$lt": [15, 20]}, "then": "Low", "else": "High"}}
assert dict(cond) == {"$cond": {"if": {"$lt": [15, 20]}, "then": "Low", "else": "High"}}

# Test condition with equal to operator
def test_equal_to_operator(self):
cond = Cond(30, '==', 30, 'Equal', 'Not Equal')
assert cond.to_dict() == {"$cond": {"if": {"$eq": [30, 30]}, "then": "Equal", "else": "Not Equal"}}
assert dict(cond) == {"$cond": {"if": {"$eq": [30, 30]}, "then": "Equal", "else": "Not Equal"}}

# Test condition with not equal to operator
def test_not_equal_to_operator(self):
cond = Cond(40, '!=', 35, 'Not Equal', 'Equal')
assert cond.to_dict() == {"$cond": {"if": {"$ne": [40, 35]}, "then": "Not Equal", "else": "Equal"}}
assert dict(cond) == {"$cond": {"if": {"$ne": [40, 35]}, "then": "Not Equal", "else": "Equal"}}

# Test condition with greater than or equal to operator
def test_greater_than_or_equal_to_operator(self):
cond = Cond(20, '>=', 20, 'Greater or Equal', 'Less')
assert cond.to_dict() == {"$cond": {"if": {"$gte": [20, 20]}, "then": "Greater or Equal", "else": "Less"}}
assert dict(cond) == {"$cond": {"if": {"$gte": [20, 20]}, "then": "Greater or Equal", "else": "Less"}}

# Test condition with less than or equal to operator
def test_less_than_or_equal_to_operator(self):
cond = Cond(18, '<=', 20, 'Less or Equal', 'Greater')
assert cond.to_dict() == {"$cond": {"if": {"$lte": [18, 20]}, "then": "Less or Equal", "else": "Greater"}}
assert dict(cond) == {"$cond": {"if": {"$lte": [18, 20]}, "then": "Less or Equal", "else": "Greater"}}

# Test condition with complex expressions
def test_complex_expression(self):
cond = Cond(15, '>', 10, Cond(20, '<', 25, 'Within Range', 'Out of Range').to_dict(), 'Invalid')
assert cond.to_dict() == {"$cond": {"if": {"$gt": [15, 10]}, "then": {"$cond": {"if": {"$lt": [20, 25]}, "then": "Within Range", "else": "Out of Range"}}, "else": "Invalid"}}
cond = Cond(15, '>', 10, dict(Cond(20, '<', 25, 'Within Range', 'Out of Range')), 'Invalid')
assert dict(cond) == {"$cond": {"if": {"$gt": [15, 10]}, "then": {"$cond": {"if": {"$lt": [20, 25]}, "then": "Within Range", "else": "Out of Range"}}, "else": "Invalid"}}

# Test invalid operator
def test_invalid_operator(self):
with pytest.raises(ValueError):
cond = Cond(25, 'invalid_operator', 20, 'High', 'Low')
cond.to_dict()
dict(cond)
16 changes: 8 additions & 8 deletions tests/test_q.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,29 @@ def test_or_operator_with_multiple_conditions(self):
q1 = Q(name="John")
q2 = Q(name="Alice")
q_combined = q1 | q2
assert q_combined.to_dict() == {
"$match": {"$or": [q1.to_dict()["$match"], q2.to_dict()["$match"]]}}
assert dict(q_combined) == {
"$match": {"$or": [dict(q1)["$match"], dict(q2)["$match"]]}}

def test_or_operator_with_multiple_conditions_more_than_rwo(self):
q1 = Q(name="John")
q2 = Q(name="Alice")
q3 = Q(name="Bob")
q_combined = q1 | q2 | q3
assert q_combined.to_dict() == {
"$match": {"$or": [q1.to_dict()["$match"], q2.to_dict()["$match"], q3.to_dict()["$match"]]}}
assert dict(q_combined) == {
"$match": {"$or": [dict(q1)["$match"], dict(q2)["$match"], dict(q3)["$match"]]}}

# Test combining NOT operators with AND
def test_combine_not_operators_with_and(self):
q1 = Q(name="John")
q2 = Q(age__lt=30)
q_combined = ~q1 & ~q2
assert q_combined.to_dict() == {
"$match": {"$and": [{"$not": [q1.to_dict()["$match"]]}, {"$not": [q2.to_dict()["$match"]]}]}}
assert dict(q_combined) == {
"$match": {"$and": [{"$not": [dict(q1)["$match"]]}, {"$not": [dict(q2)["$match"]]}]}}

# Test combining NOT operators with OR
def test_combine_not_operators_with_or(self):
q1 = Q(name="John")
q2 = Q(age__lt=30)
q_combined = ~q1 | ~q2 # Changed | to combine OR
assert q_combined.to_dict() == {
"$match": {"$or": [{"$not": [q1.to_dict()["$match"]]}, {"$not": [q2.to_dict()["$match"]]}]}}
assert dict(q_combined) == {
"$match": {"$or": [{"$not": [dict(q1)["$match"]]}, {"$not": [dict(q2)["$match"]]}]}}

0 comments on commit 6438131

Please sign in to comment.