Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 38 additions & 13 deletions mpt_api_client/rql/query_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,18 @@ class RQLQuery: # noqa: WPS214
rql = RQLQuery().nested.field.eq('value')
"""

AND = "and" # noqa: WPS115
OR = "or" # noqa: WPS115
EXPRESSION = "expr" # noqa: WPS115
OP_AND = "and" # noqa: WPS115
OP_OR = "or" # noqa: WPS115
OP_ANY = "any" # noqa: WPS115
OP_ALL = "all" # noqa: WPS115
OP_EXPRESSION = "expr" # noqa: WPS115

def __init__( # noqa: WPS211
self,
namespace_: str | None = None, # noqa: WPS120
**kwargs: QueryValue,
) -> None:
self.op: str = self.EXPRESSION
self.op: str = self.OP_EXPRESSION
self.children: list[RQLQuery] = []
self.negated: bool = False
self.expr: str | None = None
Expand All @@ -160,10 +162,10 @@ def __init__( # noqa: WPS211
if namespace_:
self.n(namespace_)
if len(kwargs) == 1:
self.op = self.EXPRESSION
self.op = self.OP_EXPRESSION
self.expr = parse_kwargs(kwargs)[0]
if len(kwargs) > 1:
self.op = self.AND
self.op = self.OP_AND
for token in parse_kwargs(kwargs):
self.children.append(self.new(expr=token))

Expand All @@ -180,14 +182,14 @@ def new(
if isinstance(children, set):
children = list(children)
query = cls()
query.op = op or cls.EXPRESSION
query.op = op or cls.OP_EXPRESSION
query.children = children or []
query.negated = negated
query.expr = expr
return query

def __len__(self) -> int:
if self.op == self.EXPRESSION:
if self.op == self.OP_EXPRESSION:
if self.expr:
return 1
return 0
Expand Down Expand Up @@ -220,23 +222,23 @@ def __hash__(self) -> int:

@override
def __repr__(self) -> str:
if self.op == self.EXPRESSION:
if self.op == self.OP_EXPRESSION:
return f"<RQLQuery({self.op}) {self.expr}>"
return f"<RQLQuery({self.op})>"

def __and__(self, other: object) -> Self:
if not isinstance(other, type(self)):
return NotImplemented
return self._join(other, self.AND)
return self._join(other, self.OP_AND)

def __or__(self, other: object) -> Self:
if not isinstance(other, type(self)):
return NotImplemented
return self._join(other, self.OR)
return self._join(other, self.OP_OR)

def __invert__(self) -> Self:
inverted_query = self.new(
op=self.AND,
op=self.OP_AND,
expr=self.expr,
negated=True,
)
Expand All @@ -250,6 +252,29 @@ def __getattr__(self, name: str) -> Self:
def __str__(self) -> str:
return self._to_string(self)

def any(self) -> Self:
"""Any nested objects have to match the filter condition.

Returns:
RQLQuery: RQLQuery with new condition

Examples:
RQLQuery(saleDetails__orderQty__gt=11).any()
will result: any(saleDetails,orderQty=11)
"""
return self.new(op=self.OP_ANY, children=[self])

def all(self) -> Self:
"""All nested objects have to match the filter condition.

Returns:
RQLQuery: RQLQuery with new condition

Example:
RQLQuery(saleDetails__orderQty__gt=1).all()
"""
return self.new(op=self.OP_ALL, children=[self])

def n(self, name: str) -> Self: # noqa: WPS111
"""Set the current field for this `RQLQuery` object.

Expand Down Expand Up @@ -485,7 +510,7 @@ def _join(self, other: "RQLQuery", op: str) -> Self:
def _append(self, query: "RQLQuery") -> "RQLQuery" | Self:
if query in self.children:
return query
single_operation = len(query) == 1 and query.op != self.EXPRESSION
single_operation = len(query) == 1 and query.op != self.OP_EXPRESSION
if (query.op == self.op or single_operation) and not query.negated:
self.children.extend(query.children)
return self
Expand Down
20 changes: 10 additions & 10 deletions tests/rql/query_builder/test_create_rql.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
def test_create():
query = RQLQuery()

assert query.op == RQLQuery.EXPRESSION
assert query.op == RQLQuery.OP_EXPRESSION
assert query.children == []
assert query.negated is False

Expand All @@ -14,14 +14,14 @@ def test_create_with_field():

query.eq("value")

assert query.op == RQLQuery.EXPRESSION
assert query.op == RQLQuery.OP_EXPRESSION
assert str(query) == "eq(field,value)"


def test_create_single_kwarg():
query = RQLQuery(id="ID")

assert query.op == RQLQuery.EXPRESSION
assert query.op == RQLQuery.OP_EXPRESSION
assert str(query) == "eq(id,ID)"
assert query.children == []
assert query.negated is False
Expand All @@ -30,24 +30,24 @@ def test_create_single_kwarg():
def test_create_multiple_kwargs(): # noqa: WPS218
query = RQLQuery(id="ID", status__in=("a", "b"), ok=True)

assert query.op == RQLQuery.AND
assert query.op == RQLQuery.OP_AND
assert str(query) == "and(eq(id,ID),in(status,(a,b)),eq(ok,true))"
assert len(query.children) == 3
assert query.children[0].op == RQLQuery.EXPRESSION
assert query.children[0].op == RQLQuery.OP_EXPRESSION
assert query.children[0].children == []
assert str(query.children[0]) == "eq(id,ID)"
assert query.children[1].op == RQLQuery.EXPRESSION
assert query.children[1].op == RQLQuery.OP_EXPRESSION
assert query.children[1].children == []
assert str(query.children[1]) == "in(status,(a,b))"
assert query.children[2].op == RQLQuery.EXPRESSION
assert query.children[2].op == RQLQuery.OP_EXPRESSION
assert query.children[2].children == []
assert str(query.children[2]) == "eq(ok,true)"


def test_new_empty():
query = RQLQuery.new()

assert query.op == RQLQuery.EXPRESSION
assert query.op == RQLQuery.OP_EXPRESSION
assert query.children == []
assert query.negated is False

Expand All @@ -57,7 +57,7 @@ def test_new_with_parameters():
status_not_done = RQLQuery.new("status=done", negated=True)

query = RQLQuery.new(
op=RQLQuery.AND,
op=RQLQuery.OP_AND,
children=[project_rql, status_not_done],
)

Expand All @@ -69,7 +69,7 @@ def test_new_with_set():
status_not_done = RQLQuery.new("status=done", negated=True)

query = RQLQuery.new(
op=RQLQuery.AND,
op=RQLQuery.OP_AND,
children={project_rql, status_not_done},
)

Expand Down
4 changes: 2 additions & 2 deletions tests/rql/query_builder/test_multiple_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def test_and_or(): # noqa: WPS218 WPS473

r5 = r1 & r2 & (r3 | r4)

assert r5.op == RQLQuery.AND
assert r5.op == RQLQuery.OP_AND
assert str(r5) == "and(eq(id,ID),eq(field,value),or(eq(other,value2),in(inop,(a,b))))" # noqa: WPS204

r5 = r1 & r2 | r3
Expand Down Expand Up @@ -43,6 +43,6 @@ def test_and_merge(): # noqa: WPS210

and3 = and1 & and2

assert and3.op == RQLQuery.AND
assert and3.op == RQLQuery.OP_AND
assert len(and3.children) == 4
assert [r1, r2, r3, r4] == and3.children
14 changes: 14 additions & 0 deletions tests/rql/query_builder/test_rql_all_any.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from mpt_api_client.rql import RQLQuery


def test_all():
query = RQLQuery(saleDetails__orderQty__gt=1).all()

rql = str(query)
assert rql == "all(gt(saleDetails.orderQty,1))"


def test_any():
query = RQLQuery(saleDetails__orderQty__gt=1).any()
rql = str(query)
assert rql == "any(gt(saleDetails.orderQty,1))"
4 changes: 2 additions & 2 deletions tests/rql/query_builder/test_rql_and.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_and_different():

assert r3 != r1
assert r3 != r2
assert r3.op == RQLQuery.AND
assert r3.op == RQLQuery.OP_AND
assert r1 in r3.children
assert r2 in r3.children

Expand All @@ -60,5 +60,5 @@ def test_and_triple():
r3 = r1 & r2 & r2

assert len(r3) == 2
assert r3.op == RQLQuery.AND
assert r3.op == RQLQuery.OP_AND
assert [r1, r2] == r3.children
6 changes: 3 additions & 3 deletions tests/rql/query_builder/test_rql_or.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def test_or_not_equals():
assert r3 != r1
assert r3 != r2

assert r3.op == RQLQuery.OR
assert r3.op == RQLQuery.OP_OR
assert r1 in r3.children
assert r2 in r3.children

Expand All @@ -65,7 +65,7 @@ def test_or_merge(): # noqa: WPS210
or2 = r3 | r4
or3 = or1 | or2

assert or3.op == RQLQuery.OR
assert or3.op == RQLQuery.OP_OR
assert len(or3.children) == 4
assert [r1, r2, r3, r4] == or3.children

Expand All @@ -77,5 +77,5 @@ def test_or_merge_duplicates():
r3 = r1 | r2 | r2

assert len(r3) == 2
assert r3.op == RQLQuery.OR
assert r3.op == RQLQuery.OP_OR
assert [r1, r2] == r3.children