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,315 @@
1
+ """Atlas Search MongoDB aggregation pipeline stages.
2
+
3
+ This module contains stages for Atlas Search and Vector Search:
4
+ Search, SearchMeta, VectorSearch, ListSearchIndexes, and RankFusion.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+
12
+ class ListSearchIndexes(BaseModel):
13
+ """
14
+ $listSearchIndexes stage - lists Atlas Search indexes.
15
+
16
+ Example:
17
+ >>> ListSearchIndexes().model_dump()
18
+ {"$listSearchIndexes": {}}
19
+
20
+ >>> ListSearchIndexes(id="index_id").model_dump()
21
+ {"$listSearchIndexes": {"id": "index_id"}}
22
+
23
+ >>> ListSearchIndexes(name="index_name").model_dump()
24
+ {"$listSearchIndexes": {"name": "index_name"}}
25
+ """
26
+
27
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
28
+
29
+ id: str | None = Field(
30
+ default=None,
31
+ description="Search index ID to filter",
32
+ )
33
+ name: str | None = Field(
34
+ default=None,
35
+ description="Search index name to filter",
36
+ )
37
+
38
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
39
+ result: dict[str, Any] = {}
40
+ if self.id is not None:
41
+ result["id"] = self.id
42
+ if self.name is not None:
43
+ result["name"] = self.name
44
+ return {"$listSearchIndexes": result}
45
+
46
+
47
+ class Search(BaseModel):
48
+ """
49
+ $search stage - Atlas full-text search.
50
+
51
+ Example:
52
+ >>> Search(index="default", text={"query": "coffee", "path": "title"})
53
+ {"$search": {"index": "default", "text": {"query": "coffee", ...}}}
54
+ """
55
+
56
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
57
+
58
+ index: str | None = Field(
59
+ default=None,
60
+ description="Name of the Atlas Search index",
61
+ )
62
+ text: dict[str, Any] | None = Field(
63
+ default=None,
64
+ description="Text search operator",
65
+ )
66
+ compound: dict[str, Any] | None = Field(
67
+ default=None,
68
+ description="Compound search operator",
69
+ )
70
+ autocomplete: dict[str, Any] | None = Field(
71
+ default=None,
72
+ description="Autocomplete search operator",
73
+ )
74
+ phrase: dict[str, Any] | None = Field(
75
+ default=None,
76
+ description="Phrase search operator",
77
+ )
78
+ wildcard: dict[str, Any] | None = Field(
79
+ default=None,
80
+ description="Wildcard search operator",
81
+ )
82
+ regex: dict[str, Any] | None = Field(
83
+ default=None,
84
+ description="Regex search operator",
85
+ )
86
+ near: dict[str, Any] | None = Field(
87
+ default=None,
88
+ description="Near search operator",
89
+ )
90
+ range: dict[str, Any] | None = Field(
91
+ default=None,
92
+ description="Range search operator",
93
+ )
94
+ exists: dict[str, Any] | None = Field(
95
+ default=None,
96
+ description="Exists search operator",
97
+ )
98
+ equals: dict[str, Any] | None = Field(
99
+ default=None,
100
+ description="Equals search operator",
101
+ )
102
+ more_like_this: dict[str, Any] | None = Field(
103
+ default=None,
104
+ serialization_alias="moreLikeThis",
105
+ description="More like this search operator",
106
+ )
107
+ query_string: dict[str, Any] | None = Field(
108
+ default=None,
109
+ serialization_alias="queryString",
110
+ description="Query string search operator",
111
+ )
112
+ highlight: dict[str, Any] | None = Field(
113
+ default=None,
114
+ description="Highlight options",
115
+ )
116
+ count: dict[str, Any] | None = Field(
117
+ default=None,
118
+ description="Count options",
119
+ )
120
+ return_stored_source: bool | None = Field(
121
+ default=None,
122
+ serialization_alias="returnStoredSource",
123
+ description="Return stored source",
124
+ )
125
+
126
+ def _add_operators(self, result: dict[str, Any]) -> None:
127
+ """Add search operators to result dict."""
128
+ if self.text is not None:
129
+ result["text"] = self.text
130
+ if self.compound is not None:
131
+ result["compound"] = self.compound
132
+ if self.autocomplete is not None:
133
+ result["autocomplete"] = self.autocomplete
134
+ if self.phrase is not None:
135
+ result["phrase"] = self.phrase
136
+ if self.wildcard is not None:
137
+ result["wildcard"] = self.wildcard
138
+ if self.regex is not None:
139
+ result["regex"] = self.regex
140
+ if self.near is not None:
141
+ result["near"] = self.near
142
+ if self.range is not None:
143
+ result["range"] = self.range
144
+
145
+ def _add_advanced(self, result: dict[str, Any]) -> None:
146
+ """Add advanced search options to result dict."""
147
+ if self.exists is not None:
148
+ result["exists"] = self.exists
149
+ if self.equals is not None:
150
+ result["equals"] = self.equals
151
+ if self.more_like_this is not None:
152
+ result["moreLikeThis"] = self.more_like_this
153
+ if self.query_string is not None:
154
+ result["queryString"] = self.query_string
155
+ if self.highlight is not None:
156
+ result["highlight"] = self.highlight
157
+ if self.count is not None:
158
+ result["count"] = self.count
159
+ if self.return_stored_source is not None:
160
+ result["returnStoredSource"] = self.return_stored_source
161
+
162
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
163
+ result: dict[str, Any] = {}
164
+ if self.index is not None:
165
+ result["index"] = self.index
166
+ self._add_operators(result)
167
+ self._add_advanced(result)
168
+ return {"$search": result}
169
+
170
+
171
+ class SearchMeta(BaseModel):
172
+ """
173
+ $searchMeta stage - returns Atlas Search metadata.
174
+
175
+ Example:
176
+ >>> SearchMeta(index="default", count={"type": "total"}).model_dump()
177
+ {"$searchMeta": {"index": "default", "count": {"type": "total"}}}
178
+ """
179
+
180
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
181
+
182
+ index: str | None = Field(
183
+ default=None,
184
+ description="Name of the Atlas Search index",
185
+ )
186
+ count: dict[str, Any] | None = Field(
187
+ default=None,
188
+ description="Count options",
189
+ )
190
+ facet: dict[str, Any] | None = Field(
191
+ default=None,
192
+ description="Facet options",
193
+ )
194
+
195
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
196
+ result: dict[str, Any] = {}
197
+ if self.index is not None:
198
+ result["index"] = self.index
199
+ if self.count is not None:
200
+ result["count"] = self.count
201
+ if self.facet is not None:
202
+ result["facet"] = self.facet
203
+ return {"$searchMeta": result}
204
+
205
+
206
+ class VectorSearch(BaseModel):
207
+ """
208
+ $vectorSearch stage - Atlas vector search (MongoDB 7.0.2+).
209
+
210
+ Example:
211
+ >>> VectorSearch(
212
+ ... index="vector_index",
213
+ ... path="embedding",
214
+ ... query_vector=[0.1, 0.2, 0.3],
215
+ ... num_candidates=100,
216
+ ... limit=10
217
+ ... ).model_dump()
218
+ {"$vectorSearch": {
219
+ "index": "vector_index",
220
+ "path": "embedding",
221
+ "queryVector": [0.1, 0.2, 0.3],
222
+ "numCandidates": 100,
223
+ "limit": 10
224
+ }}
225
+ """
226
+
227
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
228
+
229
+ index: str = Field(
230
+ ...,
231
+ description="Name of the Atlas Vector Search index",
232
+ )
233
+ path: str = Field(
234
+ ...,
235
+ description="Field path containing the vector",
236
+ )
237
+ query_vector: list[float] = Field(
238
+ ...,
239
+ serialization_alias="queryVector",
240
+ description="Query vector for similarity search",
241
+ )
242
+ num_candidates: int = Field(
243
+ ...,
244
+ serialization_alias="numCandidates",
245
+ description="Number of candidates to consider",
246
+ )
247
+ limit: int = Field(
248
+ ...,
249
+ description="Maximum number of results to return",
250
+ )
251
+ filter: dict[str, Any] | None = Field(
252
+ default=None,
253
+ description="Pre-filter for vector search",
254
+ )
255
+
256
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
257
+ result: dict[str, Any] = {
258
+ "index": self.index,
259
+ "path": self.path,
260
+ "queryVector": self.query_vector,
261
+ "numCandidates": self.num_candidates,
262
+ "limit": self.limit,
263
+ }
264
+ if self.filter is not None:
265
+ result["filter"] = self.filter
266
+ return {"$vectorSearch": result}
267
+
268
+
269
+ class RankFusion(BaseModel):
270
+ """
271
+ $rankFusion stage - combines ranked results from multiple pipelines.
272
+
273
+ Example:
274
+ >>> RankFusion(
275
+ ... input={"search": [...], "vector": [...]},
276
+ ... combination={"weights": {"search": 0.7, "vector": 0.3}}
277
+ ... ).model_dump()
278
+ {"$rankFusion": {
279
+ "input": {"search": [...], "vector": [...]},
280
+ "combination": {"weights": {"search": 0.7, "vector": 0.3}}
281
+ }}
282
+ """
283
+
284
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
285
+
286
+ input: dict[str, list[dict[str, Any]]] = Field(
287
+ ...,
288
+ description="Named input pipelines",
289
+ )
290
+ combination: dict[str, Any] | None = Field(
291
+ default=None,
292
+ description="Combination options",
293
+ )
294
+ score_details: bool | None = Field(
295
+ default=None,
296
+ serialization_alias="scoreDetails",
297
+ description="Include score details",
298
+ )
299
+
300
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
301
+ result: dict[str, Any] = {"input": self.input}
302
+ if self.combination is not None:
303
+ result["combination"] = self.combination
304
+ if self.score_details is not None:
305
+ result["scoreDetails"] = self.score_details
306
+ return {"$rankFusion": result}
307
+
308
+
309
+ __all__ = [
310
+ "Search",
311
+ "SearchMeta",
312
+ "VectorSearch",
313
+ "ListSearchIndexes",
314
+ "RankFusion",
315
+ ]
@@ -0,0 +1,111 @@
1
+ """Session-related MongoDB aggregation pipeline stages.
2
+
3
+ This module contains stages for listing sessions and sampled queries:
4
+ ListSessions, ListLocalSessions, and ListSampledQueries.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+
12
+ class ListSessions(BaseModel):
13
+ """
14
+ $listSessions stage - lists all sessions in system.sessions.
15
+
16
+ Example:
17
+ >>> ListSessions().model_dump()
18
+ {"$listSessions": {}}
19
+
20
+ >>> ListSessions(users=[{"user": "admin", "db": "admin"}]).model_dump()
21
+ {"$listSessions": {"users": [{"user": "admin", "db": "admin"}]}}
22
+
23
+ >>> ListSessions(all_users=True).model_dump()
24
+ {"$listSessions": {"allUsers": True}}
25
+ """
26
+
27
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
28
+
29
+ users: list[dict[str, str]] | None = Field(
30
+ default=None,
31
+ description="List of users to filter sessions",
32
+ )
33
+ all_users: bool | None = Field(
34
+ default=None,
35
+ serialization_alias="allUsers",
36
+ description="Return sessions for all users",
37
+ )
38
+
39
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
40
+ result: dict[str, Any] = {}
41
+ if self.users is not None:
42
+ result["users"] = self.users
43
+ if self.all_users is not None:
44
+ result["allUsers"] = self.all_users
45
+ return {"$listSessions": result}
46
+
47
+
48
+ class ListLocalSessions(BaseModel):
49
+ """
50
+ $listLocalSessions stage - lists local sessions (db.aggregate only).
51
+
52
+ Example:
53
+ >>> ListLocalSessions().model_dump()
54
+ {"$listLocalSessions": {}}
55
+
56
+ >>> ListLocalSessions(all_users=True).model_dump()
57
+ {"$listLocalSessions": {"allUsers": True}}
58
+ """
59
+
60
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
61
+
62
+ users: list[dict[str, str]] | None = Field(
63
+ default=None,
64
+ description="List of users to filter sessions",
65
+ )
66
+ all_users: bool | None = Field(
67
+ default=None,
68
+ serialization_alias="allUsers",
69
+ description="Return sessions for all users",
70
+ )
71
+
72
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
73
+ result: dict[str, Any] = {}
74
+ if self.users is not None:
75
+ result["users"] = self.users
76
+ if self.all_users is not None:
77
+ result["allUsers"] = self.all_users
78
+ return {"$listLocalSessions": result}
79
+
80
+
81
+ class ListSampledQueries(BaseModel):
82
+ """
83
+ $listSampledQueries stage - lists sampled queries.
84
+
85
+ Example:
86
+ >>> ListSampledQueries().model_dump()
87
+ {"$listSampledQueries": {}}
88
+
89
+ >>> ListSampledQueries(namespace="db.collection").model_dump()
90
+ {"$listSampledQueries": {"namespace": "db.collection"}}
91
+ """
92
+
93
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
94
+
95
+ namespace: str | None = Field(
96
+ default=None,
97
+ description="Namespace to filter sampled queries",
98
+ )
99
+
100
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
101
+ result: dict[str, Any] = {}
102
+ if self.namespace is not None:
103
+ result["namespace"] = self.namespace
104
+ return {"$listSampledQueries": result}
105
+
106
+
107
+ __all__ = [
108
+ "ListSessions",
109
+ "ListLocalSessions",
110
+ "ListSampledQueries",
111
+ ]
@@ -0,0 +1,152 @@
1
+ """Statistics and diagnostics MongoDB aggregation pipeline stages.
2
+
3
+ This module contains stages for collection/index statistics and operations:
4
+ CollStats, IndexStats, PlanCacheStats, and CurrentOp.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+
12
+ class CollStats(BaseModel):
13
+ """
14
+ $collStats stage - returns collection statistics.
15
+
16
+ Example:
17
+ >>> CollStats(lat_stats={"histograms": True}).model_dump()
18
+ {"$collStats": {"latencyStats": {"histograms": True}}}
19
+
20
+ >>> CollStats(storage_stats={}).model_dump()
21
+ {"$collStats": {"storageStats": {}}}
22
+
23
+ >>> CollStats(count={}).model_dump()
24
+ {"$collStats": {"count": {}}}
25
+ """
26
+
27
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
28
+
29
+ lat_stats: dict[str, Any] | None = Field(
30
+ default=None,
31
+ serialization_alias="latencyStats",
32
+ description="Latency statistics options",
33
+ )
34
+ storage_stats: dict[str, Any] | None = Field(
35
+ default=None,
36
+ serialization_alias="storageStats",
37
+ description="Storage statistics options",
38
+ )
39
+ count: dict[str, Any] | None = Field(
40
+ default=None,
41
+ description="Document count options",
42
+ )
43
+ query_exec_stats: dict[str, Any] | None = Field(
44
+ default=None,
45
+ serialization_alias="queryExecStats",
46
+ description="Query execution statistics options",
47
+ )
48
+
49
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
50
+ result: dict[str, Any] = {}
51
+ if self.lat_stats is not None:
52
+ result["latencyStats"] = self.lat_stats
53
+ if self.storage_stats is not None:
54
+ result["storageStats"] = self.storage_stats
55
+ if self.count is not None:
56
+ result["count"] = self.count
57
+ if self.query_exec_stats is not None:
58
+ result["queryExecStats"] = self.query_exec_stats
59
+ return {"$collStats": result}
60
+
61
+
62
+ class IndexStats(BaseModel):
63
+ """
64
+ $indexStats stage - returns index usage statistics.
65
+
66
+ Example:
67
+ >>> IndexStats().model_dump()
68
+ {"$indexStats": {}}
69
+ """
70
+
71
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
72
+
73
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
74
+ return {"$indexStats": {}}
75
+
76
+
77
+ class PlanCacheStats(BaseModel):
78
+ """
79
+ $planCacheStats stage - returns plan cache information.
80
+
81
+ Example:
82
+ >>> PlanCacheStats().model_dump()
83
+ {"$planCacheStats": {}}
84
+ """
85
+
86
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
87
+
88
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
89
+ return {"$planCacheStats": {}}
90
+
91
+
92
+ class CurrentOp(BaseModel):
93
+ """
94
+ $currentOp stage - returns current operations (db.aggregate only).
95
+
96
+ Example:
97
+ >>> CurrentOp().model_dump()
98
+ {"$currentOp": {}}
99
+
100
+ >>> CurrentOp(all_users=True, idle_connections=True).model_dump()
101
+ {"$currentOp": {"allUsers": True, "idleConnections": True}}
102
+ """
103
+
104
+ model_config = ConfigDict(populate_by_name=True, extra="forbid")
105
+
106
+ all_users: bool | None = Field(
107
+ default=None,
108
+ serialization_alias="allUsers",
109
+ description="Return operations for all users",
110
+ )
111
+ idle_connections: bool | None = Field(
112
+ default=None,
113
+ serialization_alias="idleConnections",
114
+ description="Include idle connections",
115
+ )
116
+ idle_cursors: bool | None = Field(
117
+ default=None,
118
+ serialization_alias="idleCursors",
119
+ description="Include idle cursors",
120
+ )
121
+ idle_sessions: bool | None = Field(
122
+ default=None,
123
+ serialization_alias="idleSessions",
124
+ description="Include idle sessions",
125
+ )
126
+ local_ops: bool | None = Field(
127
+ default=None,
128
+ serialization_alias="localOps",
129
+ description="Return local operations only",
130
+ )
131
+
132
+ def model_dump(self, **kwargs: Any) -> dict[str, Any]:
133
+ result: dict[str, Any] = {}
134
+ if self.all_users is not None:
135
+ result["allUsers"] = self.all_users
136
+ if self.idle_connections is not None:
137
+ result["idleConnections"] = self.idle_connections
138
+ if self.idle_cursors is not None:
139
+ result["idleCursors"] = self.idle_cursors
140
+ if self.idle_sessions is not None:
141
+ result["idleSessions"] = self.idle_sessions
142
+ if self.local_ops is not None:
143
+ result["localOps"] = self.local_ops
144
+ return {"$currentOp": result}
145
+
146
+
147
+ __all__ = [
148
+ "CollStats",
149
+ "IndexStats",
150
+ "PlanCacheStats",
151
+ "CurrentOp",
152
+ ]