AML Quick Reference
Introduction
This page provides condensed code examples for quick reference when writing AML. For detailed explanations and parameter definitions, see the full reference pages linked in the table below.
| AML Dataset | Jump to example | Full Reference |
| AML Model | Jump to example | Full Reference |
| AML Fields | Jump to example | Full Reference |
| AML Relationship | Jump to example | Full Reference |
| AML Dashboard | Jump to example | Full Reference |
| AML Dataset Fields | Jump to example | Full Reference |
| AML Persistence | Jump to example | Full Reference |
| AML Constant | Jump to example | Full Reference |
| AML Function | Jump to example | Full Reference |
| AML Module | Jump to example | Full Reference |
| AML Extend | Jump to example | Full Reference |
| AML Partial | Jump to example | Full Reference |
| AML If-else | Jump to example | Full Reference |
| AML String Interpolation | Jump to example | Full Reference |
| AML Theme | Jump to example | Full Reference |
Dataset File
Dataset raw_ecommerce {
label: 'Raw Ecommerce'
description: "This Dataset is about Ecommerce data"
data_source_name: 'demodb'
models: [
users,
orders
]
relationships: [
// define relationship between orders and users is many to one
relationship(orders.user_id > users.id, true)
]
}
Model File
Table Model
Model users {
type: 'table'
label: "Users"
data_source_name: 'bigquery_dw'
table_name: 'users'
dimension id {
label: 'ID'
type: 'number'
hidden: false // optional
definition: @sql {{#SOURCE.id}};; // optional
}
dimension email {
label: 'Email'
type: 'number'
// without "definition", it automatically uses the same column name as the dimension name ('email')
}
measure user_count {
type: 'number'
label: 'Count Users'
definition: @aql count(users.id) ;;
}
}
Query Model
Model location {
type: 'query'
label: 'Location'
data_source_name: 'demodb'
models: [cities, countries]
query: @sql
select {{ #ci.id }} as city_id
, {{ #ci.name }} as city_name
, {{ #co.code }} as country_code
, {{ #co.name }} as country_name
, {{ #co.continent_name }}
from {{ #cities as ci }}
left join {{ #countries as co }} on {{ #co.code }} = {{ #ci.country_code }};;
dimension city_id {
label: 'City Id'
type: 'number'
}
dimension city_name {
label: 'City Name'
type: 'text'
}
dimension country_code {
label: 'Country Code'
type: 'text'
}
dimension country_name {
label: 'Country Name'
type: 'text'
}
dimension continent_name {
label: 'Continent Name'
type: 'text'
}
}
Measure
measure total_orders {
label: 'Total Orders'
type: 'number'
definition: @aql count(orders.id) ;;
}
measure total_users {
label: 'Total Users'
type: 'number'
definition: @aql count(users.id) ;;
}
Dimension
// This is a simple dimension. Holistics automatically assume there is a `created_at` column in your
// underlying table
dimension created_at {
label: 'Created At'
type: 'datetime'
}
// This is a derived dimension based on the dimension above, using AQL.
dimension created_at_year {
label: 'Created At Year'
type: 'number'
definition: @aql date_part('year', orders.created_at) ;;
}
// This is a simple dimension, with explicitly defined column name.
dimension status {
label: 'Status'
type: 'text'
// If you don't include this line, Holistics will automatically use the table's column that bear the
// same name with the dimension (i.e status).
definition: @sql {{ #SOURCE.status }};;
}
// This is a dimension which uses AQL case-when syntax.
dimension age_group {
label: 'User Age Group'
description: "'Under 18', '18 - 22', '23 - 30', '31 - 40', 'Over 40'"
type: 'text'
hidden: false
definition: @aql case(
when: users.age < 18, then: 'Under 18',
when: users.age >= 18 and users.age < 23, then: '18 - 22',
when: users.age >= 23 and users.age < 31, then: '23 - 30',
when: users.age >= 31 and users.age < 41, then: '31 - 40',
else: 'Over 40'
) ;;
}
// This is a dimension to extract month's name from the base dimension `created_at`.
dimension month_name {
label: 'Month Name'
type: 'text'
definition: @aql date_format(orders.created_at, '%m - %B') ;; // example output: '12 - December'
}
Relationship File
Full-form Relationship
// Relationship full form defined in dataset file
RelationshipConfig {
rel: Relationship {
type: 'many_to_one'
from: r(products.merchant_id)
to: r(merchants.id)
}
active: true
},
// Relationship defined in model file
Relationship relationship_name {
type: 'many_to_one'
from: r(orders.user_id)
to: r(users.id)
}
Short-form Relationship
Many-to-One relationship
// Relationship short form defined in dataset file, relationship type > is many_to_one
relationship(ecommerce_cities.country_code > ecommerce_countries.code, true, 'one_way')
One-to-One relationship
// Relationship short form defined in dataset file, relationship type - is one_to_one
relationship(ecommerce_merchants.admin_id - ecommerce_users.id, true)
Dashboard
Dashboard files use the .page.aml extension (e.g. my_dashboard.page.aml).
Dashboard myDashboard {
title: 'My Dashboard'
description: ''''''
theme: my_custom_theme // or inline: PageTheme { ... }
block t1: TextBlock {
content: @md # Hello World!;;
}
block v1: VizBlock {
label: 'GMV Over Time'
viz: CombinationChart {
dataset: demo_ecommerce
}
}
block f1: FilterBlock {
label: 'Order Created At'
type: 'field'
source: FieldFilterSource {
dataset: demo_ecommerce
field: r(order_master.order_created_at)
}
default {
operator: 'matches'
value: 'last 2 years'
}
}
settings {
timezone: 'America/Los_Angeles'
cache_duration: 360
}
view: CanvasLayout {
label: 'View 1'
width: 1080
height: 620
block t1 { position: pos(30, 30, 250, 60) }
block v1 { position: pos(300, 30, 760, 250) }
block f1 { position: pos(30, 100, 250, 80) }
}
}
Dataset Fields
Dataset-level dimensions and metrics support cross-model calculations that span multiple models.
Dataset Dimension
Dataset e_commerce {
// ...
dimension full_name {
model: users
type: 'text'
label: 'Full Name'
definition: @aql concat(users.first_name, ' ', users.last_name);;
}
}
Dataset Metric
Dataset ecommerce {
// ...
// Simple aggregation
metric count_orders {
label: 'Count Orders'
type: 'number'
definition: @aql count(orders.id) ;;
}
// Cross-model aggregation
metric sum_order_value {
label: 'Sum Order Values'
type: 'number'
definition: @aql sum(order_items, order_items.quantity * products.price) ;;
}
}
Persistence
Persistence is configured inside a Model using FullPersistence or IncrementalPersistence.
Full Persistence
Model orders {
// ...
persistence: FullPersistence {
schema: 'scratch'
view_name: 'pqm_orders' // optional
}
}
Incremental Persistence
Model orders {
// ...
persistence: IncrementalPersistence {
schema: 'scratch'
incremental_column: 'updated_at'
primary_key: 'id'
}
}
Schedules
Schedules are defined in a schedules.aml file at the root of your AML project.
const schedules = [
// Persist a Query Model every hour
Schedule { cron: '0 * * * *', models: [orders] }
// Persist a Pre-Aggregate nightly
PreAggregateSchedule {
cron: '15 20 * * *'
object: ecommerce_dataset
pre_aggregates: ['agg_transactions']
}
]
Constant
const signup_threshold = 0.6
const greeting = 'Hello'
// Explicit type
Number tax_rate = 0.08
// Used in a dataset metric via string interpolation
Dataset my_dataset {
metric active_users {
definition: @aql count(*)
| where users.signup_rate > ${signup_threshold}
;;
}
}
Function
// Simple function
Func double(x: Number) {
x * 2
}
// Function that returns a VizBlock
Func revenue_chart(dataset: String) {
VizBlock {
label: 'Revenue'
viz: BarChart {
dataset: dataset
series {
field { ref: r(orders.revenue), aggregation: 'sum' }
}
}
}
}
// Call the function
revenue_chart('ecommerce')
Module
Modules are directories under modules/ in your AML project root.
|-- datasets/
| |-- ecommerce.dataset.aml
|-- modules/
| |-- cohort/
| | |-- retention.aml
| |-- utils/
| |-- date_helpers.aml
Reference objects from a submodule using dot notation:
// datasets/ecommerce.dataset.aml
Dataset ecommerce {
models: [
orders,
cohort.retention_table // model from the cohort submodule
]
}
Import specific objects into the current file's scope with use:
use cohort { retention_table }
use utils { date_helpers: dates } // alias date_helpers to dates
Extend
Extend an existing object to create a new one, adding or overriding properties.
Model users {
dimension id { type: 'number' }
dimension email { type: 'text' }
dimension salary { type: 'number', hidden: false }
}
// Add a new dimension
Model users_with_activation = users.extend({
dimension activated_at { type: 'datetime' }
})
// Override a property without re-declaring everything
Model users_anonymized = users.extend({
dimension salary { hidden: true }
})
Partial
A partial extracts reusable properties that can be applied to multiple objects via .extend().
PartialModel timestamp_fields {
dimension created_at {
type: 'datetime'
definition: @sql {{ #SOURCE.created_at }};;
}
dimension updated_at {
type: 'datetime'
definition: @sql {{ #SOURCE.updated_at }};;
}
}
Model users_base {
type: 'table'
table_name: 'public.users'
data_source_name: 'demodb'
dimension id { type: 'number' }
}
Model users = users_base.extend(timestamp_fields)
Partials also work at the Dataset and Dashboard levels:
PartialDataset revenue_metrics {
metric gmv { definition: @aql sum(orders.gmv);; }
metric mrr { definition: @aql sum(subscriptions.mrr);; }
}
Dataset company = company_base.extend(revenue_metrics)
Dataset store = store_base.extend(revenue_metrics)
If-else
const score = 85
const grade = if (score >= 90) {
'A'
} else if (score >= 75) {
'B'
} else {
'C'
}
// grade is 'B'
Commonly used inside functions to switch logic based on a parameter:
Func kpi_block(metric: 'revenue' | 'orders') {
const measure_ref = if (metric == 'revenue') {
r(orders.revenue)
} else {
r(orders.id)
}
VizBlock {
label: "KPI -- ${metric}"
viz: SingleValue {
dataset: 'ecommerce'
series {
field {
ref: measure_ref
aggregation: if (metric == 'revenue') { 'sum' } else { 'count' }
}
}
}
}
}
String Interpolation
Embed variable values directly into strings using ${}.
const env = 'production'
const schema = 'analytics'
// In a string
const table_path = '${schema}.orders' // 'analytics.orders'
// In a heredoc (SQL definition)
@sql select * from ${schema}.orders ;;
// In a function label
Func chart(title: String) {
VizBlock {
label: 'Chart -- ${title}'
}
}
// Combined with if-else
Func greet(name: 'John' | 'Alice') {
"Hello, ${if (name == 'John') { 'sir' } else { 'madam' }}!"
}
Theme
Themes control the visual styling of dashboards. Define a PageTheme in a shared file to reuse it across multiple dashboards.
PageTheme brand_theme {
background {
bg_color: '#f5f5f5'
}
canvas {
background { bg_color: '#ffffff' }
shadow: 'md'
}
block {
border { border_radius: 8, border_color: '#e0e0e0', border_style: 'solid', border_width: 1 }
background { bg_color: '#ffffff' }
label { font_family: 'Inter', font_weight: 'semibold' }
}
viz {
table {
general { bg_color: '#ffffff', font_family: 'Inter' }
header { bg_color: '#f0f0f0', font_weight: 'bold' }
}
}
}
Apply a theme to a dashboard via the theme parameter:
Dashboard my_dashboard {
theme: brand_theme
// ...
}
Override the theme for a specific block using BlockTheme:
block hero: TextBlock {
theme: BlockTheme {
background { bg_color: '#1a1a2e' }
label { font_color: '#ffffff' }
}
content: @md # Welcome ;;
}