Dimensions are everything a metric can be grouped or filtered by: a status, a country, an order date. In the latest YAML spec a dimension is a property of the column it comes from. There are two types, categorical and time, and time dimensions also need the finest grain of the column.

Copy and paste

models:
  - name: orders
    semantic_model:
      enabled: true
    agg_time_dimension: order_date
    columns:
      - name: order_id
        entity:
          type: primary
          name: order
      - name: status
        description: Order status from the shop system.
        dimension:
          type: categorical
          name: order_status       # optional: defaults to the column name
          label: Order status      # shown in BI tools
      - name: order_date
        granularity: day           # on the column, not inside dimension
        dimension:
          type: time
      - name: shipped_date
        granularity: day
        dimension:
          type: time

Query them:

mf query --metrics revenue --group-by order__order_status
mf query --metrics revenue --group-by order__order_date__quarter
mf query --metrics revenue --group-by metric_time__month,order__order_status
order__order_status      revenue
---------------------  ---------
shipped                     2050
returned                     180

Categorical dimensions

Any column you group or filter by that isn’t a key or a date: status, country, channel, a boolean flag. The name defaults to the column name; set it when the column name is unclear, as with status above, which becomes order_status. The description defaults to the column’s description, and label is what BI tools display.

A column holds either an entity or a dimension, not both. A dimension’s name must be unique within its semantic model and must not repeat the name of an entity or a metric of the same model.

Time dimensions

A time dimension needs granularity, the finest grain the column holds: day for a date, hour or finer for a timestamp. The key sits at the column level, next to name, not inside the dimension block. The legacy type_params: time_granularity is deprecated.

At query time you can ask for the same grain or a coarser one, by adding it to the name: order__order_date__week, __month, __quarter, __year. A finer grain than the column’s is refused: order__order_date__hour on a day column returns “does not match any of the available group-by-items”.

metric_time or the dimension itself

Each semantic model has a default time dimension, agg_time_dimension, and every metric of the model is counted by it. In queries that date is called metric_time, which is also the only time name that works across metrics from different models. Use the dimension’s own name, such as order__shipped_date__month, when you want to group by another date of the same rows. The difference between the two is covered in how to set the default time dimension.

Dimensions from expressions

A dimension can’t have an expr. To compute one, such as an order size band, use derived_semantics; see how to define a dimension from a SQL expression.

Partitioned dates

is_partition: true on a time dimension marks it as the partition date of a date-partitioned table. When a query joins metrics from several tables, MetricFlow then matches the partitions instead of joining across all dates.

Tested with dbt Core 1.12 and MetricFlow 0.213 on DuckDB.

References