Instructions for developing a metrics view in Rill
Introduction
Metrics views are resources that define queryable business metrics on top of a table in an OLAP database. They implement what other business intelligence tools call a "semantic layer" or "metrics layer".
Metrics views are lightweight resources that only perform validation when reconciled. They are typically found downstream of connectors and models in the project's DAG. They power many user-facing features:
- Explore dashboards: Interactive drill-down interfaces for data exploration
- Canvas dashboards: Custom chart and table components
- Alerts: Notifications when data meets certain criteria
- Reports: Scheduled data exports and summaries
- Custom APIs: Programmatic access to metrics
Core Concepts
Table source
The model: property specifies the underlying table that powers the metrics view. It can reference:
- A model in the project: Just use the model name (e.g.,
model: events) - An external table: Specify the table name as it exists in the OLAP connector
Note: The table: property is a legacy alias for referencing external tables. Always prefer model: in new metrics views.
Timeseries
The timeseries: property identifies the timestamp column used for time-based filtering and line charts. This column must be a time/timestamp type in the underlying table.
If the timeseries column is not listed in dimensions:, Rill automatically adds it as a time dimension. You can optionally configure additional time-related settings:
It is strongly recommended that you add a primary timeseries to every metrics view you create (it makes for a much better dashboard experience).
Dimensions
Dimensions are attributes you can group by or filter on. They are typically categorical (strings, enums) or temporal (dates, timestamps). Rill infers the dimension type from the underlying SQL data type:
- Categorical: String, enum, boolean columns
- Time: Timestamp, date, datetime columns
- Geospatial: Geometry or geography columns
Define dimensions using either a direct column reference or a SQL expression:
Naming: Each dimension needs a name (stable identifier used in APIs and references), which defaults to column: if provided. The display_name: is optional, and defaults to a humanized version of name if not specified.
Type: Rill can infer the dimension type (categorical, time, geo) from the underlying SQL data type. Do not set type: explicitly unless you have a specific reason to override the inferred type.
Clickable URLs: The optional uri: property marks a dimension as a clickable link for single-click navigation. It accepts either a boolean (when the dimension's own value is already a URL) or a SQL expression that produces the URL. It is a distinct property from column: and expression: and can be combined with column::
Measures
Measures are aggregation expressions that compute numeric values when grouped by dimensions. They must use aggregate functions like SUM(), COUNT(), AVG(), MIN(), MAX().
Format presets: Control how values are displayed:
none: Raw numberhumanize: Round to K, M, B (e.g., 1.2M)currency_usd: Dollar format with 2 decimals ($1,234.56)currency_eur: Euro formatpercentage: Multiply by 100 and add % signinterval_ms: Convert milliseconds to human-readable duration
For custom formatting, use format_d3 with a d3-format string:
Best practices for dimensions and measures
Naming conventions:
- Use
snake_casefor thenamefield (e.g.,total_revenue,unique_users) - Only add
display_nameanddescriptionif they provide meaningful context beyond whatnameconveys (display names auto-humanize from the name by default) - Ensure measure names don't collide with column names in the underlying table
Getting started with measures:
- Start with a
COUNT(*)measure as a baseline (e.g.,total_recordsortotal_events) - Add
SUM()measures for numeric columns that represent quantities or values - Use
humanizeas the default format preset unless the data has a specific format requirement - Keep initial measures simple using only
COUNT,SUM,AVG,MIN,MAXaggregations - Add more complex expressions (ratios, conditional aggregations) only when needed
Dimension selection:
- Include all categorical columns (strings, enums, booleans) that users might want to filter or group by
- Start with 5-10 dimensions; add more based on user needs
Timeseries:
- If there is any date/timestamp column in the underlying table, pick the primary or most interesting one and add it under
dimensions: - It is also strongly recommended that you configure a primary time dimension using
timeseries:
Inline explore
New metrics views should set version: 1 and include an explore: block, which makes Rill emit an explore dashboard for the metrics view (named after the metrics view unless name: is set):
An empty block (explore: {}) is enough to enable the dashboard with all dimensions and measures. Note that explore: with no value (null) does NOT enable it. Set explore: {skip: true} to create a metrics view without a dashboard.
Legacy behavior: Files without version: (or version: 0) auto-generate an explore even without an explore: block. Files with version: 1 only get an explore if an explore: block is present.
Full Example
Here is a complete, annotated metrics view:
Security Policies
Security policies control who can access a metrics view and what data they can see. This is a powerful feature for multi-tenant dashboards and role-based access control.
Basic access control
The access: property controls whether users can view the metrics view at all:
The expression syntax should be a DuckDB expression, which will be evaluated in a sandbox without access to any tables.
Row-level security
The row_filter: property restricts which rows a user can see. It's a SQL expression that references user attributes via templating:
Common user attributes:
{{ .user.email }}: User's email address{{ .user.domain }}: Email domain (e.g., "acme.com"){{ .user.admin }}: Boolean admin flag- Custom attributes configured in Rill Cloud
The row filter should use the SQL syntax of the metrics view's model, and can reference other tables in the model's connector.
Complex row filters
Use logical operators for sophisticated access patterns:
Hiding dimensions and measures
The exclude: property conditionally hides specific dimensions or measures from certain users:
Advanced Features
Annotations
Annotations overlay contextual information (like events or milestones) on time-series charts:
Unnest for array dimensions
When a column contains arrays, use unnest: true to flatten it at query time:
Cache configuration
Configure caching for slow metrics views that use external tables:
You should not add a cache: config when the metrics view references a model inside the project since Rill does automatic cache management in that case.
Tags on dimensions and measures
Add tags: (free-form labels) to a dimension or measure to group and filter the dropdowns and pivot tables:
Rollups
Rollups back a metrics view with pre-aggregated tables. When a query's grain, dimensions, measures, time range, and filters match a rollup, Rill reads the smaller table instead of the base table for faster results. Requires a timeseries:.
Dialect-Specific Notes
SQL expressions in dimensions and measures use the underlying OLAP database's dialect.
DuckDB
DuckDB is the default OLAP engine for local development.
Conditional aggregation with FILTER:
ClickHouse
ClickHouse is recommended for production workloads with large datasets.
Conditional aggregation:
Date functions:
Array functions:
Druid
Approximate distinct counts:
Reference documentation
Here is a full JSON schema for the metrics view syntax:

