Semantic layer
The semantic layer is where you define business logic once, as Metrics, Dimensions, and Relations, then reuse it across every Insight. Metrics are reusable aggregates, Dimensions are reusable row-level fields, and Relations declare how Models join so Visivo can auto-generate cross-model SQL.
Define once, compute everywhere
Without a semantic layer the same calculation gets re-written in every chart and the definitions drift. Defining a Metric, Dimension, or Relation once means every Insight computes it identically, and a single edit updates every dashboard that uses it.
-
Metric
A reusable aggregate (
SUM,COUNT, a ratio). Model-scoped metrics use direct SQL aggregates; global metrics compose other metrics. -
Dimension
A reusable, per-row computed field you group or filter by. Model columns also become implicit dimensions automatically.
-
Relation
A declared join condition between two Models, so Visivo generates the correct
JOINwhen an Insight pulls fields from both.
Metric: a reusable aggregate
A Metric names an aggregate calculation so charts share one definition. Metrics come in two flavors.
Model-scoped metrics
Defined under a Model's metrics: list, these use direct SQL aggregate expressions. The
expression must be a valid aggregate and cannot reference raw columns outside an aggregate
function.
models:
- name: orders
sql: SELECT * FROM orders_table
metrics:
- name: total_revenue
expression: "SUM(amount)"
description: "Total revenue from all orders"
- name: order_count
expression: "COUNT(DISTINCT id)"
Global metrics
Defined at the project level under a top-level metrics: list, global metrics compose other
metrics or fields across Models using ${ref(model).field} or ${ref(metric_name)} syntax.
Visivo resolves the dependencies and joins automatically.
metrics:
- name: revenue_per_user
expression: "${ref(orders).total_revenue} / ${ref(users).total_users}"
description: "Average revenue per user"
Metric naming
A Metric name must be a valid SQL identifier (letters, numbers, and underscores only),
and it cannot start with a number.
Dimension: a reusable row-level field
A Dimension is a calculated field evaluated for each row, used in GROUP BY or as a
filter. Unlike a Metric, it is not aggregated. Define them under a Model's dimensions: list.
models:
- name: orders
sql: SELECT * FROM orders_table
dimensions:
- name: order_month
expression: "DATE_TRUNC('month', order_date)"
description: "Month when the order was placed"
- name: is_high_value
expression: "CASE WHEN amount > 1000 THEN true ELSE false END"
data_type: BOOLEAN
Implicit dimensions
You do not have to declare a Dimension for every column. Visivo automatically exposes each of
a Model's columns as an implicit dimension, with its data_type auto-detected from the
source schema. Declare an explicit Dimension only when you need a computed expression. An
Insight can reference either a declared Dimension or a raw column with the same
${ref(model).field} syntax.
Relation: how two Models join
A Relation declares the join condition between two Models so a Metric can combine data
across them and Visivo can generate the JOIN automatically. The Models involved are
inferred from the condition, which must reference at least two different Models with
${ref(model).field} syntax. You cannot join on a Metric (an aggregated value).
relations:
- name: orders_to_users
join_type: inner
condition: "${ref(orders).user_id} = ${ref(users).id}"
is_default: true
| Field | Values | Purpose |
|---|---|---|
join_type |
inner, left, right, full |
The SQL join to use. Defaults to inner. |
condition |
${ref(a).x} = ${ref(b).y} |
The join predicate; must reference two Models. |
is_default |
true / false |
Disambiguates which Relation to use when multiple exist between the same pair of Models. |
Relations join within a single source
Both Models in a Relation must use the same Source, because a SQL join requires all tables to be reachable from one database connection. To combine data that does not already live together, load it onto a single Source with Seeds.
How it fits together
flowchart LR
MA[Model: orders]:::model --> R[Relation]:::relation
MB[Model: users]:::model --> R
MA --> MET[Metric: total_revenue]:::metric
MA --> DIM[Dimension: order_month]:::dimension
R --> GM[Global metric:<br/>revenue_per_user]:::metric
MET --> GM
GM --> I[Insight]:::insight
DIM --> I
classDef model fill:#fffbeb,stroke:#f59e0b,color:#92400e;
classDef metric fill:#ecfeff,stroke:#06b6d4,color:#155e75;
classDef dimension fill:#f0fdfa,stroke:#14b8a6,color:#115e59;
classDef relation fill:#eff6ff,stroke:#3b82f6,color:#1e40af;
classDef insight fill:#faf5ff,stroke:#a855f7,color:#6b21a8;
Learn more
- Semantic layer concept: the short overview.
- Insight: how Insights consume Metrics and Dimensions.
- Reference: Metric, Dimension, Relation.