mongo-aggro 0.1.0__py3-none-any.whl → 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mongo_aggro/__init__.py +400 -0
- mongo_aggro/accumulators.py +30 -12
- mongo_aggro/base.py +49 -9
- mongo_aggro/expressions/__init__.py +396 -0
- mongo_aggro/expressions/arithmetic.py +329 -0
- mongo_aggro/expressions/array.py +425 -0
- mongo_aggro/expressions/base.py +180 -0
- mongo_aggro/expressions/bitwise.py +84 -0
- mongo_aggro/expressions/comparison.py +161 -0
- mongo_aggro/expressions/conditional.py +117 -0
- mongo_aggro/expressions/date.py +665 -0
- mongo_aggro/expressions/encrypted.py +116 -0
- mongo_aggro/expressions/logical.py +72 -0
- mongo_aggro/expressions/object.py +122 -0
- mongo_aggro/expressions/set.py +150 -0
- mongo_aggro/expressions/size.py +48 -0
- mongo_aggro/expressions/string.py +365 -0
- mongo_aggro/expressions/trigonometry.py +283 -0
- mongo_aggro/expressions/type.py +205 -0
- mongo_aggro/expressions/variable.py +73 -0
- mongo_aggro/expressions/window.py +327 -0
- mongo_aggro/operators/__init__.py +65 -0
- mongo_aggro/operators/array.py +41 -0
- mongo_aggro/operators/base.py +15 -0
- mongo_aggro/operators/bitwise.py +81 -0
- mongo_aggro/operators/comparison.py +82 -0
- mongo_aggro/operators/element.py +32 -0
- mongo_aggro/operators/geo.py +171 -0
- mongo_aggro/operators/logical.py +111 -0
- mongo_aggro/operators/misc.py +102 -0
- mongo_aggro/operators/regex.py +25 -0
- mongo_aggro/stages/__init__.py +110 -0
- mongo_aggro/stages/array.py +69 -0
- mongo_aggro/stages/change.py +109 -0
- mongo_aggro/stages/core.py +170 -0
- mongo_aggro/stages/geo.py +93 -0
- mongo_aggro/stages/group.py +154 -0
- mongo_aggro/stages/join.py +221 -0
- mongo_aggro/stages/misc.py +45 -0
- mongo_aggro/stages/output.py +136 -0
- mongo_aggro/stages/search.py +315 -0
- mongo_aggro/stages/session.py +111 -0
- mongo_aggro/stages/stats.py +152 -0
- mongo_aggro/stages/transform.py +136 -0
- mongo_aggro/stages/window.py +139 -0
- mongo_aggro-0.2.2.dist-info/METADATA +193 -0
- mongo_aggro-0.2.2.dist-info/RECORD +49 -0
- {mongo_aggro-0.1.0.dist-info → mongo_aggro-0.2.2.dist-info}/WHEEL +1 -1
- mongo_aggro/operators.py +0 -247
- mongo_aggro/stages.py +0 -990
- mongo_aggro-0.1.0.dist-info/METADATA +0 -537
- mongo_aggro-0.1.0.dist-info/RECORD +0 -9
- {mongo_aggro-0.1.0.dist-info → mongo_aggro-0.2.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""Type conversion expression operators for MongoDB aggregation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import model_serializer
|
|
6
|
+
|
|
7
|
+
from mongo_aggro.base import serialize_value
|
|
8
|
+
from mongo_aggro.expressions.base import ExpressionBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ToStringExpr(ExpressionBase):
|
|
12
|
+
"""
|
|
13
|
+
$toString expression operator - converts value to string.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> ToStringExpr(input=F("numericId")).model_dump()
|
|
17
|
+
{"$toString": "$numericId"}
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
input: Any
|
|
21
|
+
|
|
22
|
+
@model_serializer
|
|
23
|
+
def serialize(self) -> dict[str, Any]:
|
|
24
|
+
"""Serialize to MongoDB $toString expression."""
|
|
25
|
+
return {"$toString": serialize_value(self.input)}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ToIntExpr(ExpressionBase):
|
|
29
|
+
"""
|
|
30
|
+
$toInt expression operator - converts value to integer.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> ToIntExpr(input=F("stringNum")).model_dump()
|
|
34
|
+
{"$toInt": "$stringNum"}
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
input: Any
|
|
38
|
+
|
|
39
|
+
@model_serializer
|
|
40
|
+
def serialize(self) -> dict[str, Any]:
|
|
41
|
+
"""Serialize to MongoDB $toInt expression."""
|
|
42
|
+
return {"$toInt": serialize_value(self.input)}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ToDoubleExpr(ExpressionBase):
|
|
46
|
+
"""
|
|
47
|
+
$toDouble expression operator - converts value to double.
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> ToDoubleExpr(input=F("intValue")).model_dump()
|
|
51
|
+
{"$toDouble": "$intValue"}
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
input: Any
|
|
55
|
+
|
|
56
|
+
@model_serializer
|
|
57
|
+
def serialize(self) -> dict[str, Any]:
|
|
58
|
+
"""Serialize to MongoDB $toDouble expression."""
|
|
59
|
+
return {"$toDouble": serialize_value(self.input)}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ToBoolExpr(ExpressionBase):
|
|
63
|
+
"""
|
|
64
|
+
$toBool expression operator - converts value to boolean.
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> ToBoolExpr(input=F("flag")).model_dump()
|
|
68
|
+
{"$toBool": "$flag"}
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
input: Any
|
|
72
|
+
|
|
73
|
+
@model_serializer
|
|
74
|
+
def serialize(self) -> dict[str, Any]:
|
|
75
|
+
"""Serialize to MongoDB $toBool expression."""
|
|
76
|
+
return {"$toBool": serialize_value(self.input)}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ToObjectIdExpr(ExpressionBase):
|
|
80
|
+
"""
|
|
81
|
+
$toObjectId expression operator - converts value to ObjectId.
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
>>> ToObjectIdExpr(input=F("idString")).model_dump()
|
|
85
|
+
{"$toObjectId": "$idString"}
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
input: Any
|
|
89
|
+
|
|
90
|
+
@model_serializer
|
|
91
|
+
def serialize(self) -> dict[str, Any]:
|
|
92
|
+
"""Serialize to MongoDB $toObjectId expression."""
|
|
93
|
+
return {"$toObjectId": serialize_value(self.input)}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ToLongExpr(ExpressionBase):
|
|
97
|
+
"""
|
|
98
|
+
$toLong expression operator - converts value to long integer.
|
|
99
|
+
|
|
100
|
+
Example:
|
|
101
|
+
>>> ToLongExpr(input=F("value")).model_dump()
|
|
102
|
+
{"$toLong": "$value"}
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
input: Any
|
|
106
|
+
|
|
107
|
+
@model_serializer
|
|
108
|
+
def serialize(self) -> dict[str, Any]:
|
|
109
|
+
"""Serialize to MongoDB $toLong expression."""
|
|
110
|
+
return {"$toLong": serialize_value(self.input)}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class ToDecimalExpr(ExpressionBase):
|
|
114
|
+
"""
|
|
115
|
+
$toDecimal expression operator - converts value to Decimal128.
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
>>> ToDecimalExpr(input=F("value")).model_dump()
|
|
119
|
+
{"$toDecimal": "$value"}
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
input: Any
|
|
123
|
+
|
|
124
|
+
@model_serializer
|
|
125
|
+
def serialize(self) -> dict[str, Any]:
|
|
126
|
+
"""Serialize to MongoDB $toDecimal expression."""
|
|
127
|
+
return {"$toDecimal": serialize_value(self.input)}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class ConvertExpr(ExpressionBase):
|
|
131
|
+
"""
|
|
132
|
+
$convert expression operator - converts value to specified type.
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
>>> ConvertExpr(input=F("value"), to="int").model_dump()
|
|
136
|
+
{"$convert": {"input": "$value", "to": "int"}}
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
input: Any
|
|
140
|
+
to: str
|
|
141
|
+
on_error: Any = None
|
|
142
|
+
on_null: Any = None
|
|
143
|
+
|
|
144
|
+
@model_serializer
|
|
145
|
+
def serialize(self) -> dict[str, Any]:
|
|
146
|
+
"""Serialize to MongoDB $convert expression."""
|
|
147
|
+
result: dict[str, Any] = {
|
|
148
|
+
"$convert": {
|
|
149
|
+
"input": serialize_value(self.input),
|
|
150
|
+
"to": self.to,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if self.on_error is not None:
|
|
154
|
+
result["$convert"]["onError"] = serialize_value(self.on_error)
|
|
155
|
+
if self.on_null is not None:
|
|
156
|
+
result["$convert"]["onNull"] = serialize_value(self.on_null)
|
|
157
|
+
return result
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class TypeExpr(ExpressionBase):
|
|
161
|
+
"""
|
|
162
|
+
$type expression operator - returns BSON type of a value.
|
|
163
|
+
|
|
164
|
+
Example:
|
|
165
|
+
>>> TypeExpr(input=F("field")).model_dump()
|
|
166
|
+
{"$type": "$field"}
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
input: Any
|
|
170
|
+
|
|
171
|
+
@model_serializer
|
|
172
|
+
def serialize(self) -> dict[str, Any]:
|
|
173
|
+
"""Serialize to MongoDB $type expression."""
|
|
174
|
+
return {"$type": serialize_value(self.input)}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class IsNumberExpr(ExpressionBase):
|
|
178
|
+
"""
|
|
179
|
+
$isNumber expression operator - checks if value is numeric.
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
>>> IsNumberExpr(input=F("value")).model_dump()
|
|
183
|
+
{"$isNumber": "$value"}
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
input: Any
|
|
187
|
+
|
|
188
|
+
@model_serializer
|
|
189
|
+
def serialize(self) -> dict[str, Any]:
|
|
190
|
+
"""Serialize to MongoDB $isNumber expression."""
|
|
191
|
+
return {"$isNumber": serialize_value(self.input)}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
__all__ = [
|
|
195
|
+
"ToStringExpr",
|
|
196
|
+
"ToIntExpr",
|
|
197
|
+
"ToDoubleExpr",
|
|
198
|
+
"ToBoolExpr",
|
|
199
|
+
"ToObjectIdExpr",
|
|
200
|
+
"ToLongExpr",
|
|
201
|
+
"ToDecimalExpr",
|
|
202
|
+
"ConvertExpr",
|
|
203
|
+
"TypeExpr",
|
|
204
|
+
"IsNumberExpr",
|
|
205
|
+
]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Variable expression operators for MongoDB aggregation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import model_serializer
|
|
6
|
+
|
|
7
|
+
from mongo_aggro.base import serialize_value
|
|
8
|
+
from mongo_aggro.expressions.base import ExpressionBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LetExpr(ExpressionBase):
|
|
12
|
+
"""
|
|
13
|
+
$let expression operator - defines variables for use in expression.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> LetExpr(
|
|
17
|
+
... vars={"total": MultiplyExpr(operands=[F("price"), F("qty")])},
|
|
18
|
+
... in_=GtExpr(left=Field("$$total"), right=100)
|
|
19
|
+
... ).model_dump()
|
|
20
|
+
{"$let": {"vars": {"total": {...}}, "in": {...}}}
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
vars: dict[str, Any]
|
|
24
|
+
in_: Any
|
|
25
|
+
|
|
26
|
+
@model_serializer
|
|
27
|
+
def serialize(self) -> dict[str, Any]:
|
|
28
|
+
"""Serialize to MongoDB $let expression."""
|
|
29
|
+
return {
|
|
30
|
+
"$let": {
|
|
31
|
+
"vars": {k: serialize_value(v) for k, v in self.vars.items()},
|
|
32
|
+
"in": serialize_value(self.in_),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LiteralExpr(ExpressionBase):
|
|
38
|
+
"""
|
|
39
|
+
$literal expression operator - returns value without parsing.
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> LiteralExpr(value="$field").model_dump()
|
|
43
|
+
{"$literal": "$field"}
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
value: Any
|
|
47
|
+
|
|
48
|
+
@model_serializer
|
|
49
|
+
def serialize(self) -> dict[str, Any]:
|
|
50
|
+
"""Serialize to MongoDB $literal expression."""
|
|
51
|
+
return {"$literal": self.value}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class RandExpr(ExpressionBase):
|
|
55
|
+
"""
|
|
56
|
+
$rand expression operator - returns random float between 0 and 1.
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
>>> RandExpr().model_dump()
|
|
60
|
+
{"$rand": {}}
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
@model_serializer
|
|
64
|
+
def serialize(self) -> dict[str, Any]:
|
|
65
|
+
"""Serialize to MongoDB $rand expression."""
|
|
66
|
+
return {"$rand": {}}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
__all__ = [
|
|
70
|
+
"LetExpr",
|
|
71
|
+
"LiteralExpr",
|
|
72
|
+
"RandExpr",
|
|
73
|
+
]
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"""Window expression operators for MongoDB aggregation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import model_serializer
|
|
6
|
+
|
|
7
|
+
from mongo_aggro.base import serialize_value
|
|
8
|
+
from mongo_aggro.expressions.base import ExpressionBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RankExpr(ExpressionBase):
|
|
12
|
+
"""
|
|
13
|
+
$rank window operator - returns document rank in partition.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
>>> RankExpr().model_dump()
|
|
17
|
+
{"$rank": {}}
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@model_serializer
|
|
21
|
+
def serialize(self) -> dict[str, Any]:
|
|
22
|
+
"""Serialize to MongoDB $rank expression."""
|
|
23
|
+
return {"$rank": {}}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DenseRankExpr(ExpressionBase):
|
|
27
|
+
"""
|
|
28
|
+
$denseRank window operator - returns dense rank (no gaps).
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
>>> DenseRankExpr().model_dump()
|
|
32
|
+
{"$denseRank": {}}
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@model_serializer
|
|
36
|
+
def serialize(self) -> dict[str, Any]:
|
|
37
|
+
"""Serialize to MongoDB $denseRank expression."""
|
|
38
|
+
return {"$denseRank": {}}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DocumentNumberExpr(ExpressionBase):
|
|
42
|
+
"""
|
|
43
|
+
$documentNumber window operator - returns position in partition.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
>>> DocumentNumberExpr().model_dump()
|
|
47
|
+
{"$documentNumber": {}}
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
@model_serializer
|
|
51
|
+
def serialize(self) -> dict[str, Any]:
|
|
52
|
+
"""Serialize to MongoDB $documentNumber expression."""
|
|
53
|
+
return {"$documentNumber": {}}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ShiftExpr(ExpressionBase):
|
|
57
|
+
"""
|
|
58
|
+
$shift window operator - accesses value from different position.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
>>> ShiftExpr(output=F("value"), by=1, default=0).model_dump()
|
|
62
|
+
{"$shift": {"output": "$value", "by": 1, "default": 0}}
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
output: Any
|
|
66
|
+
by: int
|
|
67
|
+
default: Any = None
|
|
68
|
+
|
|
69
|
+
@model_serializer
|
|
70
|
+
def serialize(self) -> dict[str, Any]:
|
|
71
|
+
"""Serialize to MongoDB $shift expression."""
|
|
72
|
+
result: dict[str, Any] = {
|
|
73
|
+
"output": serialize_value(self.output),
|
|
74
|
+
"by": self.by,
|
|
75
|
+
}
|
|
76
|
+
if self.default is not None:
|
|
77
|
+
result["default"] = serialize_value(self.default)
|
|
78
|
+
return {"$shift": result}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ExpMovingAvgExpr(ExpressionBase):
|
|
82
|
+
"""
|
|
83
|
+
$expMovingAvg window operator - exponential moving average.
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
>>> ExpMovingAvgExpr(input=F("price"), n=5).model_dump()
|
|
87
|
+
{"$expMovingAvg": {"input": "$price", "N": 5}}
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
input: Any
|
|
91
|
+
n: int | None = None
|
|
92
|
+
alpha: float | None = None
|
|
93
|
+
|
|
94
|
+
@model_serializer
|
|
95
|
+
def serialize(self) -> dict[str, Any]:
|
|
96
|
+
"""Serialize to MongoDB $expMovingAvg expression."""
|
|
97
|
+
result: dict[str, Any] = {"input": serialize_value(self.input)}
|
|
98
|
+
if self.n is not None:
|
|
99
|
+
result["N"] = self.n
|
|
100
|
+
elif self.alpha is not None:
|
|
101
|
+
result["alpha"] = self.alpha
|
|
102
|
+
return {"$expMovingAvg": result}
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class DerivativeExpr(ExpressionBase):
|
|
106
|
+
"""
|
|
107
|
+
$derivative window operator - calculates rate of change.
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
>>> DerivativeExpr(input=F("value"), unit="second").model_dump()
|
|
111
|
+
{"$derivative": {"input": "$value", "unit": "second"}}
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
input: Any
|
|
115
|
+
unit: str | None = None
|
|
116
|
+
|
|
117
|
+
@model_serializer
|
|
118
|
+
def serialize(self) -> dict[str, Any]:
|
|
119
|
+
"""Serialize to MongoDB $derivative expression."""
|
|
120
|
+
result: dict[str, Any] = {"input": serialize_value(self.input)}
|
|
121
|
+
if self.unit is not None:
|
|
122
|
+
result["unit"] = self.unit
|
|
123
|
+
return {"$derivative": result}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class IntegralExpr(ExpressionBase):
|
|
127
|
+
"""
|
|
128
|
+
$integral window operator - calculates area under curve.
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
>>> IntegralExpr(input=F("value"), unit="hour").model_dump()
|
|
132
|
+
{"$integral": {"input": "$value", "unit": "hour"}}
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
input: Any
|
|
136
|
+
unit: str | None = None
|
|
137
|
+
|
|
138
|
+
@model_serializer
|
|
139
|
+
def serialize(self) -> dict[str, Any]:
|
|
140
|
+
"""Serialize to MongoDB $integral expression."""
|
|
141
|
+
result: dict[str, Any] = {"input": serialize_value(self.input)}
|
|
142
|
+
if self.unit is not None:
|
|
143
|
+
result["unit"] = self.unit
|
|
144
|
+
return {"$integral": result}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class CovariancePopExpr(ExpressionBase):
|
|
148
|
+
"""
|
|
149
|
+
$covariancePop window operator - population covariance.
|
|
150
|
+
|
|
151
|
+
Example:
|
|
152
|
+
>>> CovariancePopExpr(array=[F("x"), F("y")]).model_dump()
|
|
153
|
+
{"$covariancePop": ["$x", "$y"]}
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
array: list[Any]
|
|
157
|
+
|
|
158
|
+
@model_serializer
|
|
159
|
+
def serialize(self) -> dict[str, Any]:
|
|
160
|
+
"""Serialize to MongoDB $covariancePop expression."""
|
|
161
|
+
return {"$covariancePop": [serialize_value(a) for a in self.array]}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class CovarianceSampExpr(ExpressionBase):
|
|
165
|
+
"""
|
|
166
|
+
$covarianceSamp window operator - sample covariance.
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
>>> CovarianceSampExpr(array=[F("x"), F("y")]).model_dump()
|
|
170
|
+
{"$covarianceSamp": ["$x", "$y"]}
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
array: list[Any]
|
|
174
|
+
|
|
175
|
+
@model_serializer
|
|
176
|
+
def serialize(self) -> dict[str, Any]:
|
|
177
|
+
"""Serialize to MongoDB $covarianceSamp expression."""
|
|
178
|
+
return {"$covarianceSamp": [serialize_value(a) for a in self.array]}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class LinearFillExpr(ExpressionBase):
|
|
182
|
+
"""
|
|
183
|
+
$linearFill window operator - fills nulls with linear interpolation.
|
|
184
|
+
|
|
185
|
+
Example:
|
|
186
|
+
>>> LinearFillExpr(input=F("value")).model_dump()
|
|
187
|
+
{"$linearFill": "$value"}
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
input: Any
|
|
191
|
+
|
|
192
|
+
@model_serializer
|
|
193
|
+
def serialize(self) -> dict[str, Any]:
|
|
194
|
+
"""Serialize to MongoDB $linearFill expression."""
|
|
195
|
+
return {"$linearFill": serialize_value(self.input)}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class LocfExpr(ExpressionBase):
|
|
199
|
+
"""
|
|
200
|
+
$locf window operator - last observation carried forward.
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
>>> LocfExpr(input=F("value")).model_dump()
|
|
204
|
+
{"$locf": "$value"}
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
input: Any
|
|
208
|
+
|
|
209
|
+
@model_serializer
|
|
210
|
+
def serialize(self) -> dict[str, Any]:
|
|
211
|
+
"""Serialize to MongoDB $locf expression."""
|
|
212
|
+
return {"$locf": serialize_value(self.input)}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class TopExpr(ExpressionBase):
|
|
216
|
+
"""
|
|
217
|
+
$top accumulator - returns top element based on sort.
|
|
218
|
+
|
|
219
|
+
Example:
|
|
220
|
+
>>> TopExpr(sort_by={"score": -1}, output=F("name")).model_dump()
|
|
221
|
+
{"$top": {"sortBy": {"score": -1}, "output": "$name"}}
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
sort_by: dict[str, int]
|
|
225
|
+
output: Any
|
|
226
|
+
|
|
227
|
+
@model_serializer
|
|
228
|
+
def serialize(self) -> dict[str, Any]:
|
|
229
|
+
"""Serialize to MongoDB $top expression."""
|
|
230
|
+
return {
|
|
231
|
+
"$top": {
|
|
232
|
+
"sortBy": self.sort_by,
|
|
233
|
+
"output": serialize_value(self.output),
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class BottomExpr(ExpressionBase):
|
|
239
|
+
"""
|
|
240
|
+
$bottom accumulator - returns bottom element based on sort.
|
|
241
|
+
|
|
242
|
+
Example:
|
|
243
|
+
>>> BottomExpr(sort_by={"score": -1}, output=F("name")).model_dump()
|
|
244
|
+
{"$bottom": {"sortBy": {"score": -1}, "output": "$name"}}
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
sort_by: dict[str, int]
|
|
248
|
+
output: Any
|
|
249
|
+
|
|
250
|
+
@model_serializer
|
|
251
|
+
def serialize(self) -> dict[str, Any]:
|
|
252
|
+
"""Serialize to MongoDB $bottom expression."""
|
|
253
|
+
return {
|
|
254
|
+
"$bottom": {
|
|
255
|
+
"sortBy": self.sort_by,
|
|
256
|
+
"output": serialize_value(self.output),
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class TopNWindowExpr(ExpressionBase):
|
|
262
|
+
"""
|
|
263
|
+
$topN accumulator - returns top N elements based on sort.
|
|
264
|
+
|
|
265
|
+
Example:
|
|
266
|
+
>>> TopNWindowExpr(n=3, sort_by={"score": -1}, output=F("name")).model_dump()
|
|
267
|
+
{"$topN": {"n": 3, "sortBy": {"score": -1}, "output": "$name"}}
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
n: Any
|
|
271
|
+
sort_by: dict[str, int]
|
|
272
|
+
output: Any
|
|
273
|
+
|
|
274
|
+
@model_serializer
|
|
275
|
+
def serialize(self) -> dict[str, Any]:
|
|
276
|
+
"""Serialize to MongoDB $topN expression."""
|
|
277
|
+
return {
|
|
278
|
+
"$topN": {
|
|
279
|
+
"n": serialize_value(self.n),
|
|
280
|
+
"sortBy": self.sort_by,
|
|
281
|
+
"output": serialize_value(self.output),
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
class BottomNWindowExpr(ExpressionBase):
|
|
287
|
+
"""
|
|
288
|
+
$bottomN accumulator - returns bottom N elements based on sort.
|
|
289
|
+
|
|
290
|
+
Example:
|
|
291
|
+
>>> BottomNWindowExpr(n=3, sort_by={"score": -1}, output=F("name")).model_dump()
|
|
292
|
+
{"$bottomN": {"n": 3, "sortBy": {"score": -1}, "output": "$name"}}
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
n: Any
|
|
296
|
+
sort_by: dict[str, int]
|
|
297
|
+
output: Any
|
|
298
|
+
|
|
299
|
+
@model_serializer
|
|
300
|
+
def serialize(self) -> dict[str, Any]:
|
|
301
|
+
"""Serialize to MongoDB $bottomN expression."""
|
|
302
|
+
return {
|
|
303
|
+
"$bottomN": {
|
|
304
|
+
"n": serialize_value(self.n),
|
|
305
|
+
"sortBy": self.sort_by,
|
|
306
|
+
"output": serialize_value(self.output),
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
__all__ = [
|
|
312
|
+
"RankExpr",
|
|
313
|
+
"DenseRankExpr",
|
|
314
|
+
"DocumentNumberExpr",
|
|
315
|
+
"ShiftExpr",
|
|
316
|
+
"ExpMovingAvgExpr",
|
|
317
|
+
"DerivativeExpr",
|
|
318
|
+
"IntegralExpr",
|
|
319
|
+
"CovariancePopExpr",
|
|
320
|
+
"CovarianceSampExpr",
|
|
321
|
+
"LinearFillExpr",
|
|
322
|
+
"LocfExpr",
|
|
323
|
+
"TopExpr",
|
|
324
|
+
"BottomExpr",
|
|
325
|
+
"TopNWindowExpr",
|
|
326
|
+
"BottomNWindowExpr",
|
|
327
|
+
]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Query and expression operators for MongoDB aggregation."""
|
|
2
|
+
|
|
3
|
+
from mongo_aggro.operators.array import All, ElemMatch, Size
|
|
4
|
+
from mongo_aggro.operators.base import QueryOperator
|
|
5
|
+
from mongo_aggro.operators.bitwise import (
|
|
6
|
+
BitsAllClear,
|
|
7
|
+
BitsAllSet,
|
|
8
|
+
BitsAnyClear,
|
|
9
|
+
BitsAnySet,
|
|
10
|
+
)
|
|
11
|
+
from mongo_aggro.operators.comparison import Eq, Gt, Gte, In, Lt, Lte, Ne, Nin
|
|
12
|
+
from mongo_aggro.operators.element import Exists, Type
|
|
13
|
+
from mongo_aggro.operators.geo import (
|
|
14
|
+
GeoIntersects,
|
|
15
|
+
GeoWithin,
|
|
16
|
+
Near,
|
|
17
|
+
NearSphere,
|
|
18
|
+
)
|
|
19
|
+
from mongo_aggro.operators.logical import And, Expr, Nor, Not, Or
|
|
20
|
+
from mongo_aggro.operators.misc import JsonSchema, Mod, Text, Where
|
|
21
|
+
from mongo_aggro.operators.regex import Regex
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
# Base
|
|
25
|
+
"QueryOperator",
|
|
26
|
+
# Logical
|
|
27
|
+
"And",
|
|
28
|
+
"Or",
|
|
29
|
+
"Not",
|
|
30
|
+
"Nor",
|
|
31
|
+
"Expr",
|
|
32
|
+
# Comparison
|
|
33
|
+
"Eq",
|
|
34
|
+
"Ne",
|
|
35
|
+
"Gt",
|
|
36
|
+
"Gte",
|
|
37
|
+
"Lt",
|
|
38
|
+
"Lte",
|
|
39
|
+
"In",
|
|
40
|
+
"Nin",
|
|
41
|
+
# Element
|
|
42
|
+
"Exists",
|
|
43
|
+
"Type",
|
|
44
|
+
# Array
|
|
45
|
+
"ElemMatch",
|
|
46
|
+
"Size",
|
|
47
|
+
"All",
|
|
48
|
+
# Regex
|
|
49
|
+
"Regex",
|
|
50
|
+
# Bitwise
|
|
51
|
+
"BitsAllClear",
|
|
52
|
+
"BitsAllSet",
|
|
53
|
+
"BitsAnyClear",
|
|
54
|
+
"BitsAnySet",
|
|
55
|
+
# Geospatial
|
|
56
|
+
"GeoIntersects",
|
|
57
|
+
"GeoWithin",
|
|
58
|
+
"Near",
|
|
59
|
+
"NearSphere",
|
|
60
|
+
# Miscellaneous
|
|
61
|
+
"Mod",
|
|
62
|
+
"JsonSchema",
|
|
63
|
+
"Where",
|
|
64
|
+
"Text",
|
|
65
|
+
]
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Array query operators for MongoDB aggregation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from mongo_aggro.operators.base import QueryOperator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ElemMatch(QueryOperator):
|
|
11
|
+
"""$elemMatch operator - matches array elements."""
|
|
12
|
+
|
|
13
|
+
conditions: dict[str, Any] = Field(
|
|
14
|
+
..., description="Conditions for array elements"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
18
|
+
return {"$elemMatch": self.conditions}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Size(QueryOperator):
|
|
22
|
+
"""$size operator - matches arrays with specific length."""
|
|
23
|
+
|
|
24
|
+
size: int = Field(..., description="Array size to match")
|
|
25
|
+
|
|
26
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
27
|
+
return {"$size": self.size}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class All(QueryOperator):
|
|
31
|
+
"""$all operator - matches arrays containing all specified elements."""
|
|
32
|
+
|
|
33
|
+
values: list[Any] = Field(
|
|
34
|
+
..., description="Values that must all be present"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
38
|
+
return {"$all": self.values}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
__all__ = ["ElemMatch", "Size", "All"]
|