mongo-pipebuilder 0.2.1__tar.gz → 0.2.2__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.
Files changed (18) hide show
  1. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/LICENSE +1 -0
  2. {mongo_pipebuilder-0.2.1/src/mongo_pipebuilder.egg-info → mongo_pipebuilder-0.2.2}/PKG-INFO +2 -1
  3. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/README.md +1 -0
  4. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/pyproject.toml +1 -1
  5. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/src/mongo_pipebuilder/__init__.py +1 -1
  6. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/src/mongo_pipebuilder/builder.py +22 -11
  7. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2/src/mongo_pipebuilder.egg-info}/PKG-INFO +2 -1
  8. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/tests/test_builder.py +16 -1
  9. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/tests/test_builder_validation.py +1 -0
  10. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/tests/test_builder_validation_existing.py +11 -4
  11. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/setup.cfg +0 -0
  12. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/src/mongo_pipebuilder.egg-info/SOURCES.txt +0 -0
  13. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/src/mongo_pipebuilder.egg-info/dependency_links.txt +0 -0
  14. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/src/mongo_pipebuilder.egg-info/requires.txt +0 -0
  15. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/src/mongo_pipebuilder.egg-info/top_level.txt +0 -0
  16. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/tests/test_builder_debug.py +0 -0
  17. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/tests/test_builder_insert.py +0 -0
  18. {mongo_pipebuilder-0.2.1 → mongo_pipebuilder-0.2.2}/tests/test_builder_validation_new.py +0 -0
@@ -20,3 +20,4 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  SOFTWARE.
22
22
 
23
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mongo-pipebuilder
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Type-safe, fluent MongoDB aggregation pipeline builder
5
5
  Author-email: seligoroff <seligoroff@gmail.com>
6
6
  License: MIT
@@ -373,3 +373,4 @@ See [DEVELOPMENT.md](DEVELOPMENT.md) for development guidelines.
373
373
 
374
374
  MIT License - see [LICENSE](LICENSE) file for details.
375
375
 
376
+
@@ -345,3 +345,4 @@ See [DEVELOPMENT.md](DEVELOPMENT.md) for development guidelines.
345
345
 
346
346
  MIT License - see [LICENSE](LICENSE) file for details.
347
347
 
348
+
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "mongo-pipebuilder"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Type-safe, fluent MongoDB aggregation pipeline builder"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -9,6 +9,6 @@ Author: seligoroff
9
9
 
10
10
  from mongo_pipebuilder.builder import PipelineBuilder
11
11
 
12
- __version__ = "0.2.1"
12
+ __version__ = "0.2.2"
13
13
  __all__ = ["PipelineBuilder"]
14
14
 
@@ -158,36 +158,47 @@ class PipelineBuilder:
158
158
  self._stages.append({"$project": fields})
159
159
  return self
160
160
 
161
- def group(self, group_by: Dict[str, Any], accumulators: Dict[str, Any]) -> Self:
161
+ def group(self, group_by: Union[str, Dict[str, Any], Any], accumulators: Dict[str, Any]) -> Self:
162
162
  """
163
163
  Add a $group stage for grouping documents.
164
164
 
165
165
  Args:
166
- group_by: Expression for grouping (becomes _id)
166
+ group_by: Expression for grouping (becomes _id). Can be:
167
+ - A string (field path, e.g., "$category")
168
+ - A dict (composite key, e.g., {"category": "$category"})
169
+ - Any other value (null, number, etc.)
167
170
  accumulators: Dictionary with accumulators (sum, avg, count, etc.)
168
171
 
169
172
  Returns:
170
173
  Self for method chaining
171
174
 
172
175
  Raises:
173
- TypeError: If arguments are not dictionaries
174
- ValueError: If both group_by and accumulators are empty
176
+ TypeError: If accumulators is not a dictionary
177
+ ValueError: If both group_by and accumulators are empty (when group_by is dict/str)
175
178
 
176
179
  Example:
177
180
  >>> builder.group(
178
- ... group_by={"category": "$category"},
181
+ ... group_by="$category", # String field path
182
+ ... accumulators={"total": {"$sum": "$amount"}}
183
+ ... )
184
+ >>> builder.group(
185
+ ... group_by={"category": "$category"}, # Composite key
179
186
  ... accumulators={"total": {"$sum": "$amount"}}
180
187
  ... )
181
188
  """
182
- if not isinstance(group_by, dict):
183
- raise TypeError(f"group_by must be a dict, got {type(group_by)}")
184
189
  if not isinstance(accumulators, dict):
185
190
  raise TypeError(f"accumulators must be a dict, got {type(accumulators)}")
186
191
 
187
- # Empty group_by is technically valid in MongoDB (groups all into one document)
188
- # But if both are empty, it's likely an error
189
- if not group_by and not accumulators:
190
- raise ValueError("group_by and accumulators cannot both be empty")
192
+ # Validate empty cases
193
+ # group_by can be None, empty string, empty dict, etc. - all are valid in MongoDB
194
+ # But if it's a string and empty, or dict and empty, and accumulators is also empty,
195
+ # it's likely an error
196
+ if isinstance(group_by, dict):
197
+ if not group_by and not accumulators:
198
+ raise ValueError("group_by and accumulators cannot both be empty")
199
+ elif isinstance(group_by, str):
200
+ if not group_by and not accumulators:
201
+ raise ValueError("group_by and accumulators cannot both be empty")
191
202
 
192
203
  group_stage = {"_id": group_by, **accumulators}
193
204
  self._stages.append({"$group": group_stage})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mongo-pipebuilder
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Type-safe, fluent MongoDB aggregation pipeline builder
5
5
  Author-email: seligoroff <seligoroff@gmail.com>
6
6
  License: MIT
@@ -373,3 +373,4 @@ See [DEVELOPMENT.md](DEVELOPMENT.md) for development guidelines.
373
373
 
374
374
  MIT License - see [LICENSE](LICENSE) file for details.
375
375
 
376
+
@@ -78,7 +78,7 @@ class TestPipelineBuilder:
78
78
  assert pipeline == [{"$project": {"name": 1, "email": 1, "_id": 0}}]
79
79
 
80
80
  def test_group_stage(self):
81
- """Test adding $group stage."""
81
+ """Test adding $group stage with dict."""
82
82
  builder = PipelineBuilder()
83
83
  pipeline = builder.group(
84
84
  group_by={"category": "$category"},
@@ -92,6 +92,21 @@ class TestPipelineBuilder:
92
92
  }
93
93
  }]
94
94
 
95
+ def test_group_stage_with_string(self):
96
+ """Test adding $group stage with string field path."""
97
+ builder = PipelineBuilder()
98
+ pipeline = builder.group(
99
+ group_by="$categoryType",
100
+ accumulators={"total": {"$sum": "$amount"}}
101
+ ).build()
102
+
103
+ assert pipeline == [{
104
+ "$group": {
105
+ "_id": "$categoryType",
106
+ "total": {"$sum": "$amount"}
107
+ }
108
+ }]
109
+
95
110
  def test_unwind_stage(self):
96
111
  """Test adding $unwind stage."""
97
112
  builder = PipelineBuilder()
@@ -171,3 +171,4 @@ class TestPipelineValidation:
171
171
  assert "$sort" in pipeline[5]
172
172
  assert "$limit" in pipeline[6]
173
173
 
174
+
@@ -72,11 +72,18 @@ class TestGroupValidation:
72
72
  pipeline = builder.group({}, {"count": {"$sum": 1}}).build()
73
73
  assert pipeline == [{"$group": {"_id": {}, "count": {"$sum": 1}}}]
74
74
 
75
- def test_group_invalid_group_by_type_raises_error(self):
76
- """Test that group(123, {}) raises TypeError."""
75
+ def test_group_with_string_group_by(self):
76
+ """Test that group() accepts string for group_by (field path)."""
77
77
  builder = PipelineBuilder()
78
- with pytest.raises(TypeError, match="group_by must be a dict"):
79
- builder.group(123, {})
78
+ # String field path is valid in MongoDB
79
+ pipeline = builder.group("$categoryType", {"total": {"$sum": "$amount"}}).build()
80
+ assert pipeline == [{"$group": {"_id": "$categoryType", "total": {"$sum": "$amount"}}}]
81
+
82
+ def test_group_empty_string_with_empty_accumulators_raises_error(self):
83
+ """Test that group('', {}) raises ValueError."""
84
+ builder = PipelineBuilder()
85
+ with pytest.raises(ValueError, match="group_by and accumulators cannot both be empty"):
86
+ builder.group("", {})
80
87
 
81
88
  def test_group_invalid_accumulators_type_raises_error(self):
82
89
  """Test that group({}, 123) raises TypeError."""