@lightdash/cli 2.487.0 → 2.487.1
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.
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Custom SQL in data apps
|
|
2
|
+
|
|
3
|
+
Prefer existing modelled metrics and dimensions with the required business meaning. When custom SQL is necessary, use literal SQL strings and literal `table` and explore names, or simple constants that resolve to those strings. Define fields as plain objects in arrays. Named constants, imported constants, and spreads of literal arrays are supported; definitions do not have to be directly inline.
|
|
4
|
+
|
|
5
|
+
Lightdash records the exact SQL from each app version to authorize registered viewers who can access the app. The extractor does not execute JavaScript. Do not construct SQL or field definitions through helper factories, `.map()`, string concatenation, or interpolated template literals. Keep SQL fixed and use SDK filters or declared Lightdash parameters for interactive values. Never interpolate user input into SQL.
|
|
6
|
+
|
|
7
|
+
## Supported example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { query } from '@lightdash/query-sdk';
|
|
11
|
+
|
|
12
|
+
const EXPLORE = 'orders';
|
|
13
|
+
const EXTRA_METRICS = [{
|
|
14
|
+
name: 'completed_count',
|
|
15
|
+
label: 'Completed orders',
|
|
16
|
+
table: 'orders',
|
|
17
|
+
type: 'sum',
|
|
18
|
+
sql: "CASE WHEN ${orders.status} = 'completed' THEN 1 ELSE 0 END",
|
|
19
|
+
}];
|
|
20
|
+
|
|
21
|
+
query(EXPLORE)
|
|
22
|
+
.label('Completed orders by status')
|
|
23
|
+
.dimensions(['status_label'])
|
|
24
|
+
.metrics(['completed_count'])
|
|
25
|
+
.additionalMetrics(EXTRA_METRICS)
|
|
26
|
+
.customDimensions([{
|
|
27
|
+
id: 'orders_status_label',
|
|
28
|
+
name: 'Status',
|
|
29
|
+
table: 'orders',
|
|
30
|
+
type: 'sql',
|
|
31
|
+
dimensionType: 'string',
|
|
32
|
+
sql: 'UPPER(${orders.status})',
|
|
33
|
+
}])
|
|
34
|
+
.tableCalculations([{
|
|
35
|
+
name: 'share',
|
|
36
|
+
displayName: 'Share of completed orders',
|
|
37
|
+
sql: '1.0 * ${orders.completed_count} / NULLIF(SUM(${orders.completed_count}) OVER (), 0)',
|
|
38
|
+
}]);
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Here `${orders.status}` is part of a literal SQL string, not JavaScript interpolation. Use the existing modelled completed-order metric instead if one matches; the custom metric above illustrates the fallback only.
|
|
42
|
+
|
|
43
|
+
Custom dimension IDs must match the SDK's qualified selection: `.dimensions(['status_label'])` on `query('orders')` selects `orders_status_label`, so use that full value for the definition's `id`. The returned row still uses the selected short name, `status_label`.
|
|
44
|
+
|
|
45
|
+
## Unsupported example — rewrite before completing the build
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { query } from '@lightdash/query-sdk';
|
|
49
|
+
|
|
50
|
+
const EXPLORE = 'orders';
|
|
51
|
+
const METRIC = 'order_count';
|
|
52
|
+
const sumWhen = (name, condition) => ({
|
|
53
|
+
name, table: EXPLORE, type: 'sum',
|
|
54
|
+
sql: `CASE WHEN ${condition} THEN 1 ELSE 0 END`,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
query(EXPLORE)
|
|
58
|
+
.additionalMetrics([sumWhen('completed_count', "${orders.status} = 'completed'")])
|
|
59
|
+
.tableCalculations([{
|
|
60
|
+
name: 'share', displayName: 'Share',
|
|
61
|
+
sql: `\${${EXPLORE}.${METRIC}} / SUM(\${${EXPLORE}.${METRIC}}) OVER ()`,
|
|
62
|
+
}]);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Rewrite these as literal field definitions and SQL strings, preserving their calculation and filter semantics. Do not remove a requested calculation, change its denominator, or move it into client-side JavaScript merely to satisfy extraction. The pipeline provides source locations when it cannot capture custom SQL; fix those definitions in place.
|
|
@@ -69,6 +69,8 @@ Projects range from a handful of models to well over a thousand, so the director
|
|
|
69
69
|
2. **Read only the model files the app actually needs**, e.g. `Read /tmp/dbt-repo/models/orders.yml`. Each file holds that model's complete dimensions, metrics, joins, parameters, and model-level filters.
|
|
70
70
|
3. **Grep when the index isn't enough.** If you know a field name but not its model, `Grep` the directory for it. A wide model may be split across `<name>.yml`, `<name>.part2.yml`, … — the last line of each part points at the next.
|
|
71
71
|
|
|
72
|
+
**Prefer modelled fields before defining custom metrics or dimensions.** Check the relevant model and joined-table fields by name, label, description, type, and `ai_hints`. Reuse a field when its business meaning, aggregation, grain, and filters match the request; do not recreate it just to change its label. The model context does not include field SQL, so do not claim SQL equivalence or guess a definition from its name. When no suitable field exists, custom SQL remains supported. Suggest modelling a reusable business definition as an optional follow-up, not a prerequisite for building the app.
|
|
73
|
+
|
|
72
74
|
**Never read every model file, and never page through a file with `offset`/`limit`** — pick the model from the index and read that one file whole.
|
|
73
75
|
|
|
74
76
|
### Reading dbt YAML
|
|
@@ -272,7 +274,9 @@ which query produced it. One spread per query block is enough.
|
|
|
272
274
|
|
|
273
275
|
### Table calculations
|
|
274
276
|
|
|
275
|
-
Table calculations are computed columns evaluated
|
|
277
|
+
Table calculations are computed columns evaluated in SQL over the aggregated query results. They can reference dimensions and metrics using `${table.field}` syntax in their SQL expression.
|
|
278
|
+
|
|
279
|
+
Use client-side arithmetic only when the returned inputs preserve the intended meaning. For a percentage of total, the denominator must cover the intended population: the SDK defaults to 500 rows, so summing a limited or paginated result gives a percentage of those rows, not necessarily the overall total. Use a SQL calculation or a separately queried total with matching filters when needed. Keep calculations in the query when server-side sorting, filtering, or exports depend on them.
|
|
276
280
|
|
|
277
281
|
```ts
|
|
278
282
|
query('orders')
|
|
@@ -323,6 +327,10 @@ Each additional metric needs:
|
|
|
323
327
|
- If the metric exists in the dbt YAML → use `.metrics(['metric_name'])`
|
|
324
328
|
- If you need a custom aggregation not in the YAML → define it with `.additionalMetrics()` AND include its name in `.metrics()`
|
|
325
329
|
|
|
330
|
+
### Custom SQL and viewer access
|
|
331
|
+
|
|
332
|
+
Before using `.additionalMetrics()`, `.customDimensions()`, or `.tableCalculations()`, read `/app/references/custom-sql.md`. The build pipeline checks that custom SQL can be statically extracted and sends unresolved definitions back for repair. This lets registered viewers run the recorded SQL without granting SQL-authoring permissions; normal data-access checks still apply.
|
|
333
|
+
|
|
326
334
|
### Parameters
|
|
327
335
|
|
|
328
336
|
If the dbt YAML declares a `parameters:` block (under a model's `meta:` / `config.meta:`, or in `lightdash.config.yml`), read `/app/references/parameters.md` before using `.parameters()` — key naming is scope-dependent and a wrong key is silently ignored. Never invent parameters; when none are declared, use `.filters()` instead.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/cli",
|
|
3
|
-
"version": "2.487.
|
|
3
|
+
"version": "2.487.1",
|
|
4
4
|
"description": "Lightdash CLI tool",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"@actions/core": "1.11.1",
|
|
23
23
|
"@aws-sdk/credential-providers": "3.995.0",
|
|
24
24
|
"@casl/ability": "6.8.0",
|
|
25
|
-
"@lightdash/common": "2.487.
|
|
26
|
-
"@lightdash/warehouses": "2.487.
|
|
25
|
+
"@lightdash/common": "2.487.1",
|
|
26
|
+
"@lightdash/warehouses": "2.487.1",
|
|
27
27
|
"@sidvind/better-ajv-errors": "7.0.0",
|
|
28
28
|
"@types/columnify": "1.5.1",
|
|
29
29
|
"ajv": "8.18.0",
|