moose-lib 0.4.238__py3-none-any.whl → 0.4.240__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.
moose_lib/data_models.py CHANGED
@@ -280,3 +280,16 @@ class StringToEnumMixin:
280
280
  return cls(value) # fallback to default enum validation
281
281
 
282
282
  return core_schema.with_info_before_validator_function(validate, core_schema.enum_schema(cls, list(cls)))
283
+
284
+
285
+ def is_array_nested_type(data_type: DataType) -> bool:
286
+ """Type guard to check if a data type is Array(Nested(...))."""
287
+ return (
288
+ isinstance(data_type, ArrayType) and
289
+ isinstance(data_type.element_type, Nested)
290
+ )
291
+
292
+
293
+ def is_nested_type(data_type: DataType) -> bool:
294
+ """Type guard to check if a data type is Nested (not Array)."""
295
+ return isinstance(data_type, Nested)
@@ -15,6 +15,7 @@ from moose_lib import ClickHouseEngines
15
15
  from ..config.runtime import RuntimeClickHouseConfig
16
16
  from .types import TypedMooseResource, T
17
17
  from ._registry import _tables
18
+ from ..data_models import Column, is_array_nested_type, is_nested_type, _to_columns
18
19
 
19
20
  @dataclass
20
21
  class InsertOptions:
@@ -458,9 +459,11 @@ class OlapTable(TypedMooseResource, Generic[T]):
458
459
  dict_data = []
459
460
  for record in validated_data:
460
461
  if hasattr(record, 'model_dump'):
461
- dict_data.append(record.model_dump())
462
+ record_dict = record.model_dump()
462
463
  else:
463
- dict_data.append(record)
464
+ record_dict = record
465
+ preprocessed_record = self._map_to_clickhouse_record(record_dict)
466
+ dict_data.append(preprocessed_record)
464
467
  if not dict_data:
465
468
  return table_name, b"", settings
466
469
  json_lines = self._to_json_each_row(dict_data)
@@ -527,9 +530,11 @@ class OlapTable(TypedMooseResource, Generic[T]):
527
530
  records_dict = []
528
531
  for record in records:
529
532
  if hasattr(record, 'model_dump'):
530
- records_dict.append(record.model_dump())
533
+ record_dict = record.model_dump()
531
534
  else:
532
- records_dict.append(record)
535
+ record_dict = record
536
+ preprocessed_record = self._map_to_clickhouse_record(record_dict)
537
+ records_dict.append(preprocessed_record)
533
538
 
534
539
  RETRY_BATCH_SIZE = 10
535
540
  for i in range(0, len(records_dict), RETRY_BATCH_SIZE):
@@ -762,4 +767,48 @@ class OlapTable(TypedMooseResource, Generic[T]):
762
767
  should_validate,
763
768
  strategy,
764
769
  options
765
- )
770
+ )
771
+
772
+ def _map_to_clickhouse_record(self, record: dict, columns: Optional[List[Column]] = None) -> dict:
773
+ """
774
+ Recursively transforms a record to match ClickHouse's JSONEachRow requirements.
775
+
776
+ - For every Array(Nested(...)) field at any depth, each item is wrapped in its own array and recursively processed.
777
+ - For every Nested struct (not array), it recurses into the struct.
778
+ - This ensures compatibility with kafka_clickhouse_sync
779
+
780
+ Args:
781
+ record: The input record to transform (may be deeply nested)
782
+ columns: The schema columns for this level (defaults to model columns at the top level)
783
+
784
+ Returns:
785
+ The transformed record, ready for ClickHouse JSONEachRow insertion
786
+ """
787
+ if columns is None:
788
+ columns = _to_columns(self._t)
789
+
790
+ result = record.copy()
791
+
792
+ for col in columns:
793
+ if col.name not in record:
794
+ continue
795
+
796
+ value = record[col.name]
797
+ data_type = col.data_type
798
+
799
+ if is_array_nested_type(data_type):
800
+ # For Array(Nested(...)), wrap each item in its own array and recurse
801
+ if (isinstance(value, list) and
802
+ (len(value) == 0 or isinstance(value[0], dict))):
803
+ nested_columns = data_type.element_type.columns
804
+ result[col.name] = [
805
+ [self._map_to_clickhouse_record(item, nested_columns)]
806
+ for item in value
807
+ ]
808
+ elif is_nested_type(data_type):
809
+ # For Nested struct (not array), recurse into it
810
+ if value and isinstance(value, dict):
811
+ result[col.name] = self._map_to_clickhouse_record(value, data_type.columns)
812
+ # All other types: leave as is
813
+
814
+ return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moose_lib
3
- Version: 0.4.238
3
+ Version: 0.4.240
4
4
  Home-page: https://www.fiveonefour.com/moose
5
5
  Author: Fiveonefour Labs Inc.
6
6
  Author-email: support@fiveonefour.com
@@ -1,7 +1,7 @@
1
1
  moose_lib/__init__.py,sha256=0MpzYNnjpqcBaXjR5CBr1b0M5YjXXjj9y1RKyNeOJQ8,183
2
2
  moose_lib/blocks.py,sha256=_wdvC2NC_Y3MMEnB71WTgWbeQ--zPNHk19xjToJW0C0,3185
3
3
  moose_lib/commons.py,sha256=BV5X78MuOWHiZV9bsWSN69JIvzTNWUi-gnuMiAtaO8A,2489
4
- moose_lib/data_models.py,sha256=8zYPNkOYi0o5Ut2llXwqdIqbXcg_jdv6M_nCEzJ2Onw,9512
4
+ moose_lib/data_models.py,sha256=4eoo68DXQBCy6XS0p_UPoxT9jc42LuKisuFxcDfMunM,9912
5
5
  moose_lib/dmv2-serializer.py,sha256=CL_Pvvg8tJOT8Qk6hywDNzY8MYGhMVdTOw8arZi3jng,49
6
6
  moose_lib/internal.py,sha256=ezqTTWS3T6nAPAxcjMPGYs-6ZwZwxOTZVuVFHiSkEmw,14269
7
7
  moose_lib/main.py,sha256=In-u7yA1FsLDeP_2bhIgBtHY_BkXaZqDwf7BxwyC21c,8471
@@ -18,7 +18,7 @@ moose_lib/dmv2/consumption.py,sha256=vt1APul1O21yPHaB0MRjF_9ohz5MJs5QIpDCnzFwyJA
18
18
  moose_lib/dmv2/ingest_api.py,sha256=Snek9NGwaJl_BuImSWGtQq91m9D3AJ4qBoGiKZ-9yTQ,2323
19
19
  moose_lib/dmv2/ingest_pipeline.py,sha256=Y1gsvHZjlW07gMapLnBRJEsoAPv7ThvLABoLmVV7BHE,6714
20
20
  moose_lib/dmv2/materialized_view.py,sha256=kcx-sJFTM-cH3Uc1GoldgFGodjoz0AegAQEMmohdS38,3826
21
- moose_lib/dmv2/olap_table.py,sha256=D3qpRGMnYF0gu5FRW8E5oDBqdWMCWLRv7fWv81DURsk,28378
21
+ moose_lib/dmv2/olap_table.py,sha256=U-ZdWzLKbxq1ANcG37rI3xJtouW3VM_NFGCWOcuwEs8,30567
22
22
  moose_lib/dmv2/registry.py,sha256=AaGS6Xy0vKz-wHLPgRVxfKfSwW5KksMePjZ8N7-2OKU,2054
23
23
  moose_lib/dmv2/sql_resource.py,sha256=kUZoGqxhZMHMthtBZGYJBxTFjXkspXiWLXhJRYXgGUM,1864
24
24
  moose_lib/dmv2/stream.py,sha256=H5nzqVHIXulFNMNaGZUQnhGjNx7fIg0X95kxAO_qlls,10600
@@ -31,7 +31,7 @@ tests/__init__.py,sha256=0Gh4yzPkkC3TzBGKhenpMIxJcRhyrrCfxLSfpTZnPMQ,53
31
31
  tests/conftest.py,sha256=ZVJNbnr4DwbcqkTmePW6U01zAzE6QD0kNAEZjPG1f4s,169
32
32
  tests/test_moose.py,sha256=mBsx_OYWmL8ppDzL_7Bd7xR6qf_i3-pCIO3wm2iQNaA,2136
33
33
  tests/test_redis_client.py,sha256=d9_MLYsJ4ecVil_jPB2gW3Q5aWnavxmmjZg2uYI3LVo,3256
34
- moose_lib-0.4.238.dist-info/METADATA,sha256=zqv08nRKe6ynI1OUT1BiueMWrzaYk6MUmhTTS-bOhKw,729
35
- moose_lib-0.4.238.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- moose_lib-0.4.238.dist-info/top_level.txt,sha256=XEns2-4aCmGp2XjJAeEH9TAUcGONLnSLy6ycT9FSJh8,16
37
- moose_lib-0.4.238.dist-info/RECORD,,
34
+ moose_lib-0.4.240.dist-info/METADATA,sha256=RjxA65DeF3XIFJU-BEUkhGOuB_YkYMjOIY_Xdvni3go,729
35
+ moose_lib-0.4.240.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ moose_lib-0.4.240.dist-info/top_level.txt,sha256=XEns2-4aCmGp2XjJAeEH9TAUcGONLnSLy6ycT9FSJh8,16
37
+ moose_lib-0.4.240.dist-info/RECORD,,