oplnk-python-utils 1.2.3__tar.gz → 1.2.5__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 (22) hide show
  1. {oplnk_python_utils-1.2.3/oplnk_python_utils.egg-info → oplnk_python_utils-1.2.5}/PKG-INFO +1 -1
  2. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/__init__.py +1 -1
  3. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/query/query_builder.py +72 -14
  4. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5/oplnk_python_utils.egg-info}/PKG-INFO +1 -1
  5. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/setup.py +1 -1
  6. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/LICENSE +0 -0
  7. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/MANIFEST.in +0 -0
  8. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/README.md +0 -0
  9. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/cache/__init__.py +0 -0
  10. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/cache/cache_utils.py +0 -0
  11. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/core/__init__.py +0 -0
  12. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/core/data.py +0 -0
  13. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/core/parsing.py +0 -0
  14. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/core/serialization.py +0 -0
  15. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/query/__init__.py +0 -0
  16. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils/reports/__init__.py +0 -0
  17. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils.egg-info/SOURCES.txt +0 -0
  18. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils.egg-info/dependency_links.txt +0 -0
  19. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils.egg-info/not-zip-safe +0 -0
  20. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils.egg-info/requires.txt +0 -0
  21. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/oplnk_python_utils.egg-info/top_level.txt +0 -0
  22. {oplnk_python_utils-1.2.3 → oplnk_python_utils-1.2.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oplnk-python-utils
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: Reusable utilities for caching, data manipulation, parsing, and MongoDB query building
5
5
  Home-page: https://github.com/openlinkspa/oplnk-python-utils
6
6
  Author: OpenLink SpA
@@ -7,7 +7,7 @@ from . import core
7
7
  from . import cache
8
8
  from . import query
9
9
 
10
- __version__ = "1.2.3"
10
+ __version__ = "1.2.5"
11
11
  __author__ = "OpenLink SpA"
12
12
 
13
13
  __all__ = ["core", "cache", "query", "reports", "__version__"]
@@ -626,10 +626,11 @@ class QueryBuilder:
626
626
  return {"$sort": sort_pipeline}
627
627
 
628
628
  def build_fields_pipeline(
629
- self, fields_params: List[str], populate_params: Optional[List] = None
629
+ self, fields_params: List[str], populate_params: Optional[Any] = None
630
630
  ) -> Dict[str, Any]:
631
631
  """
632
632
  Build fields projection pipeline with support for dot notation and array notation.
633
+ Automatically includes aliases for populated fields when fields is specified.
633
634
  """
634
635
  fields_pipeline = {}
635
636
 
@@ -648,15 +649,43 @@ class QueryBuilder:
648
649
 
649
650
  # Process populate_params if provided
650
651
  if populate_params:
651
- for field in populate_params:
652
+ # Convert to list if it's a dict
653
+ populate_list = (
654
+ populate_params
655
+ if isinstance(populate_params, list)
656
+ else list(populate_params.keys())
657
+ )
658
+
659
+ for field in populate_list:
652
660
  try:
661
+ # Add the original field so sanitization can work
653
662
  normalized_field = normalize_field_path(field)
654
663
  if validate_field_path(normalized_field):
655
664
  fields_pipeline[normalized_field] = 1
665
+
666
+ # Add the populated alias for audit fields
667
+ if field in RESERVED_AUDIT_FIELDS:
668
+ alias = RESERVED_AUDIT_FIELDS[field]["alias"]
669
+ normalized_alias = normalize_field_path(alias)
670
+ if validate_field_path(normalized_alias):
671
+ fields_pipeline[normalized_alias] = 1
656
672
  else:
657
- print(
658
- f"Warning: Invalid populate field path '{field}', skipping in projection"
659
- )
673
+ # For normal relations, find the alias and add it
674
+ for relation in self.relations:
675
+ relation_name = relation[1]
676
+ base_name = relation_name.replace("_ids", "").replace(
677
+ "_id", ""
678
+ )
679
+ if base_name == field:
680
+ # Use custom alias if provided (5th element)
681
+ if len(relation) > 4 and relation[4]:
682
+ alias = relation[4]
683
+ else:
684
+ alias = base_name
685
+ normalized_alias = normalize_field_path(alias)
686
+ if validate_field_path(normalized_alias):
687
+ fields_pipeline[normalized_alias] = 1
688
+ break
660
689
  except Exception as e:
661
690
  print(
662
691
  f"Warning: Error processing populate field '{field}' in projection: {e}"
@@ -842,27 +871,56 @@ class QueryBuilder:
842
871
  meta_result = {"totalCount": 0}
843
872
 
844
873
  # Sanitize populate fields: remove original ID fields when populated (unless requested in fields)
874
+ # Also ensure populated fields return null if lookup failed (instead of missing)
845
875
  if "populate" in query and isinstance(data_result, list):
846
876
  fields_requested = query.get("fields", [])
847
877
  populated_fields = self._get_populated_fields_list(query["populate"])
848
878
 
849
879
  for doc in data_result:
850
- # For audit fields: remove original if populated version exists and not requested in fields
880
+ # For audit fields
851
881
  for field_name, field_config in RESERVED_AUDIT_FIELDS.items():
852
882
  if field_name in populated_fields:
853
- # If the populated alias exists and original field is not explicitly requested
854
- if (
855
- field_config["alias"] in doc
856
- and field_name not in fields_requested
857
- ):
858
- if field_name in doc:
883
+ alias = field_config["alias"]
884
+ original_exists = field_name in doc
885
+ alias_exists = alias in doc
886
+
887
+ # If populate was requested but alias doesn't exist, set to null
888
+ # (only if the original field exists - meaning the field is part of the document)
889
+ if not alias_exists and original_exists:
890
+ doc[alias] = None
891
+
892
+ # Remove original field if not explicitly requested in fields
893
+ if alias_exists and field_name not in fields_requested:
894
+ if original_exists:
859
895
  del doc[field_name]
860
896
 
861
- # For normal relations: remove original ID unless explicitly requested in fields
897
+ # For normal relations
862
898
  for populated_field in populated_fields:
863
899
  if populated_field not in RESERVED_AUDIT_FIELDS:
864
- # Try to find the ID field for this relation
900
+ # Find the ID field and alias for this relation
865
901
  id_field = self._get_id_field_for_relation(populated_field)
902
+
903
+ # Determine the alias (same as base name or custom alias)
904
+ alias = populated_field
905
+ for relation in self.relations:
906
+ relation_name = relation[1]
907
+ base_name = relation_name.replace("_ids", "").replace(
908
+ "_id", ""
909
+ )
910
+ if base_name == populated_field:
911
+ # Use custom alias if provided (5th element)
912
+ if len(relation) > 4 and relation[4]:
913
+ alias = relation[4]
914
+ break
915
+
916
+ original_exists = id_field and id_field in doc
917
+ alias_exists = alias in doc
918
+
919
+ # If populate was requested but alias doesn't exist, set to null
920
+ if not alias_exists and original_exists:
921
+ doc[alias] = None
922
+
923
+ # Remove original ID field if not explicitly requested in fields
866
924
  if id_field and id_field not in fields_requested:
867
925
  if id_field in doc:
868
926
  del doc[id_field]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oplnk-python-utils
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: Reusable utilities for caching, data manipulation, parsing, and MongoDB query building
5
5
  Home-page: https://github.com/openlinkspa/oplnk-python-utils
6
6
  Author: OpenLink SpA
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="oplnk-python-utils",
5
- version="1.2.3",
5
+ version="1.2.5",
6
6
  author="OpenLink SpA",
7
7
  author_email="contacto@openlink.cl",
8
8
  description="Reusable utilities for caching, data manipulation, parsing, and MongoDB query building",