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.
Files changed (53) hide show
  1. mongo_aggro/__init__.py +400 -0
  2. mongo_aggro/accumulators.py +30 -12
  3. mongo_aggro/base.py +49 -9
  4. mongo_aggro/expressions/__init__.py +396 -0
  5. mongo_aggro/expressions/arithmetic.py +329 -0
  6. mongo_aggro/expressions/array.py +425 -0
  7. mongo_aggro/expressions/base.py +180 -0
  8. mongo_aggro/expressions/bitwise.py +84 -0
  9. mongo_aggro/expressions/comparison.py +161 -0
  10. mongo_aggro/expressions/conditional.py +117 -0
  11. mongo_aggro/expressions/date.py +665 -0
  12. mongo_aggro/expressions/encrypted.py +116 -0
  13. mongo_aggro/expressions/logical.py +72 -0
  14. mongo_aggro/expressions/object.py +122 -0
  15. mongo_aggro/expressions/set.py +150 -0
  16. mongo_aggro/expressions/size.py +48 -0
  17. mongo_aggro/expressions/string.py +365 -0
  18. mongo_aggro/expressions/trigonometry.py +283 -0
  19. mongo_aggro/expressions/type.py +205 -0
  20. mongo_aggro/expressions/variable.py +73 -0
  21. mongo_aggro/expressions/window.py +327 -0
  22. mongo_aggro/operators/__init__.py +65 -0
  23. mongo_aggro/operators/array.py +41 -0
  24. mongo_aggro/operators/base.py +15 -0
  25. mongo_aggro/operators/bitwise.py +81 -0
  26. mongo_aggro/operators/comparison.py +82 -0
  27. mongo_aggro/operators/element.py +32 -0
  28. mongo_aggro/operators/geo.py +171 -0
  29. mongo_aggro/operators/logical.py +111 -0
  30. mongo_aggro/operators/misc.py +102 -0
  31. mongo_aggro/operators/regex.py +25 -0
  32. mongo_aggro/stages/__init__.py +110 -0
  33. mongo_aggro/stages/array.py +69 -0
  34. mongo_aggro/stages/change.py +109 -0
  35. mongo_aggro/stages/core.py +170 -0
  36. mongo_aggro/stages/geo.py +93 -0
  37. mongo_aggro/stages/group.py +154 -0
  38. mongo_aggro/stages/join.py +221 -0
  39. mongo_aggro/stages/misc.py +45 -0
  40. mongo_aggro/stages/output.py +136 -0
  41. mongo_aggro/stages/search.py +315 -0
  42. mongo_aggro/stages/session.py +111 -0
  43. mongo_aggro/stages/stats.py +152 -0
  44. mongo_aggro/stages/transform.py +136 -0
  45. mongo_aggro/stages/window.py +139 -0
  46. mongo_aggro-0.2.2.dist-info/METADATA +193 -0
  47. mongo_aggro-0.2.2.dist-info/RECORD +49 -0
  48. {mongo_aggro-0.1.0.dist-info → mongo_aggro-0.2.2.dist-info}/WHEEL +1 -1
  49. mongo_aggro/operators.py +0 -247
  50. mongo_aggro/stages.py +0 -990
  51. mongo_aggro-0.1.0.dist-info/METADATA +0 -537
  52. mongo_aggro-0.1.0.dist-info/RECORD +0 -9
  53. {mongo_aggro-0.1.0.dist-info → mongo_aggro-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,15 @@
1
+ """Base class and utilities for MongoDB query operators."""
2
+
3
+ from pydantic import BaseModel, ConfigDict
4
+
5
+
6
+ class QueryOperator(BaseModel):
7
+ """Base class for query operators used in $match and other stages."""
8
+
9
+ model_config = ConfigDict(
10
+ populate_by_name=True,
11
+ extra="forbid",
12
+ )
13
+
14
+
15
+ __all__ = ["QueryOperator"]
@@ -0,0 +1,81 @@
1
+ """Bitwise 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 BitsAllClear(QueryOperator):
11
+ """
12
+ $bitsAllClear operator - matches where all bit positions are 0.
13
+
14
+ Example:
15
+ >>> BitsAllClear(mask=35).model_dump()
16
+ {"$bitsAllClear": 35}
17
+
18
+ >>> BitsAllClear(mask=[1, 5]).model_dump()
19
+ {"$bitsAllClear": [1, 5]}
20
+ """
21
+
22
+ mask: int | list[int] = Field(
23
+ ..., description="Bitmask or array of bit positions"
24
+ )
25
+
26
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
27
+ return {"$bitsAllClear": self.mask}
28
+
29
+
30
+ class BitsAllSet(QueryOperator):
31
+ """
32
+ $bitsAllSet operator - matches where all bit positions are 1.
33
+
34
+ Example:
35
+ >>> BitsAllSet(mask=35).model_dump()
36
+ {"$bitsAllSet": 35}
37
+ """
38
+
39
+ mask: int | list[int] = Field(
40
+ ..., description="Bitmask or array of bit positions"
41
+ )
42
+
43
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
44
+ return {"$bitsAllSet": self.mask}
45
+
46
+
47
+ class BitsAnyClear(QueryOperator):
48
+ """
49
+ $bitsAnyClear operator - matches where any bit position is 0.
50
+
51
+ Example:
52
+ >>> BitsAnyClear(mask=35).model_dump()
53
+ {"$bitsAnyClear": 35}
54
+ """
55
+
56
+ mask: int | list[int] = Field(
57
+ ..., description="Bitmask or array of bit positions"
58
+ )
59
+
60
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
61
+ return {"$bitsAnyClear": self.mask}
62
+
63
+
64
+ class BitsAnySet(QueryOperator):
65
+ """
66
+ $bitsAnySet operator - matches where any bit position is 1.
67
+
68
+ Example:
69
+ >>> BitsAnySet(mask=35).model_dump()
70
+ {"$bitsAnySet": 35}
71
+ """
72
+
73
+ mask: int | list[int] = Field(
74
+ ..., description="Bitmask or array of bit positions"
75
+ )
76
+
77
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
78
+ return {"$bitsAnySet": self.mask}
79
+
80
+
81
+ __all__ = ["BitsAllClear", "BitsAllSet", "BitsAnyClear", "BitsAnySet"]
@@ -0,0 +1,82 @@
1
+ """Comparison 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 Eq(QueryOperator):
11
+ """$eq comparison operator."""
12
+
13
+ value: Any = Field(..., description="Value to compare")
14
+
15
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
16
+ return {"$eq": self.value}
17
+
18
+
19
+ class Ne(QueryOperator):
20
+ """$ne (not equal) comparison operator."""
21
+
22
+ value: Any = Field(..., description="Value to compare")
23
+
24
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
25
+ return {"$ne": self.value}
26
+
27
+
28
+ class Gt(QueryOperator):
29
+ """$gt (greater than) comparison operator."""
30
+
31
+ value: Any = Field(..., description="Value to compare")
32
+
33
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
34
+ return {"$gt": self.value}
35
+
36
+
37
+ class Gte(QueryOperator):
38
+ """$gte (greater than or equal) comparison operator."""
39
+
40
+ value: Any = Field(..., description="Value to compare")
41
+
42
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
43
+ return {"$gte": self.value}
44
+
45
+
46
+ class Lt(QueryOperator):
47
+ """$lt (less than) comparison operator."""
48
+
49
+ value: Any = Field(..., description="Value to compare")
50
+
51
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
52
+ return {"$lt": self.value}
53
+
54
+
55
+ class Lte(QueryOperator):
56
+ """$lte (less than or equal) comparison operator."""
57
+
58
+ value: Any = Field(..., description="Value to compare")
59
+
60
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
61
+ return {"$lte": self.value}
62
+
63
+
64
+ class In(QueryOperator):
65
+ """$in operator - matches any value in the array."""
66
+
67
+ values: list[Any] = Field(..., description="List of values to match")
68
+
69
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
70
+ return {"$in": self.values}
71
+
72
+
73
+ class Nin(QueryOperator):
74
+ """$nin operator - matches none of the values in the array."""
75
+
76
+ values: list[Any] = Field(..., description="List of values to exclude")
77
+
78
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
79
+ return {"$nin": self.values}
80
+
81
+
82
+ __all__ = ["Eq", "Ne", "Gt", "Gte", "Lt", "Lte", "In", "Nin"]
@@ -0,0 +1,32 @@
1
+ """Element 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 Exists(QueryOperator):
11
+ """$exists operator - matches documents where field exists/doesn't."""
12
+
13
+ exists: bool = Field(
14
+ default=True, description="True if field should exist"
15
+ )
16
+
17
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
18
+ return {"$exists": self.exists}
19
+
20
+
21
+ class Type(QueryOperator):
22
+ """$type operator - matches documents where field is of specified type."""
23
+
24
+ bson_type: str | int | list[str | int] = Field(
25
+ ..., description="BSON type(s) to match"
26
+ )
27
+
28
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
29
+ return {"$type": self.bson_type}
30
+
31
+
32
+ __all__ = ["Exists", "Type"]
@@ -0,0 +1,171 @@
1
+ """Geospatial 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 GeoIntersects(QueryOperator):
11
+ """
12
+ $geoIntersects operator - matches geometries that intersect.
13
+
14
+ Example:
15
+ >>> GeoIntersects(geometry={
16
+ ... "type": "Polygon",
17
+ ... "coordinates": [[[-100, 60], [-100, 0], [100, 0], [100, 60]]]
18
+ ... }).model_dump()
19
+ {"$geoIntersects": {"$geometry": {...}}}
20
+ """
21
+
22
+ geometry: dict[str, Any] = Field(
23
+ ..., description="GeoJSON geometry object"
24
+ )
25
+
26
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
27
+ return {"$geoIntersects": {"$geometry": self.geometry}}
28
+
29
+
30
+ class GeoWithin(QueryOperator):
31
+ """
32
+ $geoWithin operator - matches geometries within a bounding region.
33
+
34
+ Example:
35
+ >>> GeoWithin(geometry={
36
+ ... "type": "Polygon",
37
+ ... "coordinates": [[[-100, 60], [-100, 0], [100, 0], [100, 60]]]
38
+ ... }).model_dump()
39
+ {"$geoWithin": {"$geometry": {...}}}
40
+
41
+ >>> # Using legacy shapes
42
+ >>> GeoWithin(box=[[-100, -100], [100, 100]]).model_dump()
43
+ {"$geoWithin": {"$box": [[-100, -100], [100, 100]]}}
44
+ """
45
+
46
+ geometry: dict[str, Any] | None = Field(
47
+ default=None, description="GeoJSON geometry object"
48
+ )
49
+ box: list[list[float]] | None = Field(
50
+ default=None, description="Legacy box coordinates [[x1,y1], [x2,y2]]"
51
+ )
52
+ polygon: list[list[float]] | None = Field(
53
+ default=None, description="Legacy polygon coordinates"
54
+ )
55
+ center: list[Any] | None = Field(
56
+ default=None, description="Legacy center coordinates [[x,y], radius]"
57
+ )
58
+ center_sphere: list[Any] | None = Field(
59
+ default=None,
60
+ serialization_alias="centerSphere",
61
+ description="Center sphere [[x,y], radius]",
62
+ )
63
+
64
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
65
+ result: dict[str, Any] = {}
66
+ if self.geometry is not None:
67
+ result["$geometry"] = self.geometry
68
+ if self.box is not None:
69
+ result["$box"] = self.box
70
+ if self.polygon is not None:
71
+ result["$polygon"] = self.polygon
72
+ if self.center is not None:
73
+ result["$center"] = self.center
74
+ if self.center_sphere is not None:
75
+ result["$centerSphere"] = self.center_sphere
76
+ return {"$geoWithin": result}
77
+
78
+
79
+ class Near(QueryOperator):
80
+ """
81
+ $near operator - matches geospatial objects near a point.
82
+
83
+ Example:
84
+ >>> Near(
85
+ ... geometry={"type": "Point", "coordinates": [-73.9667, 40.78]},
86
+ ... max_distance=5000,
87
+ ... min_distance=1000
88
+ ... ).model_dump()
89
+ {"$near": {"$geometry": {...}, "$maxDistance": 5000, "$minDistance": 1000}}
90
+ """
91
+
92
+ geometry: dict[str, Any] | None = Field(
93
+ default=None, description="GeoJSON point"
94
+ )
95
+ max_distance: float | None = Field(
96
+ default=None,
97
+ serialization_alias="$maxDistance",
98
+ description="Maximum distance in meters",
99
+ )
100
+ min_distance: float | None = Field(
101
+ default=None,
102
+ serialization_alias="$minDistance",
103
+ description="Minimum distance in meters",
104
+ )
105
+ # Legacy 2d index format
106
+ legacy_point: list[float] | None = Field(
107
+ default=None, description="Legacy [x, y] coordinates"
108
+ )
109
+
110
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
111
+ if self.legacy_point is not None:
112
+ result: dict[str, Any] = {"$near": self.legacy_point}
113
+ if self.max_distance is not None:
114
+ result["$maxDistance"] = self.max_distance
115
+ return result
116
+ result = {}
117
+ if self.geometry is not None:
118
+ result["$geometry"] = self.geometry
119
+ if self.max_distance is not None:
120
+ result["$maxDistance"] = self.max_distance
121
+ if self.min_distance is not None:
122
+ result["$minDistance"] = self.min_distance
123
+ return {"$near": result}
124
+
125
+
126
+ class NearSphere(QueryOperator):
127
+ """
128
+ $nearSphere operator - matches geospatial objects near a point on sphere.
129
+
130
+ Example:
131
+ >>> NearSphere(
132
+ ... geometry={"type": "Point", "coordinates": [-73.9667, 40.78]},
133
+ ... max_distance=5000
134
+ ... ).model_dump()
135
+ {"$nearSphere": {"$geometry": {...}, "$maxDistance": 5000}}
136
+ """
137
+
138
+ geometry: dict[str, Any] | None = Field(
139
+ default=None, description="GeoJSON point"
140
+ )
141
+ max_distance: float | None = Field(
142
+ default=None,
143
+ serialization_alias="$maxDistance",
144
+ description="Maximum distance in meters",
145
+ )
146
+ min_distance: float | None = Field(
147
+ default=None,
148
+ serialization_alias="$minDistance",
149
+ description="Minimum distance in meters",
150
+ )
151
+ legacy_point: list[float] | None = Field(
152
+ default=None, description="Legacy [x, y] coordinates"
153
+ )
154
+
155
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
156
+ if self.legacy_point is not None:
157
+ result: dict[str, Any] = {"$nearSphere": self.legacy_point}
158
+ if self.max_distance is not None:
159
+ result["$maxDistance"] = self.max_distance
160
+ return result
161
+ result = {}
162
+ if self.geometry is not None:
163
+ result["$geometry"] = self.geometry
164
+ if self.max_distance is not None:
165
+ result["$maxDistance"] = self.max_distance
166
+ if self.min_distance is not None:
167
+ result["$minDistance"] = self.min_distance
168
+ return {"$nearSphere": result}
169
+
170
+
171
+ __all__ = ["GeoIntersects", "GeoWithin", "Near", "NearSphere"]
@@ -0,0 +1,111 @@
1
+ """Logical 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 And(QueryOperator):
11
+ """
12
+ Logical AND operator for combining multiple conditions.
13
+
14
+ Example:
15
+ >>> And(conditions=[
16
+ ... {"status": "active"},
17
+ ... {"age": {"$gt": 18}}
18
+ ... ]).model_dump()
19
+ {"$and": [{"status": "active"}, {"age": {"$gt": 18}}]}
20
+ """
21
+
22
+ conditions: list[dict[str, Any]] = Field(
23
+ ..., description="List of conditions to AND together"
24
+ )
25
+
26
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
27
+ return {"$and": self.conditions}
28
+
29
+
30
+ class Or(QueryOperator):
31
+ """
32
+ Logical OR operator for combining multiple conditions.
33
+
34
+ Example:
35
+ >>> Or(conditions=[
36
+ ... {"status": "active"},
37
+ ... {"status": "pending"}
38
+ ... ]).model_dump()
39
+ {"$or": [{"status": "active"}, {"status": "pending"}]}
40
+ """
41
+
42
+ conditions: list[dict[str, Any]] = Field(
43
+ ..., description="List of conditions to OR together"
44
+ )
45
+
46
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
47
+ return {"$or": self.conditions}
48
+
49
+
50
+ class Not(QueryOperator):
51
+ """
52
+ Logical NOT operator for negating a condition.
53
+
54
+ Example:
55
+ >>> Not(condition={"$regex": "^test"}).model_dump()
56
+ {"$not": {"$regex": "^test"}}
57
+ """
58
+
59
+ condition: dict[str, Any] = Field(..., description="Condition to negate")
60
+
61
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
62
+ return {"$not": self.condition}
63
+
64
+
65
+ class Nor(QueryOperator):
66
+ """
67
+ Logical NOR operator - matches documents that fail all conditions.
68
+
69
+ Example:
70
+ >>> Nor(conditions=[
71
+ ... {"price": {"$gt": 1000}},
72
+ ... {"rating": {"$lt": 3}}
73
+ ... ]).model_dump()
74
+ {"$nor": [{"price": {"$gt": 1000}}, {"rating": {"$lt": 3}}]}
75
+ """
76
+
77
+ conditions: list[dict[str, Any]] = Field(
78
+ ..., description="List of conditions to NOR together"
79
+ )
80
+
81
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
82
+ return {"$nor": self.conditions}
83
+
84
+
85
+ class Expr(QueryOperator):
86
+ """
87
+ $expr operator for using aggregation expressions in queries.
88
+
89
+ Accepts both raw dicts and expression objects (EqExpr, AndExpr, etc.).
90
+ Expression objects are automatically serialized via model_dump().
91
+
92
+ Example:
93
+ >>> Expr(expression={"$eq": ["$field1", "$field2"]}).model_dump()
94
+ {"$expr": {"$eq": ["$field1", "$field2"]}}
95
+
96
+ >>> from mongo_aggro.expressions import F, EqExpr
97
+ >>> Expr(expression=(F("status") == "active")).model_dump()
98
+ {"$expr": {"$eq": ["$status", "active"]}}
99
+ """
100
+
101
+ expression: Any = Field(
102
+ ..., description="Aggregation expression (dict or ExpressionBase)"
103
+ )
104
+
105
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
106
+ from mongo_aggro.base import serialize_value
107
+
108
+ return {"$expr": serialize_value(self.expression)}
109
+
110
+
111
+ __all__ = ["And", "Or", "Not", "Nor", "Expr"]
@@ -0,0 +1,102 @@
1
+ """Miscellaneous 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 Mod(QueryOperator):
11
+ """
12
+ $mod operator - matches where field % divisor == remainder.
13
+
14
+ Example:
15
+ >>> Mod(divisor=4, remainder=0).model_dump()
16
+ {"$mod": [4, 0]}
17
+ """
18
+
19
+ divisor: int = Field(..., description="The divisor value")
20
+ remainder: int = Field(..., description="The remainder value")
21
+
22
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
23
+ return {"$mod": [self.divisor, self.remainder]}
24
+
25
+
26
+ class JsonSchema(QueryOperator):
27
+ """
28
+ $jsonSchema operator - validates documents against JSON Schema.
29
+
30
+ Example:
31
+ >>> JsonSchema(json_schema={
32
+ ... "bsonType": "object",
33
+ ... "required": ["name", "email"],
34
+ ... "properties": {
35
+ ... "name": {"bsonType": "string"},
36
+ ... "email": {"bsonType": "string"}
37
+ ... }
38
+ ... }).model_dump()
39
+ {"$jsonSchema": {...}}
40
+ """
41
+
42
+ json_schema: dict[str, Any] = Field(
43
+ ..., description="JSON Schema document"
44
+ )
45
+
46
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
47
+ return {"$jsonSchema": self.json_schema}
48
+
49
+
50
+ class Where(QueryOperator):
51
+ """
52
+ $where operator - matches using JavaScript expression.
53
+
54
+ Note: $where is slow and should be avoided when possible.
55
+
56
+ Example:
57
+ >>> Where(expression="this.credits == this.debits").model_dump()
58
+ {"$where": "this.credits == this.debits"}
59
+ """
60
+
61
+ expression: str = Field(..., description="JavaScript expression string")
62
+
63
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
64
+ return {"$where": self.expression}
65
+
66
+
67
+ class Text(QueryOperator):
68
+ """
69
+ $text operator - performs text search on indexed fields.
70
+
71
+ Example:
72
+ >>> Text(search="coffee shop", language="en").model_dump()
73
+ {"$text": {"$search": "coffee shop", "$language": "en"}}
74
+ """
75
+
76
+ search: str = Field(..., description="Text to search for")
77
+ language: str | None = Field(
78
+ default=None, description="Language for text search"
79
+ )
80
+ case_sensitive: bool | None = Field(
81
+ default=None,
82
+ serialization_alias="$caseSensitive",
83
+ description="Enable case sensitivity",
84
+ )
85
+ diacritic_sensitive: bool | None = Field(
86
+ default=None,
87
+ serialization_alias="$diacriticSensitive",
88
+ description="Enable diacritic sensitivity",
89
+ )
90
+
91
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
92
+ result: dict[str, Any] = {"$search": self.search}
93
+ if self.language is not None:
94
+ result["$language"] = self.language
95
+ if self.case_sensitive is not None:
96
+ result["$caseSensitive"] = self.case_sensitive
97
+ if self.diacritic_sensitive is not None:
98
+ result["$diacriticSensitive"] = self.diacritic_sensitive
99
+ return {"$text": result}
100
+
101
+
102
+ __all__ = ["Mod", "JsonSchema", "Where", "Text"]
@@ -0,0 +1,25 @@
1
+ """Regex 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 Regex(QueryOperator):
11
+ """$regex operator for pattern matching."""
12
+
13
+ pattern: str = Field(..., description="Regular expression pattern")
14
+ options: str | None = Field(
15
+ default=None, description="Regex options (i, m, x, s)"
16
+ )
17
+
18
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
19
+ result: dict[str, Any] = {"$regex": self.pattern}
20
+ if self.options:
21
+ result["$options"] = self.options
22
+ return result
23
+
24
+
25
+ __all__ = ["Regex"]