File: logical.py

package info (click to toggle)
python-beanie 2.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,484 kB
  • sloc: python: 14,427; makefile: 6; sh: 6
file content (166 lines) | stat: -rw-r--r-- 3,687 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
from abc import ABC
from typing import Any, Dict, Mapping, Union

from beanie.odm.operators.find import BaseFindOperator


class BaseFindLogicalOperator(BaseFindOperator, ABC): ...


class LogicalOperatorForListOfExpressions(BaseFindLogicalOperator):
    # todo: handle query return typing
    operator: str = ""

    def __init__(
        self,
        *expressions: Union[
            BaseFindOperator, Dict[str, Any], Mapping[str, Any], bool
        ],
    ):
        self.expressions = list(expressions)

    @property
    def query(self):
        if not self.expressions:
            raise AttributeError("At least one expression must be provided")
        if len(self.expressions) == 1:
            return self.expressions[0]
        return {self.operator: self.expressions}


class Or(LogicalOperatorForListOfExpressions):
    """
    `$or` query operator

    Example:

    ```python
    class Product(Document):
        price: float
        category: str

    Or(Product.price<10, Product.category=="Sweets")
    ```

    Will return query object like

    ```python
    {"$or": [{"price": {"$lt": 10}}, {"category": "Sweets"}]}
    ```

    MongoDB doc:
    <https://docs.mongodb.com/manual/reference/operator/query/or/>
    """

    operator = "$or"


class And(LogicalOperatorForListOfExpressions):
    """
    `$and` query operator

    Example:

    ```python
    class Product(Document):
        price: float
        category: str

    And(Product.price<10, Product.category=="Sweets")
    ```

    Will return query object like

    ```python
    {"$and": [{"price": {"$lt": 10}}, {"category": "Sweets"}]}
    ```

    MongoDB doc:
    <https://docs.mongodb.com/manual/reference/operator/query/and/>
    """

    operator = "$and"


class Nor(BaseFindLogicalOperator):
    """
    `$nor` query operator

    Example:

    ```python
    class Product(Document):
        price: float
        category: str

    Nor(Product.price<10, Product.category=="Sweets")
    ```

    Will return query object like

    ```python
    {"$nor": [{"price": {"$lt": 10}}, {"category": "Sweets"}]}
    ```

    MongoDB doc:
    <https://docs.mongodb.com/manual/reference/operator/query/nor/>
    """

    def __init__(
        self,
        *expressions: Union[
            BaseFindOperator, Dict[str, Any], Mapping[str, Any], bool
        ],
    ):
        self.expressions = list(expressions)

    @property
    def query(self):
        return {"$nor": self.expressions}


class Not(BaseFindLogicalOperator):
    """
    `$not` query operator

    Example:

    ```python
    class Product(Document):
        price: float
        category: str

    Not(Product.price<10)
    ```

    Will return query object like

    ```python
    {"$not": {"price": {"$lt": 10}}}
    ```

    MongoDB doc:
    <https://docs.mongodb.com/manual/reference/operator/query/not/>
    """

    def __init__(self, expression: Mapping[str, Any]):
        self.expression = expression

    @property
    def query(self):
        if len(self.expression) == 1:
            expression_key = list(self.expression.keys())[0]
            if expression_key.startswith("$"):
                raise AttributeError(
                    "Not operator can not be used with operators"
                )
            value = self.expression[expression_key]
            if isinstance(value, dict):
                internal_key = list(value.keys())[0]
                if internal_key.startswith("$"):
                    return {expression_key: {"$not": value}}

            return {expression_key: {"$not": {"$eq": value}}}
        raise AttributeError(
            "Not operator can only be used with one expression"
        )