Skip to content

Commit 3459158

Browse files
committed
Expand Cube adapter coverage: extends, views, hierarchies, meta, and export fixes
Wire up existing core model fields (extends, meta, title, shown, drill_members) that were already defined but not read during Cube YAML parsing. Add case dimension to SQL CASE WHEN conversion, hierarchy to Dimension.parent chain parsing, rolling_window.type:to_date support, time_shift to time_comparison mapping, and type:rank handling. Parse views section into composite Models with join_path/includes/excludes/prefix/alias resolution. Fix export: proper ${CUBE} join SQL format, all relationship types, pre-aggregation export, and meta/title/ shown roundtrip. Add public field to Dimension and Metric, fix inheritance merge to include pre_aggregations.
1 parent 0ee407d commit 3459158

7 files changed

Lines changed: 671 additions & 140 deletions

File tree

docs/compatibility/cube.md

Lines changed: 74 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ The adapter only reads YAML. Cube's original JavaScript schema format (`cube.js`
2828
| `sql_table` | Supported (stored as `Model.table`) |
2929
| `sql` (inline query) | Supported (stored as `Model.sql`, `Model.table = None`) |
3030
| `description` | Supported |
31-
| `extends` | Partial support: the extending cube is parsed with only its own fields. Inherited fields from the base cube are not merged. Each cube in an extends chain is independent. |
32-
| `public` / `shown` | Unsupported |
31+
| `extends` | Supported (full inheritance resolution: child inherits all dimensions, metrics, relationships, segments, and pre-aggregations from parent, with child values taking precedence) |
32+
| `meta` | Supported (stored on `Model.meta`) |
33+
| `public` / `shown` | Unsupported (cube-level visibility) |
3334
| `data_source` | Unsupported |
3435
| `sql_alias` | Unsupported |
3536
| `refresh_key` (cube-level) | Unsupported |
@@ -55,15 +56,17 @@ Not mapped: `title`, `rewrite_queries`, `context_members`.
5556
| `sql: ${cube_name}.column` | Supported (cube-name references replaced with `{model}`) |
5657
| `primary_key: true` | Supported |
5758
| `description` | Supported |
59+
| `title` | Supported (stored as `Dimension.label`) |
5860
| `format` | Supported (stored on `Dimension.format`) |
61+
| `shown` / `public` | Supported (stored as `Dimension.public`) |
62+
| `meta` | Supported (stored on `Dimension.meta`) |
5963
| Cross-cube dimension references (`${other_cube.field}`) | Supported (preserved as-is in SQL) |
6064
| Path-qualified references (`{b.d.id}`) | Supported (preserved as-is in SQL) |
61-
| `case: { when: [...] }` (case dimensions) | Partial support: the `case` block is parsed by YAML without error but the case/when/else structure is not evaluated or stored. The dimension has no SQL expression. |
65+
| `case: { when: [...] }` (case dimensions) | Supported (converted to SQL `CASE WHEN ... THEN ... ELSE ... END` expression) |
66+
| Custom `granularities` on time dimensions | Supported (stored in `Dimension.supported_granularities`) |
6267
| `sub_query: true` | Unsupported (flag ignored, dimension SQL preserved verbatim) |
63-
| Custom `granularities` on time dimensions | Unsupported (parsed by YAML but not stored) |
64-
| `meta` | Unsupported |
6568

66-
Not mapped: `shown`, `title`, `propagate_filters_to_sub_query`, `case` labels.
69+
Not mapped: `propagate_filters_to_sub_query`.
6770

6871
---
6972

@@ -82,13 +85,17 @@ Not mapped: `shown`, `title`, `propagate_filters_to_sub_query`, `case` labels.
8285
| `sql: ${CUBE}.column` | Supported (normalized to `{model}` placeholder) |
8386
| `sql: ${dimension_ref}` (referencing a dimension) | Supported (preserved for resolution) |
8487
| `description` | Supported |
88+
| `title` | Supported (stored as `Metric.label`) |
8589
| `format` | Supported (stored on `Metric.format`) |
86-
| `type: rank` | Partial support: parses without error but rank semantics (`order_by`, `reduce_by`) are not stored. Becomes a regular measure with `agg_type=count` (default fallback). |
90+
| `shown` / `public` | Supported (stored as `Metric.public`) |
91+
| `meta` | Supported (stored on `Metric.meta`) |
92+
| `drill_members` | Supported (stored as `Metric.drill_fields`) |
93+
| `type: rank` | Partial support: stored as `type=derived` with rank semantics (`order_by`, `reduce_by`) preserved in `Metric.meta["cube_type"]`. Does not execute rank window function. |
8794
| `type: string` | Unsupported (no mapping; falls back to `count`) |
8895
| `type: boolean` | Unsupported (no mapping; falls back to `count`) |
8996
| `type: running_total` | Unsupported (no mapping) |
9097

91-
Not mapped: `shown`, `title`, `drill_members` (on import; used on export), `meta`, `drill_filters`.
98+
Not mapped: `drill_filters`.
9299

93100
---
94101

@@ -125,7 +132,7 @@ Derived measures are detected by `type: number` and handled with several strateg
125132
| `rolling_window: { trailing: "1 month" }` | Supported (trailing value stored in `Metric.window`) |
126133
| `rolling_window: { offset: end }` | Partial support: the `offset` value is not stored. Only `trailing` is captured. |
127134
| `rolling_window: { leading: "-1 month" }` | Partial support: the `leading` value is not stored. |
128-
| `rolling_window: { type: to_date, granularity: year }` | Partial support: treated as cumulative. The `type` and `granularity` sub-fields are not stored. |
135+
| `rolling_window: { type: to_date, granularity: year }` | Supported (maps to `Metric.grain_to_date`, e.g., `grain_to_date="year"` for YTD) |
129136

130137
---
131138

@@ -137,6 +144,7 @@ Derived measures are detected by `type: number` and handled with several strateg
137144
| `${CUBE}` / `{CUBE}` replacement in segment SQL | Supported |
138145
| `${cube_name}` replacement in segment SQL | Supported |
139146
| `description` | Supported |
147+
| `shown` / `public` | Supported (stored as `Segment.public`) |
140148
| Query-time segment application | Supported |
141149
| Segments without `sql:` | Supported (correctly skipped, not added) |
142150

@@ -191,24 +199,38 @@ Pre-aggregations are fully mapped to Sidemantic's `PreAggregation` model, includ
191199

192200
## Views
193201

194-
Unsupported. The `views:` top-level section in Cube YAML is silently ignored during parsing. Files that contain only views (no `cubes:` section) parse without error and produce an empty graph.
202+
Supported. The `views:` top-level section in Cube YAML is parsed after all cubes are loaded. Each view is resolved into a composite Model by projecting members from source cubes.
195203

196-
Cube views are a composition layer that project and rename members from cubes via `join_path`, `includes`, `excludes`, `prefix`, and `alias`. None of these concepts are mapped:
197-
198-
- `join_path` traversal
199-
- `includes: "*"` wildcard and selective includes
200-
- `excludes` list
201-
- `prefix: true` namespacing
202-
- `alias` renaming
203-
- View-level `access_policy`
204-
- `folders` (grouping members in views)
205-
- `extends` on views
204+
| Feature | Status |
205+
|---------|--------|
206+
| `join_path: cube_name` (single cube) | Supported (resolves to target cube by last segment of path) |
207+
| `join_path: cube_a.cube_b` (multi-level) | Supported (resolves to the last cube in the path) |
208+
| `includes: "*"` (wildcard) | Supported (imports all dimensions and metrics from source cube) |
209+
| `includes:` (selective list) | Supported (imports named dimensions and metrics) |
210+
| `includes:` with `{ name, alias }` | Supported (renames members via `alias`) |
211+
| `excludes:` list | Supported (removes named members from includes) |
212+
| `prefix: true` | Supported (prefixes member names with `{cube_name}_`) |
213+
| `alias` on cube entry | Supported (used as prefix when `prefix: true`) |
214+
| View-only files (no `cubes:` section) | Supported (views that reference cubes from other files resolve correctly when parsed from a directory) |
215+
| Empty view (no resolvable cubes) | Supported (silently skipped, no model created) |
216+
| `extends` on views | Unsupported |
217+
| `folders` | Unsupported (UI grouping concept) |
218+
| View-level `access_policy` | Unsupported |
219+
220+
View models are marked with `meta={"cube_type": "view"}` and are excluded from Cube export.
206221

207222
---
208223

209224
## Hierarchies
210225

211-
Unsupported. `hierarchies:` blocks on cubes are parsed by YAML without error but not stored. Hierarchies define drill-down level ordering (e.g., year -> quarter -> month) and cross-cube level references. They have no Sidemantic equivalent.
226+
Supported. `hierarchies:` blocks on cubes are parsed and used to set `Dimension.parent` chains.
227+
228+
| Feature | Status |
229+
|---------|--------|
230+
| `hierarchies: [{ name, levels }]` | Supported (level ordering sets `Dimension.parent` on each child level) |
231+
| Multiple hierarchies per cube | Supported |
232+
| Cross-cube level references (e.g., `users.city`) | Partial support: cross-cube references (containing dots) are silently skipped. Only same-cube levels are linked. |
233+
| `title` on hierarchies | Not stored (used for display only) |
212234

213235
---
214236

@@ -226,19 +248,27 @@ Unsupported. Access policy blocks on cubes and views are parsed by YAML without
226248

227249
## Multi-Stage Calculations
228250

229-
Unsupported. The `multi_stage: true` flag on measures is parsed by YAML without error but not stored. Multi-stage calculations in Cube enable measures that reference other measures as inputs, run in separate query stages, and support features like `group_by` (for percent-of-total) and `time_shift` (for period comparisons). The adapter parses the measure's `sql` and `type` normally, so the measure still appears in the graph, but multi-stage execution semantics are not reproduced.
251+
Partial support. The `multi_stage: true` flag on measures is parsed by YAML without error but not stored. Multi-stage calculations in Cube enable measures that reference other measures as inputs, run in separate query stages, and support features like `group_by` (for percent-of-total) and `time_shift` (for period comparisons). The adapter parses the measure's `sql` and `type` normally, so the measure still appears in the graph, but multi-stage execution semantics are not reproduced.
230252

231-
Related unsupported sub-features:
232-
- `time_shift: [{ time_dimension, interval, type }]` on measures
233-
- `group_by` (percent-of-total grouping)
234-
- `order_by` / `reduce_by` (ranking)
235-
- `case` / `switch` / `when` / `else` on measures
253+
| Feature | Status |
254+
|---------|--------|
255+
| `time_shift: [{ time_dimension, interval, type: prior }]` | Supported (maps to `Metric.type="time_comparison"` with `comparison_type` and `time_offset`) |
256+
| `group_by` (percent-of-total grouping) | Unsupported |
257+
| `order_by` / `reduce_by` (ranking) | Partial support: stored in `Metric.meta` for `type: rank` measures |
258+
| `case` / `switch` / `when` / `else` on measures | Unsupported |
236259

237260
---
238261

239262
## Custom Calendars and Granularities
240263

241-
Unsupported. Cube supports custom calendar cubes (`calendar: true`) with custom granularity definitions on time dimensions (`granularities: [{ name, sql, interval, origin }]`) and dimension-level `time_shift` definitions. The adapter parses these structures without error but does not store them. Time dimensions always default to `granularity: day`.
264+
Partial support. Cube supports custom calendar cubes (`calendar: true`) with custom granularity definitions on time dimensions. The `calendar: true` flag is not stored, but custom granularities are now parsed.
265+
266+
| Feature | Status |
267+
|---------|--------|
268+
| `granularities: [{ name, ... }]` on time dimensions | Supported (granularity names stored in `Dimension.supported_granularities`) |
269+
| `granularities[].sql`, `interval`, `origin` | Unsupported (only the name is stored) |
270+
| `calendar: true` on cubes | Partial support (parsed without error, not stored) |
271+
| Dimension-level `time_shift` | Unsupported |
242272

243273
---
244274

@@ -268,21 +298,30 @@ Sidemantic can export its semantic model back to Cube YAML format.
268298
| Cubes with `sql_table` | Supported |
269299
| Cubes with `sql` (inline query) | Supported |
270300
| `description` | Supported |
301+
| `meta` on cubes | Supported |
271302
| Dimensions (string, number, time, boolean) | Supported (`{model}` mapped back to Cube types) |
272303
| `primary_key: true` on dimensions | Supported |
273304
| `format` on dimensions | Supported |
305+
| `title` on dimensions | Supported (exported from `Dimension.label`) |
306+
| `meta` on dimensions | Supported |
307+
| `shown: false` on dimensions | Supported (exported when `Dimension.public` is False) |
274308
| Standard measures (count, count_distinct, sum, avg, min, max) | Supported |
275309
| Derived measures (`type: number`) | Supported |
276310
| Ratio metrics | Supported (exported as `type: number` with `${numerator}::float / NULLIF(${denominator}, 0)`) |
277-
| Cumulative metrics | Supported (exported with `rolling_window: { trailing }`) |
311+
| Cumulative metrics | Supported (exported with `rolling_window: { trailing }` or `rolling_window: { type: to_date, granularity }`) |
278312
| Time comparison metrics | Partial support: exported as `type: number` with a description annotation; no `time_shift` block generated. |
279313
| Measures with filters | Supported (exported as `filters: [{ sql }]`) |
280314
| `format` on measures | Supported |
281-
| `drill_members` on measures | Supported (exported from hierarchy dimensions when available) |
315+
| `title` on measures | Supported (exported from `Metric.label`) |
316+
| `meta` on measures | Supported |
317+
| `shown: false` on measures | Supported (exported when `Metric.public` is False) |
318+
| `drill_members` on measures | Supported (exported from `Metric.drill_fields` or hierarchy dimensions) |
282319
| Segments | Supported (`{model}` replaced back with `${CUBE}`) |
283-
| Joins (many_to_one) | Supported (generates `sql` join expression from foreign key and primary key) |
284-
| Joins (one_to_many, one_to_one) | Partial support: only `many_to_one` relationships are exported as joins. Other relationship types are omitted. |
285-
| Pre-aggregations | Unsupported (not exported) |
320+
| `shown: false` on segments | Supported (exported when `Segment.public` is False) |
321+
| Joins (many_to_one, one_to_one) | Supported (generates `${CUBE}.fk = ${target}.pk` join SQL) |
322+
| Joins (one_to_many) | Supported (generates `${CUBE}.pk = ${target}.fk` with swapped direction) |
323+
| Joins (many_to_many) | Unsupported (skipped; requires junction table info) |
324+
| Pre-aggregations | Supported (all fields exported including refresh_key, indexes, build_range) |
286325
| Model inheritance resolution | Supported (inheritance resolved before export) |
287-
| Roundtrip fidelity (Cube -> parse -> export -> re-parse) | Supported for dimensions, metrics, and segments. Relationships are not fully round-tripped due to the export limitation above. |
288-
326+
| View models | Supported (skipped during export, identified by `meta.cube_type == "view"`) |
327+
| Roundtrip fidelity (Cube -> parse -> export -> re-parse) | Supported for dimensions, metrics, segments, and pre-aggregations. |

0 commit comments

Comments
 (0)