Skip to main content

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 DatasetJump to exampleFull Reference
AML ModelJump to exampleFull Reference
AML FieldsJump to exampleFull Reference
AML RelationshipJump to exampleFull Reference
AML DashboardJump to exampleFull Reference
AML Dataset FieldsJump to exampleFull Reference
AML PersistenceJump to exampleFull Reference
AML ConstantJump to exampleFull Reference
AML FunctionJump to exampleFull Reference
AML ModuleJump to exampleFull Reference
AML ExtendJump to exampleFull Reference
AML PartialJump to exampleFull Reference
AML If-elseJump to exampleFull Reference
AML String InterpolationJump to exampleFull Reference
AML ThemeJump to exampleFull 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).

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.

schedules.aml
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().

timestamp_fields.aml
PartialModel timestamp_fields {
dimension created_at {
type: 'datetime'
definition: @sql {{ #SOURCE.created_at }};;
}
dimension updated_at {
type: 'datetime'
definition: @sql {{ #SOURCE.updated_at }};;
}
}
users.model.aml
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.

themes.aml
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:

my_dashboard.page.aml
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 ;;
}

Open Markdown
Let us know what you think about this document :)