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,136 @@
|
|
|
1
|
+
"""Document transformation MongoDB aggregation pipeline stages.
|
|
2
|
+
|
|
3
|
+
This module contains stages for transforming document structure:
|
|
4
|
+
AddFields, Set, Unset, ReplaceRoot, ReplaceWith, and Redact.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AddFields(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
$addFields stage - adds new fields to documents.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> AddFields(fields={"isActive": True, "score": {"$sum": "$marks"}})
|
|
18
|
+
{"$addFields": {"isActive": true, "score": {"$sum": "$marks"}}}
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
22
|
+
|
|
23
|
+
fields: dict[str, Any] = Field(..., description="Fields to add")
|
|
24
|
+
|
|
25
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
26
|
+
return {"$addFields": self.fields}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Set(BaseModel):
|
|
30
|
+
"""
|
|
31
|
+
$set stage - alias for $addFields.
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
>>> Set(fields={"status": "processed"}).model_dump()
|
|
35
|
+
{"$set": {"status": "processed"}}
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
39
|
+
|
|
40
|
+
fields: dict[str, Any] = Field(..., description="Fields to set")
|
|
41
|
+
|
|
42
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
43
|
+
return {"$set": self.fields}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Unset(BaseModel):
|
|
47
|
+
"""
|
|
48
|
+
$unset stage - removes fields from documents.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
>>> Unset(fields=["password", "secret"]).model_dump()
|
|
52
|
+
{"$unset": ["password", "secret"]}
|
|
53
|
+
|
|
54
|
+
>>> Unset(fields="temporaryField").model_dump()
|
|
55
|
+
{"$unset": "temporaryField"}
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
59
|
+
|
|
60
|
+
fields: str | list[str] = Field(..., description="Field(s) to remove")
|
|
61
|
+
|
|
62
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
63
|
+
return {"$unset": self.fields}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ReplaceRoot(BaseModel):
|
|
67
|
+
"""
|
|
68
|
+
$replaceRoot stage - replaces document with specified embedded document.
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
>>> ReplaceRoot(new_root="$nested").model_dump()
|
|
72
|
+
{"$replaceRoot": {"newRoot": "$nested"}}
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
76
|
+
|
|
77
|
+
new_root: str | dict[str, Any] = Field(
|
|
78
|
+
...,
|
|
79
|
+
validation_alias="newRoot",
|
|
80
|
+
serialization_alias="newRoot",
|
|
81
|
+
description="Expression for new root",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
85
|
+
return {"$replaceRoot": {"newRoot": self.new_root}}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ReplaceWith(BaseModel):
|
|
89
|
+
"""
|
|
90
|
+
$replaceWith stage - replaces document (alias for $replaceRoot).
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
>>> ReplaceWith(expression="$embedded").model_dump()
|
|
94
|
+
{"$replaceWith": "$embedded"}
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
98
|
+
|
|
99
|
+
expression: str | dict[str, Any] = Field(
|
|
100
|
+
..., description="Expression for new document"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
104
|
+
return {"$replaceWith": self.expression}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class Redact(BaseModel):
|
|
108
|
+
"""
|
|
109
|
+
$redact stage - restricts document content based on stored info.
|
|
110
|
+
|
|
111
|
+
Example:
|
|
112
|
+
>>> Redact(expression={
|
|
113
|
+
... "$cond": {
|
|
114
|
+
... "if": {"$eq": ["$level", 5]},
|
|
115
|
+
... "then": "$$PRUNE",
|
|
116
|
+
... "else": "$$DESCEND"
|
|
117
|
+
... }
|
|
118
|
+
... }).model_dump()
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
122
|
+
|
|
123
|
+
expression: dict[str, Any] = Field(..., description="Redaction expression")
|
|
124
|
+
|
|
125
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
126
|
+
return {"$redact": self.expression}
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
__all__ = [
|
|
130
|
+
"AddFields",
|
|
131
|
+
"Set",
|
|
132
|
+
"Unset",
|
|
133
|
+
"ReplaceRoot",
|
|
134
|
+
"ReplaceWith",
|
|
135
|
+
"Redact",
|
|
136
|
+
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Window function MongoDB aggregation pipeline stages.
|
|
2
|
+
|
|
3
|
+
This module contains stages for window operations and data filling:
|
|
4
|
+
SetWindowFields, Densify, and Fill.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class SetWindowFields(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
$setWindowFields stage - performs window calculations.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> SetWindowFields(
|
|
18
|
+
... partition_by="$state",
|
|
19
|
+
... sort_by={"date": 1},
|
|
20
|
+
... output={
|
|
21
|
+
... "cumulative": {
|
|
22
|
+
... "$sum": "$quantity",
|
|
23
|
+
... "window": {"documents": ["unbounded", "current"]}
|
|
24
|
+
... }
|
|
25
|
+
... }
|
|
26
|
+
... ).model_dump()
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
30
|
+
|
|
31
|
+
partition_by: str | dict[str, Any] | None = Field(
|
|
32
|
+
default=None,
|
|
33
|
+
validation_alias="partitionBy",
|
|
34
|
+
serialization_alias="partitionBy",
|
|
35
|
+
description="Partitioning expression",
|
|
36
|
+
)
|
|
37
|
+
sort_by: dict[str, Literal[-1, 1]] | None = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
validation_alias="sortBy",
|
|
40
|
+
serialization_alias="sortBy",
|
|
41
|
+
description="Sort specification",
|
|
42
|
+
)
|
|
43
|
+
output: dict[str, Any] = Field(
|
|
44
|
+
..., description="Output field specifications"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
48
|
+
result: dict[str, Any] = {"output": self.output}
|
|
49
|
+
if self.partition_by is not None:
|
|
50
|
+
result["partitionBy"] = self.partition_by
|
|
51
|
+
if self.sort_by is not None:
|
|
52
|
+
result["sortBy"] = self.sort_by
|
|
53
|
+
return {"$setWindowFields": result}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Densify(BaseModel):
|
|
57
|
+
"""
|
|
58
|
+
$densify stage - fills gaps in data.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
>>> Densify(
|
|
62
|
+
... field="date",
|
|
63
|
+
... range={"step": 1, "unit": "day", "bounds": "full"},
|
|
64
|
+
... partition_by_fields=["series"]
|
|
65
|
+
... ).model_dump()
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
69
|
+
|
|
70
|
+
field: str = Field(..., description="Field to densify")
|
|
71
|
+
range: dict[str, Any] = Field(..., description="Range specification")
|
|
72
|
+
partition_by_fields: list[str] | None = Field(
|
|
73
|
+
default=None,
|
|
74
|
+
validation_alias="partitionByFields",
|
|
75
|
+
serialization_alias="partitionByFields",
|
|
76
|
+
description="Fields to partition by",
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
80
|
+
result: dict[str, Any] = {"field": self.field, "range": self.range}
|
|
81
|
+
if self.partition_by_fields is not None:
|
|
82
|
+
result["partitionByFields"] = self.partition_by_fields
|
|
83
|
+
return {"$densify": result}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class Fill(BaseModel):
|
|
87
|
+
"""
|
|
88
|
+
$fill stage - fills null/missing field values.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
>>> Fill(
|
|
92
|
+
... sort_by={"date": 1},
|
|
93
|
+
... output={
|
|
94
|
+
... "score": {"method": "linear"},
|
|
95
|
+
... "bootcamp": {"value": "missing"}
|
|
96
|
+
... }
|
|
97
|
+
... ).model_dump()
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
101
|
+
|
|
102
|
+
output: dict[str, Any] = Field(
|
|
103
|
+
..., description="Output field specifications"
|
|
104
|
+
)
|
|
105
|
+
partition_by: str | dict[str, Any] | None = Field(
|
|
106
|
+
default=None,
|
|
107
|
+
validation_alias="partitionBy",
|
|
108
|
+
serialization_alias="partitionBy",
|
|
109
|
+
description="Partitioning expression",
|
|
110
|
+
)
|
|
111
|
+
partition_by_fields: list[str] | None = Field(
|
|
112
|
+
default=None,
|
|
113
|
+
validation_alias="partitionByFields",
|
|
114
|
+
serialization_alias="partitionByFields",
|
|
115
|
+
description="Fields to partition by",
|
|
116
|
+
)
|
|
117
|
+
sort_by: dict[str, Literal[-1, 1]] | None = Field(
|
|
118
|
+
default=None,
|
|
119
|
+
validation_alias="sortBy",
|
|
120
|
+
serialization_alias="sortBy",
|
|
121
|
+
description="Sort specification",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
125
|
+
result: dict[str, Any] = {"output": self.output}
|
|
126
|
+
if self.partition_by is not None:
|
|
127
|
+
result["partitionBy"] = self.partition_by
|
|
128
|
+
if self.partition_by_fields is not None:
|
|
129
|
+
result["partitionByFields"] = self.partition_by_fields
|
|
130
|
+
if self.sort_by is not None:
|
|
131
|
+
result["sortBy"] = self.sort_by
|
|
132
|
+
return {"$fill": result}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
__all__ = [
|
|
136
|
+
"SetWindowFields",
|
|
137
|
+
"Densify",
|
|
138
|
+
"Fill",
|
|
139
|
+
]
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mongo-aggro
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: MongoDB Aggregation Pipeline Builder with Pydantic
|
|
5
|
+
Project-URL: Homepage, https://github.com/hamedghenaat/mongo-aggro
|
|
6
|
+
Project-URL: Repository, https://github.com/hamedghenaat/mongo-aggro
|
|
7
|
+
Project-URL: Documentation, https://hamedghenaat.github.io/mongo-aggro/
|
|
8
|
+
Author: Hamed Ghenaat
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: aggregation,database,mongodb,pipeline,pydantic
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Database
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Requires-Dist: pydantic>=2.10.0
|
|
23
|
+
Provides-Extra: build
|
|
24
|
+
Requires-Dist: build>=1.2.0; extra == 'build'
|
|
25
|
+
Requires-Dist: twine>=6.0.0; extra == 'build'
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: black>=24.10.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: isort>=5.13.2; extra == 'dev'
|
|
29
|
+
Requires-Dist: mypy>=1.13.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pre-commit>=4.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pyupgrade>=3.19.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
33
|
+
Provides-Extra: docs
|
|
34
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
|
|
35
|
+
Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
|
|
36
|
+
Requires-Dist: mkdocstrings-python>=1.12.0; extra == 'docs'
|
|
37
|
+
Requires-Dist: mkdocstrings>=0.27.0; extra == 'docs'
|
|
38
|
+
Provides-Extra: test
|
|
39
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == 'test'
|
|
40
|
+
Requires-Dist: pytest-mock>=3.14.0; extra == 'test'
|
|
41
|
+
Requires-Dist: pytest>=8.0.0; extra == 'test'
|
|
42
|
+
Description-Content-Type: text/markdown
|
|
43
|
+
|
|
44
|
+
# Mongo Aggro
|
|
45
|
+
|
|
46
|
+
A Python package for building MongoDB aggregation pipelines with Pydantic.
|
|
47
|
+
|
|
48
|
+
[](https://pypi.org/project/mongo-aggro/)
|
|
49
|
+
[](https://pypi.org/project/mongo-aggro/)
|
|
50
|
+
[](https://github.com/hamedghenaat/mongo-aggro/blob/main/LICENSE)
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- **Type-safe** - Pydantic models with full type hints
|
|
55
|
+
- **Zero-conversion** - Pass `Pipeline` directly to `collection.aggregate()`
|
|
56
|
+
- **Python operators** - Use `F("age") > 18` instead of `{"$gt": ["$age", 18]}`
|
|
57
|
+
- **149 expression operators** - Arithmetic, string, date, array, and more
|
|
58
|
+
- **46 aggregation stages** - All major MongoDB stages supported
|
|
59
|
+
- **31 query predicates** - Comparison, logical, geospatial, bitwise
|
|
60
|
+
- **17 accumulators** - Sum, Avg, Min, Max, Push, TopN, etc.
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv add mongo-aggro
|
|
66
|
+
# or
|
|
67
|
+
pip install mongo-aggro
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from mongo_aggro import Pipeline, Match, Group, Sort, Expr
|
|
74
|
+
from mongo_aggro.expressions import F
|
|
75
|
+
|
|
76
|
+
# Traditional approach
|
|
77
|
+
pipeline = Pipeline([
|
|
78
|
+
Match(query={"status": "active"}),
|
|
79
|
+
Group(id="$category", accumulators={"total": {"$sum": "$amount"}}),
|
|
80
|
+
Sort(fields={"total": -1})
|
|
81
|
+
])
|
|
82
|
+
|
|
83
|
+
# Python operator syntax
|
|
84
|
+
pipeline = Pipeline([
|
|
85
|
+
Match(query=Expr(expression=(F("status") == "active")).model_dump()),
|
|
86
|
+
Group(id="$category", accumulators={"total": {"$sum": "$amount"}}),
|
|
87
|
+
])
|
|
88
|
+
|
|
89
|
+
# Pass directly to MongoDB
|
|
90
|
+
results = collection.aggregate(pipeline)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Package Structure
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# Import from root (most common)
|
|
97
|
+
from mongo_aggro import Pipeline, Match, Group, Sort, Expr
|
|
98
|
+
|
|
99
|
+
# Import expressions from sub-package
|
|
100
|
+
from mongo_aggro.expressions import F, AddExpr, ConcatExpr, CondExpr
|
|
101
|
+
|
|
102
|
+
# Import stages by category
|
|
103
|
+
from mongo_aggro.stages import Match, Project, Lookup, SetWindowFields
|
|
104
|
+
|
|
105
|
+
# Import query operators
|
|
106
|
+
from mongo_aggro.operators import In, Regex, GeoWithin, Exists
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Expression Operators with Python Syntax
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from mongo_aggro import Expr, Match
|
|
113
|
+
from mongo_aggro.expressions import F
|
|
114
|
+
|
|
115
|
+
# Comparison operators
|
|
116
|
+
F("age") > 18 # {"$gt": ["$age", 18]}
|
|
117
|
+
F("status") == "active" # {"$eq": ["$status", "active"]}
|
|
118
|
+
|
|
119
|
+
# Logical operators (use & | ~ instead of and or not)
|
|
120
|
+
(F("age") >= 18) & (F("status") == "active")
|
|
121
|
+
(F("type") == "premium") | (F("balance") > 1000)
|
|
122
|
+
|
|
123
|
+
# Use in Match stage
|
|
124
|
+
Match(query=Expr(expression=(
|
|
125
|
+
(F("status") == "active") & (F("age") > 18)
|
|
126
|
+
)).model_dump())
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Stages
|
|
130
|
+
|
|
131
|
+
| Category | Stages |
|
|
132
|
+
|----------|--------|
|
|
133
|
+
| **Core** | Match, Project, Group, Sort, Limit, Skip, Unwind, AddFields, Set, Unset |
|
|
134
|
+
| **Join** | Lookup, GraphLookup, UnionWith |
|
|
135
|
+
| **Aggregation** | Bucket, BucketAuto, Facet, SortByCount, Count |
|
|
136
|
+
| **Output** | Out, Merge |
|
|
137
|
+
| **Window** | SetWindowFields, Densify, Fill |
|
|
138
|
+
| **Geospatial** | GeoNear |
|
|
139
|
+
| **Search** | Search, SearchMeta, VectorSearch, RankFusion |
|
|
140
|
+
| **Change Stream** | ChangeStream, ChangeStreamSplitLargeEvent |
|
|
141
|
+
| **Admin** | CollStats, IndexStats, CurrentOp, ListSessions |
|
|
142
|
+
|
|
143
|
+
## Expression Operators
|
|
144
|
+
|
|
145
|
+
| Category | Examples |
|
|
146
|
+
|----------|----------|
|
|
147
|
+
| **Comparison** | Eq, Ne, Gt, Gte, Lt, Lte, Cmp |
|
|
148
|
+
| **Logical** | And, Or, Not |
|
|
149
|
+
| **Arithmetic** | Add, Subtract, Multiply, Divide, Mod, Abs, Ceil, Floor |
|
|
150
|
+
| **String** | Concat, Split, ToLower, ToUpper, Trim, RegexMatch |
|
|
151
|
+
| **Array** | ArraySize, Filter, Map, Reduce, Slice, InArray |
|
|
152
|
+
| **Date** | DateAdd, DateDiff, DateToString, Year, Month, Day |
|
|
153
|
+
| **Type** | ToDate, ToString, ToInt, Convert, Type |
|
|
154
|
+
| **Conditional** | Cond, IfNull, Switch |
|
|
155
|
+
| **Set** | SetUnion, SetIntersection, SetDifference |
|
|
156
|
+
| **Window** | Rank, DenseRank, Shift, ExpMovingAvg |
|
|
157
|
+
|
|
158
|
+
## Query Predicates
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from mongo_aggro import Match, In, Regex, Exists, GeoWithin
|
|
162
|
+
|
|
163
|
+
Match(query={
|
|
164
|
+
"status": In(values=["active", "pending"]).model_dump(),
|
|
165
|
+
"email": Regex(pattern="@company\\.com$").model_dump(),
|
|
166
|
+
"profile": Exists(exists=True).model_dump(),
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Accumulators
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from mongo_aggro import Group, Sum, Avg, Max, Push, merge_accumulators
|
|
174
|
+
|
|
175
|
+
Group(
|
|
176
|
+
id="$category",
|
|
177
|
+
accumulators=merge_accumulators(
|
|
178
|
+
Sum(name="total", field="amount"),
|
|
179
|
+
Avg(name="average", field="price"),
|
|
180
|
+
Max(name="highest", field="price"),
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Documentation
|
|
186
|
+
|
|
187
|
+
- [User Guide](https://hamedghenaat.github.io/mongo-aggro/guide/)
|
|
188
|
+
- [API Reference](https://hamedghenaat.github.io/mongo-aggro/api/)
|
|
189
|
+
- [Examples](https://hamedghenaat.github.io/mongo-aggro/examples/)
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
MIT
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
mongo_aggro/__init__.py,sha256=pEKMZyNaS1tawDb0cTc0AA1ztWJiFL2cS-rwx754ErU,11057
|
|
2
|
+
mongo_aggro/accumulators.py,sha256=Q5e_iyuHvy-zxSSXgRv00VNq5zz3ku41yFIQ6FOLb74,14828
|
|
3
|
+
mongo_aggro/base.py,sha256=UImOK0KD12qAouYKKiFwF0gBq8Cduvmm7p08Bs1Rky0,7198
|
|
4
|
+
mongo_aggro/expressions/__init__.py,sha256=S99sz-fFzZCguI3viFQondR_mYSZaxsn0zlBL0w_e_s,7925
|
|
5
|
+
mongo_aggro/expressions/arithmetic.py,sha256=pJ_JU04uImK5AVul0YrcnXqaPRn4UxB_gCyBUegzlm0,7705
|
|
6
|
+
mongo_aggro/expressions/array.py,sha256=0_xAkBEA-lTVLzWP0e8BNOUIvYRpuaqPhgEncD_BVI4,10722
|
|
7
|
+
mongo_aggro/expressions/base.py,sha256=KHMKsYzcwbAHmMFhSVPvuXIODTgRVrdMsyV0dUmicuU,5381
|
|
8
|
+
mongo_aggro/expressions/bitwise.py,sha256=KR5pQc-foDu-3R8pGPUsDaKT_hR2JZItnBc927QyQDs,1979
|
|
9
|
+
mongo_aggro/expressions/comparison.py,sha256=IPKuAA2DuWtFgJ3JhCNZYhWv31eqPVFELBfLsP4Opog,3628
|
|
10
|
+
mongo_aggro/expressions/conditional.py,sha256=wVDgsUKtsKUiOAVrydNxOSavXsbkCxkAWTB-wfoIj2E,2961
|
|
11
|
+
mongo_aggro/expressions/date.py,sha256=Y6qpxH19p8Jp9IzMC-FcF4W2m_3P3O0wS9OaR8mk81U,18725
|
|
12
|
+
mongo_aggro/expressions/encrypted.py,sha256=darNTAu_QXesBY7u84e2kGyMMqiY-Y2PaRJO1kC1AFE,3315
|
|
13
|
+
mongo_aggro/expressions/logical.py,sha256=WcvfIaabBOpXyfxv1l3g5QvDX5Ek5H2Jl4lRaRCNhvE,1758
|
|
14
|
+
mongo_aggro/expressions/object.py,sha256=r6-igAHLvM7uNgHMFxqrI2K60-eV1Gr1qH_BmIMDT_k,3236
|
|
15
|
+
mongo_aggro/expressions/set.py,sha256=eKuOMN63uv3t605wI955FmiJ3uuwfcr7_GWbuPs33CU,3976
|
|
16
|
+
mongo_aggro/expressions/size.py,sha256=HdKwKQFp3wJRE8Y_0B6KRrO6ExEs2ncoYfU-AfbfDqQ,1144
|
|
17
|
+
mongo_aggro/expressions/string.py,sha256=hWT3Nc7G_X5h1WFKlpCP8tefXzXYNIjevgYuQILhlTI,9412
|
|
18
|
+
mongo_aggro/expressions/trigonometry.py,sha256=oGAeB0EHVqd4XgaWDWT16Jyu70D7TGjrRgv8hs7i1WY,6583
|
|
19
|
+
mongo_aggro/expressions/type.py,sha256=9Wkir3L4KIbNBa0WU1Nswqg77ZMjZAvlNk5qTLU0tIo,5043
|
|
20
|
+
mongo_aggro/expressions/variable.py,sha256=W5gO3gKKIuVtvNbAJEdUz9l6fqoVkQsAK00oQYBewrI,1756
|
|
21
|
+
mongo_aggro/expressions/window.py,sha256=BJY-_Q26pSifp5H06ffGQCHusHHOi4UFyu5jPmzBqIk,8582
|
|
22
|
+
mongo_aggro/operators/__init__.py,sha256=vBEFytD3YA68-9-XfrK2rfs23LK35-zaF7I_FsKPgLo,1289
|
|
23
|
+
mongo_aggro/operators/array.py,sha256=MnULX4J12_Rq-dNuVcTiy1gj6_Cp8pJfa82zUjzDPD4,1051
|
|
24
|
+
mongo_aggro/operators/base.py,sha256=TRFaPmOCmokt5z7DSKG6ieaEN3Do75q8msvGQKLfc-I,335
|
|
25
|
+
mongo_aggro/operators/bitwise.py,sha256=ouP8SoKe4qvcsOYG8ecRTc96HOA5dkjvlxRBsAR3CBs,1972
|
|
26
|
+
mongo_aggro/operators/comparison.py,sha256=2xeex4p0knVwUaWIFi0AU-VGv381MgrdyCG6PLicV8k,2150
|
|
27
|
+
mongo_aggro/operators/element.py,sha256=lYbspdyIsFhUXp26NfnP6xXfdf6Z1yKOY54RSIhoQgM,811
|
|
28
|
+
mongo_aggro/operators/geo.py,sha256=dhksA-ZbzA6T63iBtTOrzuDyWg01D44PnFWMfNn6IgQ,5683
|
|
29
|
+
mongo_aggro/operators/logical.py,sha256=qpe37VBvkDJSvwFrJG4UINOq2KaLh7P_ZUN-N5U00p4,3057
|
|
30
|
+
mongo_aggro/operators/misc.py,sha256=_DKOkqwZtUFkTpdVWy8uthKfB5ZIuVfAvLII-sxj9NU,3038
|
|
31
|
+
mongo_aggro/operators/regex.py,sha256=D4Yn7qGiIhH6yTm1F5P6lfpoRz4-Rn8w3rHDe1Jj3EU,645
|
|
32
|
+
mongo_aggro/stages/__init__.py,sha256=OyIr5g_1ostDHzrg-Ql_0elQ8itDYzKTr-D6rAAmmXw,2239
|
|
33
|
+
mongo_aggro/stages/array.py,sha256=mto_Dwvoktazs2Om5w2ahcpSCt1F9c0NSi7pqNL7VG0,2059
|
|
34
|
+
mongo_aggro/stages/change.py,sha256=Dkv0VNkZ7x3hmd3u1pTQFWwh8KmX-bxA_GXZr7rG-wI,3614
|
|
35
|
+
mongo_aggro/stages/core.py,sha256=q0InLKAa3EMHKJAaae2JmuKydc7pkKmSYbQKtdOkf8s,4310
|
|
36
|
+
mongo_aggro/stages/geo.py,sha256=1e7MZP_HRfziBOps37wuYEztkOKshYm5v3xax4Ahlx8,2939
|
|
37
|
+
mongo_aggro/stages/group.py,sha256=ug6JlmS65Bpq5hjaVQExX9WzItMBTFAytyuthR7TjrQ,4587
|
|
38
|
+
mongo_aggro/stages/join.py,sha256=Q77DQXfLvpmWVg6_fE4JS_maVUqfMerFD0sgvNcQGzk,6683
|
|
39
|
+
mongo_aggro/stages/misc.py,sha256=PMYKOJBu4sdR7umYhuWVEqcVclwIWoZSpV-Unq7Na0U,1034
|
|
40
|
+
mongo_aggro/stages/output.py,sha256=6lAnHcFUtVCdKQnELitd4xlO5A_zBLgjVX2EnUIek7w,3901
|
|
41
|
+
mongo_aggro/stages/search.py,sha256=3h5gkMOJZceUy6LM_zPUjepxUwxBiLZUWLkALftjrio,9648
|
|
42
|
+
mongo_aggro/stages/session.py,sha256=fb6U-aL9O-svuE_abDYNk4oJ5KF2K2u-VX1HXkicex4,3226
|
|
43
|
+
mongo_aggro/stages/stats.py,sha256=8UMcG00MxbfIxzFzdCXBk6tAc8VZ8fvXqNBzKgttKh8,4541
|
|
44
|
+
mongo_aggro/stages/transform.py,sha256=AXFIuEgjAY0aaQTEY4uNrxqH6cvkmJlkIqcVbka5Mwo,3621
|
|
45
|
+
mongo_aggro/stages/window.py,sha256=34fUensjsDwJr-jUI_c6PFgVqTmj6n2T2fZvKTJZG9c,4291
|
|
46
|
+
mongo_aggro-0.2.2.dist-info/METADATA,sha256=qx7oBKbbVvHUyw-widcti1f4YJx1zWL7WvGSv-evZhQ,6303
|
|
47
|
+
mongo_aggro-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
48
|
+
mongo_aggro-0.2.2.dist-info/licenses/LICENSE,sha256=XnrHxv3Dgf-ttUmsrNPMnQ69JLMED67bxKk3zrOdB40,1070
|
|
49
|
+
mongo_aggro-0.2.2.dist-info/RECORD,,
|