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 +13 -0
- moose_lib/dmv2/olap_table.py +54 -5
- {moose_lib-0.4.238.dist-info → moose_lib-0.4.240.dist-info}/METADATA +1 -1
- {moose_lib-0.4.238.dist-info → moose_lib-0.4.240.dist-info}/RECORD +6 -6
- {moose_lib-0.4.238.dist-info → moose_lib-0.4.240.dist-info}/WHEEL +0 -0
- {moose_lib-0.4.238.dist-info → moose_lib-0.4.240.dist-info}/top_level.txt +0 -0
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)
|
moose_lib/dmv2/olap_table.py
CHANGED
@@ -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
|
-
|
462
|
+
record_dict = record.model_dump()
|
462
463
|
else:
|
463
|
-
|
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
|
-
|
533
|
+
record_dict = record.model_dump()
|
531
534
|
else:
|
532
|
-
|
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,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=
|
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=
|
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.
|
35
|
-
moose_lib-0.4.
|
36
|
-
moose_lib-0.4.
|
37
|
-
moose_lib-0.4.
|
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,,
|
File without changes
|
File without changes
|