mongo-pipebuilder 0.4.0__tar.gz → 0.5.0__tar.gz
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_pipebuilder-0.4.0/src/mongo_pipebuilder.egg-info → mongo_pipebuilder-0.5.0}/PKG-INFO +16 -4
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/README.md +14 -1
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/pyproject.toml +3 -4
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder/__init__.py +1 -1
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder/builder.py +31 -2
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0/src/mongo_pipebuilder.egg-info}/PKG-INFO +16 -4
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder.egg-info/SOURCES.txt +1 -0
- mongo_pipebuilder-0.5.0/tests/test_builder_add_stages.py +95 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/LICENSE +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/MANIFEST.in +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/setup.cfg +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder.egg-info/dependency_links.txt +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder.egg-info/requires.txt +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder.egg-info/top_level.txt +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder.py +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_debug.py +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_insert.py +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_lookup_let.py +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_match_expr.py +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_union_with.py +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_validation.py +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_validation_existing.py +0 -0
- {mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_validation_new.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mongo-pipebuilder
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Type-safe, fluent MongoDB aggregation pipeline builder
|
|
5
5
|
Author-email: seligoroff <seligoroff@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -12,14 +12,13 @@ Keywords: mongodb,aggregation,pipeline,builder,query
|
|
|
12
12
|
Classifier: Development Status :: 3 - Alpha
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
19
|
Classifier: Topic :: Database
|
|
21
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.9
|
|
23
22
|
Description-Content-Type: text/markdown
|
|
24
23
|
License-File: LICENSE
|
|
25
24
|
Requires-Dist: typing_extensions>=4.0.0; python_version < "3.11"
|
|
@@ -28,7 +27,7 @@ Dynamic: license-file
|
|
|
28
27
|
# mongo-pipebuilder
|
|
29
28
|
|
|
30
29
|
[](https://badge.fury.io/py/mongo-pipebuilder)
|
|
31
|
-
[](https://www.python.org/downloads/)
|
|
32
31
|
[](https://opensource.org/licenses/MIT)
|
|
33
32
|
[](https://github.com/psf/black)
|
|
34
33
|
[](https://github.com/seligoroff/mongo-pipebuilder)
|
|
@@ -282,6 +281,19 @@ Adds a custom stage for advanced use cases.
|
|
|
282
281
|
}})
|
|
283
282
|
```
|
|
284
283
|
|
|
284
|
+
##### `add_stages(stages: Iterable[Dict[str, Any]]) -> Self`
|
|
285
|
+
|
|
286
|
+
Adds multiple stages at once (e.g. a subpipeline from another builder). Empty dicts are skipped. Useful to avoid loops when inserting a ready-made list of stages.
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
# From a list
|
|
290
|
+
.add_stages([{"$match": {"level": "error"}}, {"$limit": 100}])
|
|
291
|
+
|
|
292
|
+
# From another builder
|
|
293
|
+
sub = PipelineBuilder().match({"source": "api"}).project({"name": 1})
|
|
294
|
+
.add_stages(sub.build())
|
|
295
|
+
```
|
|
296
|
+
|
|
285
297
|
##### `prepend(stage: Dict[str, Any]) -> Self`
|
|
286
298
|
|
|
287
299
|
Adds a stage at the beginning of the pipeline.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# mongo-pipebuilder
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/py/mongo-pipebuilder)
|
|
4
|
-
[](https://www.python.org/downloads/)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://github.com/psf/black)
|
|
7
7
|
[](https://github.com/seligoroff/mongo-pipebuilder)
|
|
@@ -255,6 +255,19 @@ Adds a custom stage for advanced use cases.
|
|
|
255
255
|
}})
|
|
256
256
|
```
|
|
257
257
|
|
|
258
|
+
##### `add_stages(stages: Iterable[Dict[str, Any]]) -> Self`
|
|
259
|
+
|
|
260
|
+
Adds multiple stages at once (e.g. a subpipeline from another builder). Empty dicts are skipped. Useful to avoid loops when inserting a ready-made list of stages.
|
|
261
|
+
|
|
262
|
+
```python
|
|
263
|
+
# From a list
|
|
264
|
+
.add_stages([{"$match": {"level": "error"}}, {"$limit": 100}])
|
|
265
|
+
|
|
266
|
+
# From another builder
|
|
267
|
+
sub = PipelineBuilder().match({"source": "api"}).project({"name": 1})
|
|
268
|
+
.add_stages(sub.build())
|
|
269
|
+
```
|
|
270
|
+
|
|
258
271
|
##### `prepend(stage: Dict[str, Any]) -> Self`
|
|
259
272
|
|
|
260
273
|
Adds a stage at the beginning of the pipeline.
|
|
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mongo-pipebuilder"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.0"
|
|
8
8
|
description = "Type-safe, fluent MongoDB aggregation pipeline builder"
|
|
9
9
|
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
11
|
dependencies = [
|
|
12
12
|
"typing_extensions>=4.0.0; python_version<'3.11'",
|
|
13
13
|
]
|
|
@@ -20,7 +20,6 @@ classifiers = [
|
|
|
20
20
|
"Development Status :: 3 - Alpha",
|
|
21
21
|
"Intended Audience :: Developers",
|
|
22
22
|
"Programming Language :: Python :: 3",
|
|
23
|
-
"Programming Language :: Python :: 3.8",
|
|
24
23
|
"Programming Language :: Python :: 3.9",
|
|
25
24
|
"Programming Language :: Python :: 3.10",
|
|
26
25
|
"Programming Language :: Python :: 3.11",
|
|
@@ -84,7 +83,7 @@ skip_empty = true
|
|
|
84
83
|
directory = "tests/htmlcov"
|
|
85
84
|
|
|
86
85
|
[tool.mypy]
|
|
87
|
-
python_version = "3.
|
|
86
|
+
python_version = "3.9"
|
|
88
87
|
warn_return_any = true
|
|
89
88
|
warn_unused_configs = true
|
|
90
89
|
disallow_untyped_defs = false
|
|
@@ -10,9 +10,9 @@ import copy
|
|
|
10
10
|
import difflib
|
|
11
11
|
import json
|
|
12
12
|
from pathlib import Path
|
|
13
|
-
from typing import Any, Dict, List, Optional, Union
|
|
13
|
+
from typing import Any, Dict, Iterable, List, Optional, Union
|
|
14
14
|
|
|
15
|
-
# For compatibility with Python < 3.11
|
|
15
|
+
# For compatibility with Python < 3.11 (Self is in typing from 3.11)
|
|
16
16
|
from typing_extensions import Self
|
|
17
17
|
|
|
18
18
|
|
|
@@ -686,6 +686,35 @@ class PipelineBuilder:
|
|
|
686
686
|
self._stages.append(stage)
|
|
687
687
|
return self
|
|
688
688
|
|
|
689
|
+
def add_stages(self, stages: Iterable[Dict[str, Any]]) -> Self:
|
|
690
|
+
"""
|
|
691
|
+
Add multiple pipeline stages at once (e.g. a subpipeline from another builder).
|
|
692
|
+
|
|
693
|
+
Empty dict stages are skipped, as with add_stage. Each element must be a
|
|
694
|
+
dictionary.
|
|
695
|
+
|
|
696
|
+
Args:
|
|
697
|
+
stages: Iterable of stage dictionaries (e.g. list, or result of .build()).
|
|
698
|
+
|
|
699
|
+
Returns:
|
|
700
|
+
Self for method chaining.
|
|
701
|
+
|
|
702
|
+
Raises:
|
|
703
|
+
TypeError: If stages is None or any element is not a dictionary.
|
|
704
|
+
|
|
705
|
+
Example:
|
|
706
|
+
>>> builder.add_stages([{"$match": {"x": 1}}, {"$limit": 10}])
|
|
707
|
+
>>> builder.add_stages(other_builder.build())
|
|
708
|
+
"""
|
|
709
|
+
if stages is None:
|
|
710
|
+
raise TypeError("stages must not be None")
|
|
711
|
+
for stage in stages:
|
|
712
|
+
if not isinstance(stage, dict):
|
|
713
|
+
raise TypeError("All stages must be dictionaries")
|
|
714
|
+
if stage:
|
|
715
|
+
self._stages.append(stage)
|
|
716
|
+
return self
|
|
717
|
+
|
|
689
718
|
def __len__(self) -> int:
|
|
690
719
|
"""
|
|
691
720
|
Return the number of stages in the pipeline.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mongo-pipebuilder
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Type-safe, fluent MongoDB aggregation pipeline builder
|
|
5
5
|
Author-email: seligoroff <seligoroff@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -12,14 +12,13 @@ Keywords: mongodb,aggregation,pipeline,builder,query
|
|
|
12
12
|
Classifier: Development Status :: 3 - Alpha
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
19
|
Classifier: Topic :: Database
|
|
21
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.9
|
|
23
22
|
Description-Content-Type: text/markdown
|
|
24
23
|
License-File: LICENSE
|
|
25
24
|
Requires-Dist: typing_extensions>=4.0.0; python_version < "3.11"
|
|
@@ -28,7 +27,7 @@ Dynamic: license-file
|
|
|
28
27
|
# mongo-pipebuilder
|
|
29
28
|
|
|
30
29
|
[](https://badge.fury.io/py/mongo-pipebuilder)
|
|
31
|
-
[](https://www.python.org/downloads/)
|
|
32
31
|
[](https://opensource.org/licenses/MIT)
|
|
33
32
|
[](https://github.com/psf/black)
|
|
34
33
|
[](https://github.com/seligoroff/mongo-pipebuilder)
|
|
@@ -282,6 +281,19 @@ Adds a custom stage for advanced use cases.
|
|
|
282
281
|
}})
|
|
283
282
|
```
|
|
284
283
|
|
|
284
|
+
##### `add_stages(stages: Iterable[Dict[str, Any]]) -> Self`
|
|
285
|
+
|
|
286
|
+
Adds multiple stages at once (e.g. a subpipeline from another builder). Empty dicts are skipped. Useful to avoid loops when inserting a ready-made list of stages.
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
# From a list
|
|
290
|
+
.add_stages([{"$match": {"level": "error"}}, {"$limit": 100}])
|
|
291
|
+
|
|
292
|
+
# From another builder
|
|
293
|
+
sub = PipelineBuilder().match({"source": "api"}).project({"name": 1})
|
|
294
|
+
.add_stages(sub.build())
|
|
295
|
+
```
|
|
296
|
+
|
|
285
297
|
##### `prepend(stage: Dict[str, Any]) -> Self`
|
|
286
298
|
|
|
287
299
|
Adds a stage at the beginning of the pipeline.
|
{mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder.egg-info/SOURCES.txt
RENAMED
|
@@ -10,6 +10,7 @@ src/mongo_pipebuilder.egg-info/dependency_links.txt
|
|
|
10
10
|
src/mongo_pipebuilder.egg-info/requires.txt
|
|
11
11
|
src/mongo_pipebuilder.egg-info/top_level.txt
|
|
12
12
|
tests/test_builder.py
|
|
13
|
+
tests/test_builder_add_stages.py
|
|
13
14
|
tests/test_builder_debug.py
|
|
14
15
|
tests/test_builder_insert.py
|
|
15
16
|
tests/test_builder_lookup_let.py
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for PipelineBuilder.add_stages (add multiple stages at once).
|
|
3
|
+
|
|
4
|
+
Author: seligoroff
|
|
5
|
+
"""
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from mongo_pipebuilder import PipelineBuilder
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestAddStages:
|
|
12
|
+
"""Tests for add_stages method."""
|
|
13
|
+
|
|
14
|
+
def test_add_stages_empty_list_adds_nothing(self):
|
|
15
|
+
"""add_stages([]) adds no stages."""
|
|
16
|
+
builder = PipelineBuilder()
|
|
17
|
+
builder.match({"x": 1}).add_stages([])
|
|
18
|
+
pipeline = builder.build()
|
|
19
|
+
assert len(pipeline) == 1
|
|
20
|
+
assert pipeline[0] == {"$match": {"x": 1}}
|
|
21
|
+
|
|
22
|
+
def test_add_stages_one_stage(self):
|
|
23
|
+
"""add_stages([stage]) adds one stage."""
|
|
24
|
+
builder = PipelineBuilder()
|
|
25
|
+
builder.add_stages([{"$match": {"x": 1}}])
|
|
26
|
+
pipeline = builder.build()
|
|
27
|
+
assert len(pipeline) == 1
|
|
28
|
+
assert pipeline[0] == {"$match": {"x": 1}}
|
|
29
|
+
|
|
30
|
+
def test_add_stages_two_stages(self):
|
|
31
|
+
"""add_stages([s1, s2]) adds two stages in order."""
|
|
32
|
+
builder = PipelineBuilder()
|
|
33
|
+
builder.add_stages([{"$match": {"a": 1}}, {"$limit": 10}])
|
|
34
|
+
pipeline = builder.build()
|
|
35
|
+
assert len(pipeline) == 2
|
|
36
|
+
assert pipeline[0] == {"$match": {"a": 1}}
|
|
37
|
+
assert pipeline[1] == {"$limit": 10}
|
|
38
|
+
|
|
39
|
+
def test_add_stages_skips_empty_dict_stages(self):
|
|
40
|
+
"""add_stages([{}, stage, ...]) skips empty dicts, adds only non-empty stages."""
|
|
41
|
+
builder = PipelineBuilder()
|
|
42
|
+
builder.add_stages([{}, {"$match": {"x": 1}}])
|
|
43
|
+
pipeline = builder.build()
|
|
44
|
+
assert len(pipeline) == 1
|
|
45
|
+
assert pipeline[0] == {"$match": {"x": 1}}
|
|
46
|
+
|
|
47
|
+
def test_add_stages_from_other_builder_build(self):
|
|
48
|
+
"""add_stages(other_builder.build()) appends subpipeline; order and content preserved."""
|
|
49
|
+
sub = PipelineBuilder().match({"source": "api"}).project({"name": 1, "_id": 0})
|
|
50
|
+
builder = PipelineBuilder()
|
|
51
|
+
builder.match({"status": "active"}).add_stages(sub.build())
|
|
52
|
+
pipeline = builder.build()
|
|
53
|
+
assert len(pipeline) == 3
|
|
54
|
+
assert pipeline[0] == {"$match": {"status": "active"}}
|
|
55
|
+
assert pipeline[1] == {"$match": {"source": "api"}}
|
|
56
|
+
assert pipeline[2] == {"$project": {"name": 1, "_id": 0}}
|
|
57
|
+
assert len(sub) == 2
|
|
58
|
+
|
|
59
|
+
def test_add_stages_none_raises(self):
|
|
60
|
+
"""add_stages(None) raises TypeError."""
|
|
61
|
+
builder = PipelineBuilder()
|
|
62
|
+
with pytest.raises(TypeError, match="stages must not be None"):
|
|
63
|
+
builder.add_stages(None)
|
|
64
|
+
|
|
65
|
+
def test_add_stages_non_dict_element_raises(self):
|
|
66
|
+
"""add_stages(iterable with non-dict element) raises TypeError."""
|
|
67
|
+
builder = PipelineBuilder()
|
|
68
|
+
with pytest.raises(TypeError, match="All stages must be dictionaries"):
|
|
69
|
+
builder.add_stages([{"$match": {}}, "not a stage"])
|
|
70
|
+
|
|
71
|
+
def test_add_stages_chaining(self):
|
|
72
|
+
"""add_stages returns self; can chain with match, limit, etc."""
|
|
73
|
+
builder = PipelineBuilder()
|
|
74
|
+
pipeline = (
|
|
75
|
+
builder.add_stages([{"$match": {"source": "api"}}])
|
|
76
|
+
.match({"status": "active"})
|
|
77
|
+
.limit(10)
|
|
78
|
+
.build()
|
|
79
|
+
)
|
|
80
|
+
assert len(pipeline) == 3
|
|
81
|
+
assert pipeline[0] == {"$match": {"source": "api"}}
|
|
82
|
+
assert pipeline[1] == {"$match": {"status": "active"}}
|
|
83
|
+
assert pipeline[2] == {"$limit": 10}
|
|
84
|
+
|
|
85
|
+
def test_add_stages_copy_independent(self):
|
|
86
|
+
"""copy() after add_stages yields independent builder; modifying copy does not change original."""
|
|
87
|
+
builder = PipelineBuilder()
|
|
88
|
+
builder.add_stages([{"$match": {"x": 1}}])
|
|
89
|
+
c = builder.copy()
|
|
90
|
+
c.limit(5)
|
|
91
|
+
assert len(builder) == 1
|
|
92
|
+
assert len(c) == 2
|
|
93
|
+
assert builder.build()[0] == {"$match": {"x": 1}}
|
|
94
|
+
assert c.build()[0] == {"$match": {"x": 1}}
|
|
95
|
+
assert c.build()[1] == {"$limit": 5}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder.egg-info/requires.txt
RENAMED
|
File without changes
|
{mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/src/mongo_pipebuilder.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mongo_pipebuilder-0.4.0 → mongo_pipebuilder-0.5.0}/tests/test_builder_validation_existing.py
RENAMED
|
File without changes
|
|
File without changes
|