semantic-query-compiler 0.1.0__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.
- semantic_query_compiler-0.1.0/.github/workflows/ci.yml +26 -0
- semantic_query_compiler-0.1.0/.gitignore +8 -0
- semantic_query_compiler-0.1.0/CHANGELOG.md +32 -0
- semantic_query_compiler-0.1.0/PKG-INFO +565 -0
- semantic_query_compiler-0.1.0/README.md +533 -0
- semantic_query_compiler-0.1.0/docs/architecture.md +205 -0
- semantic_query_compiler-0.1.0/docs/support-matrix.md +37 -0
- semantic_query_compiler-0.1.0/docs/validation.md +142 -0
- semantic_query_compiler-0.1.0/examples/quickstart/README.md +37 -0
- semantic_query_compiler-0.1.0/examples/quickstart/request.json +6 -0
- semantic_query_compiler-0.1.0/examples/quickstart/semantic.yml +63 -0
- semantic_query_compiler-0.1.0/examples/quickstart/setup.sql +28 -0
- semantic_query_compiler-0.1.0/pyproject.toml +57 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/__init__.py +11 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/cli.py +757 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/column_registry.py +216 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/compiler.py +1632 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/dbt_manifest.py +70 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/dialects.py +93 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/loader.py +68 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/models.py +231 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/plan.py +42 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/result_compare.py +128 -0
- semantic_query_compiler-0.1.0/src/semantic_query_compiler/validator.py +539 -0
- semantic_query_compiler-0.1.0/tests/fixtures/column_registry.json +31 -0
- semantic_query_compiler-0.1.0/tests/fixtures/semantic_layer.yml +116 -0
- semantic_query_compiler-0.1.0/tests/test_column_registry.py +104 -0
- semantic_query_compiler-0.1.0/tests/test_compare_metric.py +125 -0
- semantic_query_compiler-0.1.0/tests/test_compile.py +1008 -0
- semantic_query_compiler-0.1.0/tests/test_dbt_manifest.py +97 -0
- semantic_query_compiler-0.1.0/tests/test_dialect_matrix.py +50 -0
- semantic_query_compiler-0.1.0/tests/test_duckdb_execution.py +428 -0
- semantic_query_compiler-0.1.0/tests/test_result_compare.py +67 -0
- semantic_query_compiler-0.1.0/tests/test_validate.py +298 -0
- semantic_query_compiler-0.1.0/uv.lock +859 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
push:
|
|
6
|
+
branches: [master, main]
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v7
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
run: uv python install 3.12
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: uv sync --extra dev
|
|
23
|
+
- name: Lint
|
|
24
|
+
run: uv run ruff check .
|
|
25
|
+
- name: Test
|
|
26
|
+
run: uv run pytest
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - beta
|
|
4
|
+
|
|
5
|
+
Initial beta release of `semantic-query-compiler`.
|
|
6
|
+
|
|
7
|
+
### Included
|
|
8
|
+
|
|
9
|
+
- YAML semantic model loader with `semantic`, `model`, `semantic_layer`, `modules`, and single `module` roots.
|
|
10
|
+
- CLI commands for model inspection, validation, SQL compilation, result comparison, and column-registry generation.
|
|
11
|
+
- BigQuery, DuckDB, Postgres, and Snowflake SQL rendering.
|
|
12
|
+
- Aggregate, count, count-distinct, ratio, custom-value, custom-ratio, and cross-module derived-ratio metrics.
|
|
13
|
+
- Metric filters, query filters, named slices, breakdowns, joined breakdowns, and safe multi-hop joins over `many-to-one` / `one-to-one` relationships.
|
|
14
|
+
- Period shortcuts including closed trailing periods such as `last 24 complete months`.
|
|
15
|
+
- Time comparison, cumulative windows, targets, target comparisons, and ratio target handling.
|
|
16
|
+
- Custom SQL parsing/transpilation with optional function allowlists.
|
|
17
|
+
- Column registry generation from `information_schema` JSON/JSONL and dbt `manifest.json`.
|
|
18
|
+
- Result comparison harness for DuckDB and BigQuery.
|
|
19
|
+
- Fresh-install DuckDB quickstart example.
|
|
20
|
+
|
|
21
|
+
### Support level
|
|
22
|
+
|
|
23
|
+
- BigQuery: execution-tested and used as the primary warehouse validation target.
|
|
24
|
+
- DuckDB: execution-tested, including copied real-model data checks across all metrics and key request shapes.
|
|
25
|
+
- Postgres and Snowflake: compile/parse-tested; execution validation is not part of this beta.
|
|
26
|
+
|
|
27
|
+
### Known limits
|
|
28
|
+
|
|
29
|
+
- This is a beta, not a production-trust/v1 release.
|
|
30
|
+
- Dashboard/export parity against trusted business reports is still required before relying on a migrated model for production decisions.
|
|
31
|
+
- Postgres and Snowflake support should be treated as SQL rendering support until execution tests are added.
|
|
32
|
+
- The compiler owns SQL generation and comparison helpers; warehouse execution is intentionally left to caller tooling.
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: semantic-query-compiler
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Agent-friendly semantic metric compiler with dialect-aware SQL rendering
|
|
5
|
+
Project-URL: Homepage, https://github.com/rasmusengelbrecht/semantic-query-compiler
|
|
6
|
+
Project-URL: Repository, https://github.com/rasmusengelbrecht/semantic-query-compiler
|
|
7
|
+
Project-URL: Issues, https://github.com/rasmusengelbrecht/semantic-query-compiler/issues
|
|
8
|
+
Author: Rasmus Engelbrecht
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: analytics,bigquery,duckdb,metrics,semantic-layer,sql
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Information Technology
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Database
|
|
20
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: duckdb>=1.1
|
|
23
|
+
Requires-Dist: pydantic<2.12,>=2.7
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
25
|
+
Requires-Dist: sqlglot>=30.0
|
|
26
|
+
Provides-Extra: bigquery
|
|
27
|
+
Requires-Dist: google-cloud-bigquery>=3.0; extra == 'bigquery'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# semantic-query-compiler
|
|
34
|
+
|
|
35
|
+
Agent-friendly semantic metric compiler with dialect-aware SQL rendering.
|
|
36
|
+
|
|
37
|
+
`semantic-query-compiler` turns governed YAML metric definitions into inspectable SQL for warehouses such as BigQuery, DuckDB, Postgres, and Snowflake. It is designed for CLI and agent workflows: discover metrics, validate a model, compile SQL, explain the query plan, and compare results against trusted SQL when you need confidence.
|
|
38
|
+
|
|
39
|
+
The compiler is intentionally separate from warehouse execution. It owns semantic definitions, validation, planning, SQL rendering, and comparison helpers. Run compiled SQL with your warehouse client, BI tool, notebook, dbt workflow, or agent runtime.
|
|
40
|
+
|
|
41
|
+
Current status: `0.1.0` beta. BigQuery and DuckDB have execution coverage; Postgres and Snowflake are compile/parse-tested. See `docs/support-matrix.md` for support levels and v1 release gates.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
From a checkout:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv venv
|
|
49
|
+
uv pip install -e '.[dev]'
|
|
50
|
+
semantic --help
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
From GitHub:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
uv tool install 'semantic-query-compiler @ git+https://github.com/rasmusengelbrecht/semantic-query-compiler.git'
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Optional BigQuery comparison support needs the BigQuery extra or dependency:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uv pip install -e '.[bigquery]'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Quickstart
|
|
66
|
+
|
|
67
|
+
Create starter files:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
semantic init --dir demo-semantic --with-duckdb-example
|
|
71
|
+
cd demo-semantic
|
|
72
|
+
semantic validate --model semantic.yml --check-compilable
|
|
73
|
+
semantic compile revenue --model semantic.yml --period "current month" --request request.json --format sql
|
|
74
|
+
semantic compare-metric revenue \
|
|
75
|
+
--model semantic.yml \
|
|
76
|
+
--period "current month" \
|
|
77
|
+
--request request.json \
|
|
78
|
+
--reference reference_revenue.sql \
|
|
79
|
+
--dialect duckdb \
|
|
80
|
+
--engine duckdb \
|
|
81
|
+
--setup setup.sql
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
A runnable DuckDB example lives in `examples/quickstart/`.
|
|
85
|
+
|
|
86
|
+
## Define a semantic model
|
|
87
|
+
|
|
88
|
+
Create a YAML file, for example `semantic.yml`. The recommended root key is `semantic`; `model` and `semantic_layer` are also accepted root keys.
|
|
89
|
+
|
|
90
|
+
```yaml
|
|
91
|
+
semantic:
|
|
92
|
+
version: 1
|
|
93
|
+
modules:
|
|
94
|
+
- identifier: bookings
|
|
95
|
+
project: demo-project
|
|
96
|
+
schema: analytics
|
|
97
|
+
table: bookings
|
|
98
|
+
dimensions:
|
|
99
|
+
- column: country
|
|
100
|
+
type: categorical
|
|
101
|
+
- column: channel
|
|
102
|
+
type: categorical
|
|
103
|
+
metrics:
|
|
104
|
+
- identifier: booking_count
|
|
105
|
+
name: Booking Count
|
|
106
|
+
calculation: count
|
|
107
|
+
time: bookings.created_at
|
|
108
|
+
filters:
|
|
109
|
+
- column: state
|
|
110
|
+
operator: in
|
|
111
|
+
expression: confirmed,completed,cancelled
|
|
112
|
+
slices:
|
|
113
|
+
- name: confirmed_only
|
|
114
|
+
filter:
|
|
115
|
+
column: state
|
|
116
|
+
operator: equals
|
|
117
|
+
expression: confirmed
|
|
118
|
+
dimensions:
|
|
119
|
+
- country
|
|
120
|
+
- channel
|
|
121
|
+
|
|
122
|
+
- identifier: revenue
|
|
123
|
+
name: Revenue
|
|
124
|
+
calculation: sum
|
|
125
|
+
time: bookings.created_at
|
|
126
|
+
value: bookings.revenue
|
|
127
|
+
filters:
|
|
128
|
+
- column: state
|
|
129
|
+
operator: in
|
|
130
|
+
expression: confirmed,completed,cancelled
|
|
131
|
+
dimensions:
|
|
132
|
+
- country
|
|
133
|
+
- channel
|
|
134
|
+
|
|
135
|
+
- identifier: average_daily_rate
|
|
136
|
+
name: Average Daily Rate
|
|
137
|
+
calculation: ratio
|
|
138
|
+
time: bookings.created_at
|
|
139
|
+
numerator: bookings.gross_booking_value
|
|
140
|
+
denominator: bookings.nights
|
|
141
|
+
dimensions:
|
|
142
|
+
- country
|
|
143
|
+
|
|
144
|
+
- identifier: request_success_rate
|
|
145
|
+
name: Request Success Rate
|
|
146
|
+
calculation: custom-ratio
|
|
147
|
+
time: bookings.created_at
|
|
148
|
+
numerator_sql: COUNT(CASE WHEN requested_at IS NOT NULL AND state IN ('confirmed', 'completed') THEN id END)
|
|
149
|
+
denominator_sql: COUNT(CASE WHEN requested_at IS NOT NULL THEN id END)
|
|
150
|
+
allowed_functions: [COUNT, IF]
|
|
151
|
+
dimensions:
|
|
152
|
+
- country
|
|
153
|
+
|
|
154
|
+
- identifier: ad_spend
|
|
155
|
+
project: demo-project
|
|
156
|
+
schema: analytics
|
|
157
|
+
table: marketing_costs
|
|
158
|
+
dimensions:
|
|
159
|
+
- column: country
|
|
160
|
+
type: categorical
|
|
161
|
+
metrics:
|
|
162
|
+
- identifier: marketing_spend
|
|
163
|
+
name: Marketing Spend
|
|
164
|
+
calculation: sum
|
|
165
|
+
time: marketing_costs.date
|
|
166
|
+
value: marketing_costs.cost
|
|
167
|
+
dimensions:
|
|
168
|
+
- country
|
|
169
|
+
|
|
170
|
+
cross_module_metrics:
|
|
171
|
+
- identifier: roas
|
|
172
|
+
name: ROAS
|
|
173
|
+
calculation: derived-ratio
|
|
174
|
+
numerator_metric: revenue
|
|
175
|
+
denominator_metric: marketing_spend
|
|
176
|
+
join_on: time
|
|
177
|
+
join_type: inner
|
|
178
|
+
dimensions:
|
|
179
|
+
- country
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Supported metric calculations:
|
|
183
|
+
|
|
184
|
+
- `sum`
|
|
185
|
+
- `count`
|
|
186
|
+
- `count-distinct`
|
|
187
|
+
- `ratio` as safe `SUM(numerator) / SUM(denominator)`
|
|
188
|
+
- `custom-value`
|
|
189
|
+
- `custom-ratio`
|
|
190
|
+
- cross-module `derived-ratio`
|
|
191
|
+
|
|
192
|
+
Supported metric and slice filter operators:
|
|
193
|
+
|
|
194
|
+
- `equals`, `not-equals`
|
|
195
|
+
- `is`, `is-not`
|
|
196
|
+
- `in`, `not-in`
|
|
197
|
+
- `like`, `not-like`
|
|
198
|
+
- `greater-than`, `greater-than-or-equal`
|
|
199
|
+
- `less-than`, `less-than-or-equal`
|
|
200
|
+
- `null`, `not-null`, `is-null`, `is-not-null`
|
|
201
|
+
|
|
202
|
+
## Create a request shape
|
|
203
|
+
|
|
204
|
+
For CLI use, prefer `--period` for the date window and keep the request file focused on query shape:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"timeGrain": "monthly",
|
|
209
|
+
"breakdownDimensionIds": ["country"],
|
|
210
|
+
"filters": [
|
|
211
|
+
{ "dimensionId": "country", "filterValues": ["DK"] }
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Then compile with a named period:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
semantic compile revenue \
|
|
220
|
+
--model semantic.yml \
|
|
221
|
+
--period "last 24 complete months" \
|
|
222
|
+
--request request.json \
|
|
223
|
+
--dialect bigquery
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Supported request features:
|
|
227
|
+
|
|
228
|
+
- `timeGrain`: `none`, `daily`, `weekly`, `monthly`, `quarterly`, `yearly`
|
|
229
|
+
- `breakdownDimensionIds`
|
|
230
|
+
- `sliceName` for named metric slice filters
|
|
231
|
+
- query filters
|
|
232
|
+
- `timeComparison`: `YoY`, `MoM`, `Last Year`, `Last Month`, `YoY Match Weekday`
|
|
233
|
+
- `cumulativeMode`: `ytd`, `mtd`, `rolling_days`
|
|
234
|
+
- `targetSeries` with explicit target table mapping
|
|
235
|
+
- `excludeOpenPeriod` and `excludeToday` period clipping
|
|
236
|
+
|
|
237
|
+
For API use, or when you need exact boundaries, `fromDate` and `toDate` can still be provided directly in the request JSON.
|
|
238
|
+
|
|
239
|
+
Supported period shortcuts:
|
|
240
|
+
|
|
241
|
+
- `current year` (year-to-date)
|
|
242
|
+
- `current month` (month-to-date)
|
|
243
|
+
- `last N days|weeks|months|quarters|years`
|
|
244
|
+
- `last N complete days|weeks|months|quarters|years`
|
|
245
|
+
- `Q1 2026`
|
|
246
|
+
- `2026`
|
|
247
|
+
- `01-2026`
|
|
248
|
+
|
|
249
|
+
For trailing closed-period reporting, prefer `complete` periods. Example: `--period "last 24 complete months"` returns 24 closed monthly buckets. For current-period-to-date reporting, combine a relative period with `excludeOpenPeriod` in the request. Example: current-year YTD through the last complete month:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"timeGrain": "monthly",
|
|
254
|
+
"cumulativeMode": "ytd",
|
|
255
|
+
"timeComparison": "YoY",
|
|
256
|
+
"targetSeries": "budget_current",
|
|
257
|
+
"excludeOpenPeriod": true
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Compile SQL
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
semantic compile revenue \
|
|
265
|
+
--model semantic.yml \
|
|
266
|
+
--period "last 24 complete months" \
|
|
267
|
+
--request request.json \
|
|
268
|
+
--dialect bigquery \
|
|
269
|
+
--format sql
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Explain the generated plan:
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
semantic compile revenue \
|
|
276
|
+
--model semantic.yml \
|
|
277
|
+
--period "current year" \
|
|
278
|
+
--request request.json \
|
|
279
|
+
--dialect bigquery \
|
|
280
|
+
--format explain
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Write SQL to a file:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
semantic compile revenue \
|
|
287
|
+
--model semantic.yml \
|
|
288
|
+
--period "current year" \
|
|
289
|
+
--request request.json \
|
|
290
|
+
--dialect bigquery \
|
|
291
|
+
--output query.sql
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Inspect and validate a model
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
semantic init --dir demo-semantic --with-duckdb-example
|
|
298
|
+
|
|
299
|
+
semantic metrics --model semantic.yml --format json
|
|
300
|
+
semantic search-metrics "gross booking value" --model semantic.yml --format json
|
|
301
|
+
semantic describe revenue --model semantic.yml --format json
|
|
302
|
+
semantic joins --model semantic.yml --format json
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`metrics`, `search-metrics`, and `describe` include discovery metadata such as descriptions, `ai_context` synonyms, teams, generic metadata, and relationship/formula notes when present in the model.
|
|
306
|
+
|
|
307
|
+
Run validation:
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
semantic validate \
|
|
311
|
+
--model semantic.yml \
|
|
312
|
+
--check-compilable \
|
|
313
|
+
--format json
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Optional: for stronger governance, validate against a column registry. This catches model drift such as missing tables/columns, stale warehouse metadata, and unsafe joins when uniqueness metadata is available:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
semantic validate \
|
|
320
|
+
--model semantic.yml \
|
|
321
|
+
--column-registry column_registry.json \
|
|
322
|
+
--max-registry-age-hours 24 \
|
|
323
|
+
--require-join-uniqueness \
|
|
324
|
+
--check-compilable \
|
|
325
|
+
--format json
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
A registry can be generated from warehouse `information_schema` exports or a dbt `manifest.json` when you have those available:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
semantic registry-from-information-schema \
|
|
332
|
+
--input information_schema_columns.jsonl \
|
|
333
|
+
--warehouse warehouse-prod \
|
|
334
|
+
--output column_registry.json
|
|
335
|
+
|
|
336
|
+
semantic registry-from-dbt-manifest \
|
|
337
|
+
--input target/manifest.json \
|
|
338
|
+
--warehouse warehouse-prod \
|
|
339
|
+
--output column_registry.json
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Targets
|
|
343
|
+
|
|
344
|
+
Targets are optional benchmark/budget/goal values that can be joined onto metric results. They live outside the semantic model in a normal warehouse table. The compiler only needs to know which table to read and how its columns map to the generic target contract.
|
|
345
|
+
|
|
346
|
+
A simple unsegmented target table has one row per metric, target series, and time period:
|
|
347
|
+
|
|
348
|
+
```sql
|
|
349
|
+
CREATE TABLE analytics.metric_targets (
|
|
350
|
+
metric_id TEXT,
|
|
351
|
+
target_series TEXT,
|
|
352
|
+
metric_time DATE,
|
|
353
|
+
target_value DOUBLE
|
|
354
|
+
);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Example rows:
|
|
358
|
+
|
|
359
|
+
| metric_id | target_series | metric_time | target_value |
|
|
360
|
+
| --- | --- | --- | ---: |
|
|
361
|
+
| revenue | budget_current | 2026-01-01 | 125000 |
|
|
362
|
+
| revenue | budget_current | 2026-02-01 | 130000 |
|
|
363
|
+
| booking_count | budget_current | 2026-01-01 | 420 |
|
|
364
|
+
|
|
365
|
+
Then request a target series:
|
|
366
|
+
|
|
367
|
+
```json
|
|
368
|
+
{
|
|
369
|
+
"timeGrain": "monthly",
|
|
370
|
+
"targetSeries": "budget_current"
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Compile with the target table:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
semantic compile revenue \
|
|
378
|
+
--model semantic.yml \
|
|
379
|
+
--period "current year" \
|
|
380
|
+
--request target-request.json \
|
|
381
|
+
--target-table warehouse.analytics.metric_targets \
|
|
382
|
+
--format sql
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
The default target column contract is:
|
|
386
|
+
|
|
387
|
+
| Purpose | Default column |
|
|
388
|
+
| --- | --- |
|
|
389
|
+
| Metric identifier | `metric_id` |
|
|
390
|
+
| Target series / version | `target_series` |
|
|
391
|
+
| Target period start | `metric_time` |
|
|
392
|
+
| Target value | `target_value` |
|
|
393
|
+
| Breakdown dimensions | one column per grouped breakdown alias |
|
|
394
|
+
|
|
395
|
+
If your table uses different names, map them at compile time:
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
semantic compile revenue \
|
|
399
|
+
--model semantic.yml \
|
|
400
|
+
--period "current year" \
|
|
401
|
+
--request target-request.json \
|
|
402
|
+
--target-table warehouse.analytics.metric_targets \
|
|
403
|
+
--target-metric-column metric \
|
|
404
|
+
--target-series-column series \
|
|
405
|
+
--target-time-column time \
|
|
406
|
+
--target-value-column value \
|
|
407
|
+
--format sql
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
For breakdown targets, add one target column per breakdown dimension. The grain becomes one row per metric, target series, time period, and target dimension combination. For example, a monthly `revenue` target broken down by `country` and `channel` should have one row for each `(metric_id, target_series, metric_time, country, channel)` combination.
|
|
411
|
+
|
|
412
|
+
The target column should use the same name as the breakdown alias, or the dimension can set `target_column` in the semantic model:
|
|
413
|
+
|
|
414
|
+
```yaml
|
|
415
|
+
- column: country
|
|
416
|
+
type: categorical
|
|
417
|
+
target_column: market
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Example segmented target table:
|
|
421
|
+
|
|
422
|
+
```sql
|
|
423
|
+
CREATE TABLE analytics.metric_targets (
|
|
424
|
+
metric_id TEXT,
|
|
425
|
+
target_series TEXT,
|
|
426
|
+
metric_time DATE,
|
|
427
|
+
market TEXT,
|
|
428
|
+
channel TEXT,
|
|
429
|
+
target_value DOUBLE
|
|
430
|
+
);
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
Example segmented rows:
|
|
434
|
+
|
|
435
|
+
| metric_id | target_series | metric_time | market | channel | target_value |
|
|
436
|
+
| --- | --- | --- | --- | --- | ---: |
|
|
437
|
+
| revenue | budget_current | 2026-01-01 | DK | paid_search | 45000 |
|
|
438
|
+
| revenue | budget_current | 2026-01-01 | DK | organic | 30000 |
|
|
439
|
+
| revenue | budget_current | 2026-01-01 | SE | paid_search | 25000 |
|
|
440
|
+
| revenue | budget_current | 2026-01-01 | SE | organic | 25000 |
|
|
441
|
+
|
|
442
|
+
When compiling `breakdownDimensionIds: ["country", "channel"]`, the compiler joins target rows on `metric_id`, `target_series`, `metric_time`, `market`, and `channel`, then returns `target_value` and `target_comparison_percentage`. If the request does not include a breakdown, matching target rows are summed to the requested time grain.
|
|
443
|
+
|
|
444
|
+
If one physical target table stores multiple overlapping grains, pass each possible target dimension column with `--target-dimension-column`:
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
semantic compile revenue \
|
|
448
|
+
--model semantic.yml \
|
|
449
|
+
--period "current year" \
|
|
450
|
+
--request country-request.json \
|
|
451
|
+
--target-table warehouse.analytics.metric_targets \
|
|
452
|
+
--target-dimension-column market \
|
|
453
|
+
--target-dimension-column channel \
|
|
454
|
+
--format sql
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
The compiler then picks the lowest matching target grain for the requested breakdown before summing targets. This prevents duplicate totals when a table contains both total rows and segmented rows. Use `--target-null-column` only when you want to force a fixed-grain target by requiring a target dimension column to be `NULL`.
|
|
458
|
+
|
|
459
|
+
Targets can be combined with time comparison and cumulative modes. Target columns describe current-period target comparison; historical comparison columns describe historical metric comparison.
|
|
460
|
+
|
|
461
|
+
Ratio target semantics:
|
|
462
|
+
|
|
463
|
+
- additive metrics sum matching target rows
|
|
464
|
+
- same-module `ratio` metrics try to find matching numerator and denominator `sum` metrics with the same time/filter shape, then compute `SUM(numerator target) / SUM(denominator target)`
|
|
465
|
+
- `custom-ratio` metrics use the direct target metric aggregate (`SUM(value)`) because their numerator/denominator are arbitrary SQL snippets, not reusable metric IDs
|
|
466
|
+
- cross-module `derived-ratio` metrics compute `SUM(numerator metric target) / SUM(denominator metric target)`
|
|
467
|
+
- these component target semantics also apply when targets are combined with `timeComparison` or `cumulativeMode`
|
|
468
|
+
|
|
469
|
+
That means a direct target row for the ratio metric itself is not used when component target metrics are available. If a component target is missing, the ratio target is `NULL`.
|
|
470
|
+
|
|
471
|
+
Request filters are applied to target rows too. The compiler maps filter dimensions through `target_column` metadata before filtering the target table.
|
|
472
|
+
|
|
473
|
+
## Custom SQL governance
|
|
474
|
+
|
|
475
|
+
Custom metrics can use SQL snippets:
|
|
476
|
+
|
|
477
|
+
```yaml
|
|
478
|
+
- identifier: postgres_custom
|
|
479
|
+
name: Postgres Custom
|
|
480
|
+
calculation: custom-value
|
|
481
|
+
time: bookings.created_at
|
|
482
|
+
custom_sql_dialect: postgres
|
|
483
|
+
allowed_functions: [SUM, CAST]
|
|
484
|
+
sql_expression: "SUM(gross_booking_value::INT)"
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Behavior:
|
|
488
|
+
|
|
489
|
+
- snippets are parsed with SQLGlot
|
|
490
|
+
- `custom_sql_dialect` parses from a source dialect and renders into the requested target dialect
|
|
491
|
+
- bare columns are qualified with the base table alias
|
|
492
|
+
- `allowed_functions` is optional; when present, functions outside the allowlist fail compilation
|
|
493
|
+
|
|
494
|
+
SQLGlot models `CASE WHEN ... THEN ... END` branches as `IF`, so include `IF` in `allowed_functions` for governed CASE expressions.
|
|
495
|
+
|
|
496
|
+
## Compare results
|
|
497
|
+
|
|
498
|
+
Compare compiled SQL for one metric against reference SQL:
|
|
499
|
+
|
|
500
|
+
```bash
|
|
501
|
+
semantic compare-metric revenue \
|
|
502
|
+
--model semantic.yml \
|
|
503
|
+
--request request.json \
|
|
504
|
+
--reference reference.sql \
|
|
505
|
+
--engine bigquery \
|
|
506
|
+
--project my-gcp-project \
|
|
507
|
+
--location EU \
|
|
508
|
+
--format json
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Compare two arbitrary SQL statements:
|
|
512
|
+
|
|
513
|
+
```bash
|
|
514
|
+
semantic compare-results \
|
|
515
|
+
--left current.sql \
|
|
516
|
+
--right reference.sql \
|
|
517
|
+
--engine duckdb \
|
|
518
|
+
--setup setup.sql \
|
|
519
|
+
--format json
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Use this for adoption checks: compare compiler output to your own trusted SQL, dashboards, or finance-approved reports before relying on a metric in production.
|
|
523
|
+
|
|
524
|
+
See `docs/support-matrix.md` for current beta support levels and v1 release gates.
|
|
525
|
+
|
|
526
|
+
## Execute compiled SQL
|
|
527
|
+
|
|
528
|
+
The compiler does not own warehouse execution. Write SQL to a file and run it with your warehouse client, BI tool, notebook, dbt operation, or agent runtime:
|
|
529
|
+
|
|
530
|
+
```bash
|
|
531
|
+
semantic compile revenue \
|
|
532
|
+
--model semantic.yml \
|
|
533
|
+
--period "current year" \
|
|
534
|
+
--request request.json \
|
|
535
|
+
--dialect bigquery \
|
|
536
|
+
--output query.sql
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
## What is implemented
|
|
540
|
+
|
|
541
|
+
- YAML semantic model loader
|
|
542
|
+
- metric listing, metadata-aware search, description, validation, and join graph CLI
|
|
543
|
+
- SQL / JSON / explain output
|
|
544
|
+
- BigQuery, DuckDB, Postgres, and Snowflake dialect rendering
|
|
545
|
+
- aggregate, ratio, custom, and cross-module metrics
|
|
546
|
+
- safe multi-hop joins over `many-to-one` / `one-to-one` relationships
|
|
547
|
+
- metric filters, query filters, breakdowns, and time grains
|
|
548
|
+
- time comparisons
|
|
549
|
+
- cumulative windows
|
|
550
|
+
- target joins and target/comparison/cumulative combinations
|
|
551
|
+
- custom SQL source-dialect transpilation and function allowlists
|
|
552
|
+
- result comparison harness for DuckDB and BigQuery
|
|
553
|
+
- column registry generation from `information_schema` and dbt `manifest.json`
|
|
554
|
+
- dialect matrix tests that compile and parse representative BigQuery, DuckDB, Postgres, and Snowflake SQL
|
|
555
|
+
- CI lint/test gate
|
|
556
|
+
|
|
557
|
+
## Development
|
|
558
|
+
|
|
559
|
+
```bash
|
|
560
|
+
uv venv
|
|
561
|
+
uv pip install -e '.[dev]'
|
|
562
|
+
uv run ruff check .
|
|
563
|
+
uv run pytest
|
|
564
|
+
uv build
|
|
565
|
+
```
|