@bonnard/cli 0.1.3 → 0.1.5
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.
- package/dist/bin/bon.mjs +1919 -100
- package/dist/bin/models-IsV2sX74.mjs +76 -0
- package/dist/bin/{validate-Bd1D39Bj.mjs → validate-C4EHvJzJ.mjs} +47 -4
- package/dist/docs/README.md +78 -0
- package/dist/docs/_index.md +70 -0
- package/dist/docs/topics/cubes.data-source.md +92 -0
- package/dist/docs/topics/cubes.dimensions.format.md +195 -0
- package/dist/docs/topics/cubes.dimensions.md +184 -0
- package/dist/docs/topics/cubes.dimensions.primary-key.md +106 -0
- package/dist/docs/topics/cubes.dimensions.sub-query.md +174 -0
- package/dist/docs/topics/cubes.dimensions.time.md +111 -0
- package/dist/docs/topics/cubes.dimensions.types.md +107 -0
- package/dist/docs/topics/cubes.extends.md +149 -0
- package/dist/docs/topics/cubes.hierarchies.md +174 -0
- package/dist/docs/topics/cubes.joins.md +115 -0
- package/dist/docs/topics/cubes.md +117 -0
- package/dist/docs/topics/cubes.measures.calculated.md +99 -0
- package/dist/docs/topics/cubes.measures.drill-members.md +158 -0
- package/dist/docs/topics/cubes.measures.filters.md +86 -0
- package/dist/docs/topics/cubes.measures.format.md +153 -0
- package/dist/docs/topics/cubes.measures.md +162 -0
- package/dist/docs/topics/cubes.measures.rolling.md +119 -0
- package/dist/docs/topics/cubes.measures.types.md +122 -0
- package/dist/docs/topics/cubes.public.md +172 -0
- package/dist/docs/topics/cubes.refresh-key.md +153 -0
- package/dist/docs/topics/cubes.segments.md +121 -0
- package/dist/docs/topics/cubes.sql.md +61 -0
- package/dist/docs/topics/pre-aggregations.md +126 -0
- package/dist/docs/topics/pre-aggregations.rollup.md +162 -0
- package/dist/docs/topics/syntax.context-variables.md +153 -0
- package/dist/docs/topics/syntax.md +133 -0
- package/dist/docs/topics/syntax.references.md +174 -0
- package/dist/docs/topics/views.cubes.md +162 -0
- package/dist/docs/topics/views.folders.md +154 -0
- package/dist/docs/topics/views.includes.md +139 -0
- package/dist/docs/topics/views.md +138 -0
- package/dist/docs/topics/workflow.deploy.md +128 -0
- package/dist/docs/topics/workflow.mcp.md +100 -0
- package/dist/docs/topics/workflow.md +147 -0
- package/dist/docs/topics/workflow.query.md +198 -0
- package/dist/docs/topics/workflow.validate.md +152 -0
- package/dist/templates/claude/rules/bonnard.md +15 -0
- package/dist/templates/claude/settings.json +7 -0
- package/dist/templates/claude/skills/bonnard-cli/SKILL.md +59 -0
- package/dist/templates/claude/skills/bonnard-queries/SKILL.md +68 -0
- package/dist/templates/cursor/rules/bonnard-cli.mdc +47 -0
- package/dist/templates/cursor/rules/bonnard-queries.mdc +49 -0
- package/dist/templates/cursor/rules/bonnard.mdc +20 -0
- package/dist/templates/shared/bonnard.md +81 -0
- package/package.json +13 -8
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# cubes.dimensions
|
|
2
|
+
|
|
3
|
+
> Define attributes for grouping and filtering data.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Dimensions are the attributes used to slice and filter your data. They answer questions like "by what?", "which ones?", and "when?". Dimensions enable grouping measures and applying filters.
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
dimensions:
|
|
13
|
+
- name: id
|
|
14
|
+
type: number
|
|
15
|
+
sql: id
|
|
16
|
+
primary_key: true
|
|
17
|
+
description: Unique order identifier
|
|
18
|
+
|
|
19
|
+
- name: status
|
|
20
|
+
type: string
|
|
21
|
+
sql: status
|
|
22
|
+
description: Order status (pending, completed, cancelled)
|
|
23
|
+
|
|
24
|
+
- name: amount
|
|
25
|
+
type: number
|
|
26
|
+
sql: amount
|
|
27
|
+
description: Order amount in dollars
|
|
28
|
+
|
|
29
|
+
- name: is_active
|
|
30
|
+
type: boolean
|
|
31
|
+
sql: is_active
|
|
32
|
+
description: Whether the order is currently active
|
|
33
|
+
|
|
34
|
+
- name: created_at
|
|
35
|
+
type: time
|
|
36
|
+
sql: created_at
|
|
37
|
+
description: When the order was created
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Required Properties
|
|
41
|
+
|
|
42
|
+
| Property | Description |
|
|
43
|
+
|----------|-------------|
|
|
44
|
+
| `name` | Unique identifier in snake_case |
|
|
45
|
+
| `type` | Data type (string, number, time, etc.) |
|
|
46
|
+
| `sql` | SQL expression for the value |
|
|
47
|
+
|
|
48
|
+
## Optional Properties
|
|
49
|
+
|
|
50
|
+
| Property | Description |
|
|
51
|
+
|----------|-------------|
|
|
52
|
+
| `primary_key` | Marks as unique identifier (required for joins) |
|
|
53
|
+
| `title` | Human-readable display name |
|
|
54
|
+
| `description` | Documentation for consumers |
|
|
55
|
+
| `format` | Output format hints |
|
|
56
|
+
| `public` | Whether exposed in API (default: true) |
|
|
57
|
+
|
|
58
|
+
## Dimension Types
|
|
59
|
+
|
|
60
|
+
### string
|
|
61
|
+
|
|
62
|
+
Text values for categorization:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
- name: status
|
|
66
|
+
type: string
|
|
67
|
+
sql: status
|
|
68
|
+
|
|
69
|
+
- name: full_name
|
|
70
|
+
type: string
|
|
71
|
+
sql: "CONCAT(first_name, ' ', last_name)"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### number
|
|
75
|
+
|
|
76
|
+
Numeric values (not aggregated):
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
- name: quantity
|
|
80
|
+
type: number
|
|
81
|
+
sql: quantity
|
|
82
|
+
|
|
83
|
+
- name: unit_price
|
|
84
|
+
type: number
|
|
85
|
+
sql: price
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### boolean
|
|
89
|
+
|
|
90
|
+
True/false values:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
- name: is_active
|
|
94
|
+
type: boolean
|
|
95
|
+
sql: is_active
|
|
96
|
+
|
|
97
|
+
- name: has_subscription
|
|
98
|
+
type: boolean
|
|
99
|
+
sql: "subscription_id IS NOT NULL"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### time
|
|
103
|
+
|
|
104
|
+
Dates and timestamps (enables time-based analysis):
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
- name: created_at
|
|
108
|
+
type: time
|
|
109
|
+
sql: created_at
|
|
110
|
+
|
|
111
|
+
- name: order_date
|
|
112
|
+
type: time
|
|
113
|
+
sql: DATE(ordered_at)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### geo
|
|
117
|
+
|
|
118
|
+
Geographic coordinates:
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
- name: location
|
|
122
|
+
type: geo
|
|
123
|
+
latitude:
|
|
124
|
+
sql: lat
|
|
125
|
+
longitude:
|
|
126
|
+
sql: lng
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### switch
|
|
130
|
+
|
|
131
|
+
Enumerated values with labels:
|
|
132
|
+
|
|
133
|
+
```yaml
|
|
134
|
+
- name: status_label
|
|
135
|
+
type: switch
|
|
136
|
+
case:
|
|
137
|
+
when:
|
|
138
|
+
- sql: "{CUBE}.status = 'active'"
|
|
139
|
+
label: Active
|
|
140
|
+
- sql: "{CUBE}.status = 'inactive'"
|
|
141
|
+
label: Inactive
|
|
142
|
+
else:
|
|
143
|
+
label: Unknown
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Primary Key
|
|
147
|
+
|
|
148
|
+
Mark the unique identifier — required for joins and count_distinct:
|
|
149
|
+
|
|
150
|
+
```yaml
|
|
151
|
+
- name: id
|
|
152
|
+
type: number
|
|
153
|
+
sql: id
|
|
154
|
+
primary_key: true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Calculated Dimensions
|
|
158
|
+
|
|
159
|
+
Create derived attributes:
|
|
160
|
+
|
|
161
|
+
```yaml
|
|
162
|
+
- name: order_size
|
|
163
|
+
type: string
|
|
164
|
+
sql: >
|
|
165
|
+
CASE
|
|
166
|
+
WHEN {CUBE}.amount > 1000 THEN 'large'
|
|
167
|
+
WHEN {CUBE}.amount > 100 THEN 'medium'
|
|
168
|
+
ELSE 'small'
|
|
169
|
+
END
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Best Practices
|
|
173
|
+
|
|
174
|
+
1. **Always define a primary key** — enables joins and count_distinct
|
|
175
|
+
2. **Use appropriate types** — time for dates, string for categories
|
|
176
|
+
3. **Name clearly** — `customer_email` not `email1`
|
|
177
|
+
4. **Document business logic** — explain calculated dimensions
|
|
178
|
+
|
|
179
|
+
## See Also
|
|
180
|
+
|
|
181
|
+
- cubes.dimensions.types
|
|
182
|
+
- cubes.dimensions.primary-key
|
|
183
|
+
- cubes.dimensions.time
|
|
184
|
+
- cubes.joins
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# cubes.dimensions.primary-key
|
|
2
|
+
|
|
3
|
+
> Mark the unique identifier dimension for a cube.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `primary_key` property identifies the dimension that uniquely identifies each row. This is required for `count_distinct` measures and joins to work correctly.
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
cubes:
|
|
13
|
+
- name: users
|
|
14
|
+
sql: SELECT * FROM users
|
|
15
|
+
|
|
16
|
+
dimensions:
|
|
17
|
+
- name: id
|
|
18
|
+
type: number
|
|
19
|
+
sql: id
|
|
20
|
+
primary_key: true
|
|
21
|
+
|
|
22
|
+
- name: email
|
|
23
|
+
type: string
|
|
24
|
+
sql: email
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Why It Matters
|
|
28
|
+
|
|
29
|
+
### Enables count_distinct
|
|
30
|
+
|
|
31
|
+
Without a primary key, count_distinct may produce incorrect results:
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
measures:
|
|
35
|
+
- name: unique_users
|
|
36
|
+
type: count_distinct
|
|
37
|
+
sql: "{users.id}" # Requires primary_key on users.id
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Required for Joins
|
|
41
|
+
|
|
42
|
+
Cube uses primary keys to correctly join data:
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
cubes:
|
|
46
|
+
- name: orders
|
|
47
|
+
sql: SELECT * FROM orders
|
|
48
|
+
|
|
49
|
+
dimensions:
|
|
50
|
+
- name: id
|
|
51
|
+
type: number
|
|
52
|
+
sql: id
|
|
53
|
+
primary_key: true
|
|
54
|
+
|
|
55
|
+
joins:
|
|
56
|
+
- name: users
|
|
57
|
+
relationship: many_to_one
|
|
58
|
+
sql: "{CUBE}.user_id = {users.id}"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Affects Pre-aggregations
|
|
62
|
+
|
|
63
|
+
Primary keys determine how pre-aggregations handle duplicates.
|
|
64
|
+
|
|
65
|
+
## Rules
|
|
66
|
+
|
|
67
|
+
1. **At least one per cube** — required for joins and count_distinct to work
|
|
68
|
+
2. **Composite keys supported** — multiple dimensions can be marked as primary key
|
|
69
|
+
3. **Must be unique** — the combination of primary key values should not repeat
|
|
70
|
+
4. **Usually numeric** — but can be string (UUID, etc.)
|
|
71
|
+
5. **Auto-hides from API** — primary key dimensions default to `public: false`
|
|
72
|
+
|
|
73
|
+
## Common Patterns
|
|
74
|
+
|
|
75
|
+
### Auto-increment ID
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
- name: id
|
|
79
|
+
type: number
|
|
80
|
+
sql: id
|
|
81
|
+
primary_key: true
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### UUID
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
- name: id
|
|
88
|
+
type: string
|
|
89
|
+
sql: id
|
|
90
|
+
primary_key: true
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Composite Key (use concat)
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
- name: composite_id
|
|
97
|
+
type: string
|
|
98
|
+
sql: "CONCAT(order_id, '-', line_item_id)"
|
|
99
|
+
primary_key: true
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## See Also
|
|
103
|
+
|
|
104
|
+
- cubes.dimensions
|
|
105
|
+
- cubes.dimensions.types
|
|
106
|
+
- cubes.joins
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# cubes.dimensions.sub-query
|
|
2
|
+
|
|
3
|
+
> Bring measures from other cubes into a dimension.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Subquery dimensions allow you to reference measures from joined cubes as dimension values. This enables nested aggregations and bringing aggregate values into row-level context.
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
cubes:
|
|
13
|
+
- name: users
|
|
14
|
+
sql_table: users
|
|
15
|
+
|
|
16
|
+
dimensions:
|
|
17
|
+
- name: id
|
|
18
|
+
type: number
|
|
19
|
+
sql: id
|
|
20
|
+
primary_key: true
|
|
21
|
+
|
|
22
|
+
- name: name
|
|
23
|
+
type: string
|
|
24
|
+
sql: name
|
|
25
|
+
|
|
26
|
+
# Subquery dimension - brings order count as a user attribute
|
|
27
|
+
- name: order_count
|
|
28
|
+
type: number
|
|
29
|
+
sql: "{orders.count}"
|
|
30
|
+
sub_query: true
|
|
31
|
+
|
|
32
|
+
joins:
|
|
33
|
+
- name: orders
|
|
34
|
+
relationship: one_to_many
|
|
35
|
+
sql: "{CUBE}.id = {orders.user_id}"
|
|
36
|
+
|
|
37
|
+
- name: orders
|
|
38
|
+
sql_table: orders
|
|
39
|
+
|
|
40
|
+
measures:
|
|
41
|
+
- name: count
|
|
42
|
+
type: count
|
|
43
|
+
|
|
44
|
+
dimensions:
|
|
45
|
+
- name: id
|
|
46
|
+
type: number
|
|
47
|
+
sql: id
|
|
48
|
+
primary_key: true
|
|
49
|
+
|
|
50
|
+
- name: user_id
|
|
51
|
+
type: number
|
|
52
|
+
sql: user_id
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Properties
|
|
56
|
+
|
|
57
|
+
| Property | Description |
|
|
58
|
+
|----------|-------------|
|
|
59
|
+
| `sub_query: true` | Enables measure reference in dimension |
|
|
60
|
+
| `propagate_filters_to_sub_query` | Pass query filters to the subquery |
|
|
61
|
+
|
|
62
|
+
## Syntax
|
|
63
|
+
|
|
64
|
+
### Basic Subquery Dimension
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
dimensions:
|
|
68
|
+
- name: total_orders
|
|
69
|
+
type: number
|
|
70
|
+
sql: "{orders.count}"
|
|
71
|
+
sub_query: true
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### With Filter Propagation
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
dimensions:
|
|
78
|
+
- name: recent_order_count
|
|
79
|
+
type: number
|
|
80
|
+
sql: "{orders.count}"
|
|
81
|
+
sub_query: true
|
|
82
|
+
propagate_filters_to_sub_query: true
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
When `propagate_filters_to_sub_query: true`, filters applied in the main query also apply to the subquery aggregation.
|
|
86
|
+
|
|
87
|
+
## How It Works
|
|
88
|
+
|
|
89
|
+
Cube generates an optimized LEFT JOIN with an aggregation subquery:
|
|
90
|
+
|
|
91
|
+
```sql
|
|
92
|
+
SELECT
|
|
93
|
+
users.id,
|
|
94
|
+
users.name,
|
|
95
|
+
orders_subquery.count AS order_count
|
|
96
|
+
FROM users
|
|
97
|
+
LEFT JOIN (
|
|
98
|
+
SELECT user_id, COUNT(*) as count
|
|
99
|
+
FROM orders
|
|
100
|
+
GROUP BY user_id
|
|
101
|
+
) AS orders_subquery ON users.id = orders_subquery.user_id
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Use Cases
|
|
105
|
+
|
|
106
|
+
### User Metrics
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
# On users cube
|
|
110
|
+
dimensions:
|
|
111
|
+
- name: lifetime_order_count
|
|
112
|
+
type: number
|
|
113
|
+
sql: "{orders.count}"
|
|
114
|
+
sub_query: true
|
|
115
|
+
|
|
116
|
+
- name: lifetime_revenue
|
|
117
|
+
type: number
|
|
118
|
+
sql: "{orders.total_revenue}"
|
|
119
|
+
sub_query: true
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Product Statistics
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
# On products cube
|
|
126
|
+
dimensions:
|
|
127
|
+
- name: times_purchased
|
|
128
|
+
type: number
|
|
129
|
+
sql: "{order_items.count}"
|
|
130
|
+
sub_query: true
|
|
131
|
+
|
|
132
|
+
- name: total_quantity_sold
|
|
133
|
+
type: number
|
|
134
|
+
sql: "{order_items.total_quantity}"
|
|
135
|
+
sub_query: true
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Categorization Based on Aggregates
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
dimensions:
|
|
142
|
+
- name: order_count
|
|
143
|
+
type: number
|
|
144
|
+
sql: "{orders.count}"
|
|
145
|
+
sub_query: true
|
|
146
|
+
|
|
147
|
+
- name: customer_tier
|
|
148
|
+
type: string
|
|
149
|
+
sql: >
|
|
150
|
+
CASE
|
|
151
|
+
WHEN {order_count} >= 100 THEN 'platinum'
|
|
152
|
+
WHEN {order_count} >= 50 THEN 'gold'
|
|
153
|
+
WHEN {order_count} >= 10 THEN 'silver'
|
|
154
|
+
ELSE 'bronze'
|
|
155
|
+
END
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Requirements
|
|
159
|
+
|
|
160
|
+
1. **Join must exist** between the cubes
|
|
161
|
+
2. **Measure must exist** in the joined cube
|
|
162
|
+
3. **Primary key required** on both cubes
|
|
163
|
+
|
|
164
|
+
## Best Practices
|
|
165
|
+
|
|
166
|
+
1. **Use for commonly needed aggregates** — avoid creating subquery dimensions for rarely used metrics
|
|
167
|
+
2. **Consider performance** — subqueries add complexity to generated SQL
|
|
168
|
+
3. **Document the relationship** — make it clear where the data comes from
|
|
169
|
+
|
|
170
|
+
## See Also
|
|
171
|
+
|
|
172
|
+
- cubes.dimensions
|
|
173
|
+
- cubes.joins
|
|
174
|
+
- cubes.measures
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# cubes.dimensions.time
|
|
2
|
+
|
|
3
|
+
> Enable time-based analysis with time dimensions.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Time dimensions (`type: time`) enable powerful time-based analysis including granularity selection, date ranges, and rolling windows. They're essential for any time-series analytics.
|
|
8
|
+
|
|
9
|
+
## Example
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
dimensions:
|
|
13
|
+
- name: created_at
|
|
14
|
+
type: time
|
|
15
|
+
sql: created_at
|
|
16
|
+
|
|
17
|
+
- name: order_date
|
|
18
|
+
type: time
|
|
19
|
+
sql: DATE(ordered_at)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Time Granularities
|
|
23
|
+
|
|
24
|
+
When querying a time dimension, you can specify granularity:
|
|
25
|
+
|
|
26
|
+
| Granularity | Description |
|
|
27
|
+
|-------------|-------------|
|
|
28
|
+
| `second` | Group by second |
|
|
29
|
+
| `minute` | Group by minute |
|
|
30
|
+
| `hour` | Group by hour |
|
|
31
|
+
| `day` | Group by day |
|
|
32
|
+
| `week` | Group by week (Monday start) |
|
|
33
|
+
| `month` | Group by month |
|
|
34
|
+
| `quarter` | Group by quarter |
|
|
35
|
+
| `year` | Group by year |
|
|
36
|
+
|
|
37
|
+
## Query Usage
|
|
38
|
+
|
|
39
|
+
Time dimensions can be used in `timeDimensions` for special handling:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"measures": ["orders.count"],
|
|
44
|
+
"timeDimensions": [{
|
|
45
|
+
"dimension": "orders.created_at",
|
|
46
|
+
"granularity": "month",
|
|
47
|
+
"dateRange": ["2024-01-01", "2024-12-31"]
|
|
48
|
+
}]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Date Ranges
|
|
53
|
+
|
|
54
|
+
Common date range options:
|
|
55
|
+
|
|
56
|
+
- `"today"`, `"yesterday"`
|
|
57
|
+
- `"this week"`, `"last week"`
|
|
58
|
+
- `"this month"`, `"last month"`
|
|
59
|
+
- `"this year"`, `"last year"`
|
|
60
|
+
- `"last 7 days"`, `"last 30 days"`
|
|
61
|
+
- `["2024-01-01", "2024-12-31"]` (custom range)
|
|
62
|
+
|
|
63
|
+
## Best Practices
|
|
64
|
+
|
|
65
|
+
### Use Native Timestamps
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
# Good - preserves precision
|
|
69
|
+
- name: created_at
|
|
70
|
+
type: time
|
|
71
|
+
sql: created_at
|
|
72
|
+
|
|
73
|
+
# Avoid - loses time component
|
|
74
|
+
- name: created_at
|
|
75
|
+
type: time
|
|
76
|
+
sql: DATE(created_at)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Multiple Time Dimensions
|
|
80
|
+
|
|
81
|
+
A cube can have multiple time dimensions for different purposes:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
- name: created_at
|
|
85
|
+
type: time
|
|
86
|
+
sql: created_at
|
|
87
|
+
|
|
88
|
+
- name: shipped_at
|
|
89
|
+
type: time
|
|
90
|
+
sql: shipped_at
|
|
91
|
+
|
|
92
|
+
- name: delivered_at
|
|
93
|
+
type: time
|
|
94
|
+
sql: delivered_at
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Timezone Handling
|
|
98
|
+
|
|
99
|
+
For consistent timezone handling, convert in SQL:
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
- name: created_at
|
|
103
|
+
type: time
|
|
104
|
+
sql: "CONVERT_TIMEZONE('UTC', 'America/New_York', created_at)"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## See Also
|
|
108
|
+
|
|
109
|
+
- cubes.dimensions
|
|
110
|
+
- cubes.dimensions.types
|
|
111
|
+
- cubes.measures.rolling
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# cubes.dimensions.types
|
|
2
|
+
|
|
3
|
+
> The 6 dimension types for categorizing and filtering data.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Dimensions define the attributes used to group and filter data. Choose the appropriate type based on the data characteristics.
|
|
8
|
+
|
|
9
|
+
## Dimension Types
|
|
10
|
+
|
|
11
|
+
### string
|
|
12
|
+
Text values for categorization.
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
- name: status
|
|
16
|
+
type: string
|
|
17
|
+
sql: status
|
|
18
|
+
|
|
19
|
+
- name: full_name
|
|
20
|
+
type: string
|
|
21
|
+
sql: "CONCAT(first_name, ' ', last_name)"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### number
|
|
25
|
+
Numeric values (not aggregated).
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
- name: quantity
|
|
29
|
+
type: number
|
|
30
|
+
sql: quantity
|
|
31
|
+
|
|
32
|
+
- name: price
|
|
33
|
+
type: number
|
|
34
|
+
sql: unit_price
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### boolean
|
|
38
|
+
True/false values.
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
- name: is_active
|
|
42
|
+
type: boolean
|
|
43
|
+
sql: is_active
|
|
44
|
+
|
|
45
|
+
- name: has_orders
|
|
46
|
+
type: boolean
|
|
47
|
+
sql: "order_count > 0"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### time
|
|
51
|
+
Date and timestamp values (enables time-based analysis).
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
- name: created_at
|
|
55
|
+
type: time
|
|
56
|
+
sql: created_at
|
|
57
|
+
|
|
58
|
+
- name: order_date
|
|
59
|
+
type: time
|
|
60
|
+
sql: DATE(ordered_at)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### geo
|
|
64
|
+
Geographic coordinates (latitude/longitude).
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
- name: location
|
|
68
|
+
type: geo
|
|
69
|
+
latitude:
|
|
70
|
+
sql: lat
|
|
71
|
+
longitude:
|
|
72
|
+
sql: lng
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### switch
|
|
76
|
+
Dimension with predefined set of values.
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
- name: status_label
|
|
80
|
+
type: switch
|
|
81
|
+
sql: status
|
|
82
|
+
case:
|
|
83
|
+
when:
|
|
84
|
+
- sql: "{CUBE}.status = 'active'"
|
|
85
|
+
label: Active
|
|
86
|
+
- sql: "{CUBE}.status = 'inactive'"
|
|
87
|
+
label: Inactive
|
|
88
|
+
else:
|
|
89
|
+
label: Unknown
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Type Selection Guide
|
|
93
|
+
|
|
94
|
+
| Data | Type | Example |
|
|
95
|
+
|------|------|---------|
|
|
96
|
+
| Categories, names, IDs | `string` | status, country, user_id |
|
|
97
|
+
| Prices, quantities | `number` | price, quantity, score |
|
|
98
|
+
| Flags, toggles | `boolean` | is_active, has_subscription |
|
|
99
|
+
| Dates, timestamps | `time` | created_at, order_date |
|
|
100
|
+
| Coordinates | `geo` | location, delivery_point |
|
|
101
|
+
| Enumerated values | `switch` | status_label, tier_name |
|
|
102
|
+
|
|
103
|
+
## See Also
|
|
104
|
+
|
|
105
|
+
- cubes.dimensions
|
|
106
|
+
- cubes.dimensions.primary-key
|
|
107
|
+
- cubes.dimensions.time
|