From bbb27a50c7ee929e47bf088a3d8253b2d56f702a Mon Sep 17 00:00:00 2001 From: Vianney Mixtur Date: Fri, 15 Sep 2023 23:28:07 +0200 Subject: [PATCH 1/4] Fixed bug with Group --- monggregate/stages/group.py | 3 ++- monggregate/utils.py | 13 +++++++++- test/test_stages.py | 47 +++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/monggregate/stages/group.py b/monggregate/stages/group.py index 53ad66b..05ea3c9 100644 --- a/monggregate/stages/group.py +++ b/monggregate/stages/group.py @@ -66,7 +66,7 @@ from typing import Any from monggregate.base import pyd from monggregate.stages.stage import Stage -from monggregate.utils import validate_field_path +from monggregate.utils import validate_field_path, validate_field_paths class Group(Stage): """ @@ -91,6 +91,7 @@ class Group(Stage): # Validators # ------------------------------------------ _validate_by = pyd.validator("by", pre=True, always=True, allow_reuse=True)(validate_field_path) # re-used pyd.validator + _validate_iterable_by = pyd.validator("by", pre=True, always=True, allow_reuse=True)(validate_field_paths) # re-used pyd.validator @pyd.validator("query", always=True) @classmethod diff --git a/monggregate/utils.py b/monggregate/utils.py index 4ba419d..e38c882 100644 --- a/monggregate/utils.py +++ b/monggregate/utils.py @@ -66,7 +66,18 @@ def to_unique_list(keys:T)->list[str]|T: def validate_field_path(path:str|None)->str|None: """Validates field path""" - if path and not path.startswith("$"): + if isinstance(path, str) and not path.startswith("$"): path = "$" + path return path + + +def validate_field_paths(paths:list[str]|set[str])->list[str]: + """Validates field paths""" + + if isinstance(paths, list): + paths = [validate_field_path(path) for path in paths] + elif isinstance(paths, set): + paths = {validate_field_path(path) for path in paths} + + return paths diff --git a/test/test_stages.py b/test/test_stages.py index 6b52310..d1d371f 100644 --- a/test/test_stages.py +++ b/test/test_stages.py @@ -115,6 +115,7 @@ def test_count(self, state:State)->None: assert count state["count"] = count + def test_group(self, state:State)->None: """Tests the group stage""" @@ -141,6 +142,52 @@ def test_group(self, state:State)->None: } ) + # Test by as list + # ------------------------ + assert Group( + by=["name", "age"], + query = { + "output":{"$sum":"income"} + } + ) + + # Test by as set + # ------------------------ + assert Group( + by=set(["name", "age"]), + query = { + "output":{"$sum":"income"} + } + ) + + # Test by as constant + # ------------------------ + assert Group( + by=1, + query = { + "output":{"$sum":"income"} + } + ) + + # Test by as dict + # ------------------------ + assert Group( + by={"name":"$name"}, + query = { + "output":{"$sum":"income"} + } + ) + + # Test by as None + # ------------------------ + assert Group( + by=None, + query = { + "output":{"$sum":"income"} + } + ) + + def test_limit(self, state:State)->None: """Tests the limit stage""" From e92746b65feda1ddf7130029804e51e3bf07c0cb Mon Sep 17 00:00:00 2001 From: Vianney Mixtur Date: Sun, 17 Sep 2023 01:09:22 +0200 Subject: [PATCH 2/4] Added more tests on group stage --- monggregate/pipeline.py | 2 +- monggregate/stages/group.py | 2 +- monggregate/utils.py | 3 +- requirements.txt | Bin 3140 -> 3178 bytes test/test_group.py | 115 ++++++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 test/test_group.py diff --git a/monggregate/pipeline.py b/monggregate/pipeline.py index b4984cd..1f356ea 100644 --- a/monggregate/pipeline.py +++ b/monggregate/pipeline.py @@ -355,7 +355,7 @@ def group(self, *, by:Any|None=None, query:dict={})->"Pipeline": Arguments: ------------------------ - - by / _id (offcial MongoDB name represented by a pydantic alias), str | list[str] | set[str] : field or group of fields to group on + - by, str | list[str] | set[str] | dict | None : field or group of fields to group by - query, dict | None : Computed aggregated values (per group) """ diff --git a/monggregate/stages/group.py b/monggregate/stages/group.py index 05ea3c9..fe9bb75 100644 --- a/monggregate/stages/group.py +++ b/monggregate/stages/group.py @@ -74,7 +74,7 @@ class Group(Stage): Attributes: ------------------------ - - by / _id (offcial MongoDB name represented by a pydantic alias), str | list[str] | set[str] : field or group of fields to group on + - by, str | list[str] | set[str] | dict | None : field or group of fields to group by - query, dict | None : Computed aggregated values (per group) diff --git a/monggregate/utils.py b/monggregate/utils.py index e38c882..0b93c8c 100644 --- a/monggregate/utils.py +++ b/monggregate/utils.py @@ -78,6 +78,7 @@ def validate_field_paths(paths:list[str]|set[str])->list[str]: if isinstance(paths, list): paths = [validate_field_path(path) for path in paths] elif isinstance(paths, set): - paths = {validate_field_path(path) for path in paths} + paths = [validate_field_path(path) for path in paths] + paths.sort() return paths diff --git a/requirements.txt b/requirements.txt index 87e0d8f4c9456466ddeaecda0e4f2b083c26d370..8bd1e3ef4b9dbd5af239295844c4a6f3b0c2693d 100644 GIT binary patch delta 44 vcmX>i@k(NY0F!(sLo!1NLkdGCLmCjKG88e`0-*te9)l$i8*JuevgQH+@5~6| delta 12 TcmaDQaYSN+0MlkOCKE0IA6Em) diff --git a/test/test_group.py b/test/test_group.py new file mode 100644 index 0000000..6ba4b9a --- /dev/null +++ b/test/test_group.py @@ -0,0 +1,115 @@ +"""Module to test group (temp)""" + +#from dictdiffer import diff +from monggregate import Pipeline +from monggregate.stages.group import Group + + +def test_group_stage(): + """Test group stage""" + + # Test by as list + # ------------------------------ + assert Group( + by=["name", "age"], + query = { + "output":{"$sum":"income"} + } + ).statement == { + "$group":{ + "_id":["$name", "$age"], + "output":{"$sum":"income"} + } + } + + # Test by as set + # ------------------------------ + group = Group( + by={"name", "age"}, + query = { + "output":{"$sum":"income"} + } + ) + + expected_statement = { + "$group":{ + "_id":["$age", "$name"], + "output":{"$sum":"income"} + } + } + assert group.statement == expected_statement + #, list(diff(expected_statement, group.statement)) + + + # Test by as dict + # ------------------------------ + assert Group( + by={"name":"$name", "age":"$age"}, + query = { + "output":{"$sum":"income"} + } + ).statement == { + "$group":{ + "_id":{"name":"$name", "age":"$age"}, + "output":{"$sum":"income"} + } + } + +def test_group_in_pipeline(): + + # Reproduce tests above with pipeline + # ----------------------------------- + # + # Test by as list + # ------------------------------ + + assert Pipeline().group( + by=["name", "age"], + query = { + "output":{"$sum":"income"} + } + ).statement == [ + { + "$group":{ + "_id":["$name", "$age"], + "output":{"$sum":"income"} + } + } + ] + + # Test by as set + # ------------------------------ + assert Pipeline().group( + by={"name", "age"}, + query = { + "output":{"$sum":"income"} + } + ).statement == [ + { + "$group":{ + "_id":["$age", "$name"], + "output":{"$sum":"income"} + } + } + ] + + # Test by as dict + # ------------------------------ + assert Pipeline().group( + by={"name":"$name", "age":"$age"}, + query = { + "output":{"$sum":"income"} + } + ).statement == [ + { + "$group":{ + "_id":{"name":"$name", "age":"$age"}, + "output":{"$sum":"income"} + } + } + ] + +if __name__ == "__main__": + test_group_stage() + test_group_in_pipeline() + print("All tests passed") From 0c2d9958a1a942dd222d15ba478d8c8a4ac21420 Mon Sep 17 00:00:00 2001 From: Vianney Mixtur Date: Sun, 17 Sep 2023 22:40:52 +0200 Subject: [PATCH 3/4] Updated changelog --- changelog.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index ea29a41..581fb3b 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,19 @@ # Release Notes -## 0.16.0 +## 0.16.2 + +### Fixes + +* Allow to use iterable and dicts to group by in Group class and pipeline group function + +## 0.16.1 +### Fixes + +* Fixed replace_root by passing document argument to ReplaceRoot class + + +## 0.16.0 ### New Features From 90853f368c84e8f8363a92f704dc445a63163b30 Mon Sep 17 00:00:00 2001 From: Vianney Mixtur Date: Sun, 17 Sep 2023 22:41:24 +0200 Subject: [PATCH 4/4] bump version 0.16.1 -> 0.16.2 --- monggregate/__init__.py | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/monggregate/__init__.py b/monggregate/__init__.py index 9175f8b..33fb14e 100644 --- a/monggregate/__init__.py +++ b/monggregate/__init__.py @@ -5,7 +5,7 @@ from monggregate.pipeline import Pipeline -__version__ = "0.16.1" +__version__ = "0.16.2" __author__ = "Vianney Mixtur" __contact__ = "prenom.nom@outlook.fr" __copyright__ = "Copyright © 2022 Vianney Mixtur" diff --git a/pyproject.toml b/pyproject.toml index a291abb..2379ddc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "monggregate" -version = "0.16.1" +version = "0.16.2" description = "MongoDB aggregation pipelines made easy. Joins, grouping, counting and much more..." readme = "README.md" authors = [{ name = "Vianney Mixtur", email = "vianney.mixtur@outlook.fr" }] @@ -35,7 +35,7 @@ dev = ["bumpver", "pytest", "mypy", "pylint"] Homepage = "https://github.com/VianneyMI/monggregate" [tool.bumpver] -current_version = "0.16.1" +current_version = "0.16.2" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "bump version {old_version} -> {new_version}" commit = true