When Malloy runs a query, it returns two things. The results of the query and metadata about the results. The metadata are the schema for the results, including type information. Malloy also provides a mechanism to tag things in the source code and return tags with this meta data.
In Malloy, anything that can be named can be tagged. A tag starts with a #. Tags that start on a new line attach the tag the thing on the following line. For more details about how tagging works, see the Tags section.
Malloy's rendering library interprets these tags to change how results are rendered.
Tagging individual elements
In the query below, the measure percent_of_total is tagged as a percentage. Any time percent_of_total is used in a query, Malloy's rendering library will be displayed as a percentage.
source: flights is duckdb.table('../data/flights.parquet') extend { measure: flight_count is count() # percent percent_of_flights is flight_count / all(flight_count) }
run: flights -> { group_by: carrier aggregate: flight_count percent_of_flights }
[ { "carrier": "WN", "flight_count": 88751, "percent_of_flights": 0.2573783375431738 }, { "carrier": "US", "flight_count": 37683, "percent_of_flights": 0.10928088577750582 }, { "carrier": "AA", "flight_count": 34577, "percent_of_flights": 0.10027347046489979 }, { "carrier": "NW", "flight_count": 33580, "percent_of_flights": 0.09738216554968143 }, { "carrier": "UA", "flight_count": 32757, "percent_of_flights": 0.09499546149228454 }, { "carrier": "DL", "flight_count": 32130, "percent_of_flights": 0.09317715840116929 }, { "carrier": "RU", "flight_count": 16074, "percent_of_flights": 0.046614679244954715 }, { "carrier": "MQ", "flight_count": 15869, "percent_of_flights": 0.046020178234302996 }, { "carrier": "EV", "flight_count": 15769, "percent_of_flights": 0.04573017774130216 }, { "carrier": "HP", "flight_count": 9750, "percent_of_flights": 0.028275048067581715 }, { "carrier": "AS", "flight_count": 8453, "percent_of_flights": 0.024513741673360845 }, { "carrier": "CO", "flight_count": 7139, "percent_of_flights": 0.020703135195329833 }, { "carrier": "B6", "flight_count": 4842, "percent_of_flights": 0.01404182387110058 }, { "carrier": "OH", "flight_count": 4420, "percent_of_flights": 0.012818021790637044 }, { "carrier": "TZ", "flight_count": 3033, "percent_of_flights": 0.00879571495271542 } ]
WITH __stage0 AS ( SELECT group_set, CASE WHEN group_set=1 THEN base."carrier" END as "carrier__1", CASE WHEN group_set=1 THEN COUNT(1) END as "flight_count__1", (CASE WHEN group_set=1 THEN COUNT(1) END)*1.0/MAX((CASE WHEN group_set=0 THEN COUNT(1) END)) OVER () as "percent_of_flights__1" FROM '../data/flights.parquet' as base CROSS JOIN (SELECT UNNEST(GENERATE_SERIES(0,1,1)) as group_set ) as group_set GROUP BY 1,2 ) SELECT "carrier__1" as "carrier", MAX(CASE WHEN group_set=1 THEN "flight_count__1" END) as "flight_count", MAX(CASE WHEN group_set=1 THEN "percent_of_flights__1" END) as "percent_of_flights" FROM __stage0 WHERE group_set NOT IN (0) GROUP BY 1 ORDER BY 2 desc NULLS LAST
run: duckdb.table('../data/flights.parquet') -> { group_by: carrier aggregate: flight_count is count() }
[ { "carrier": "WN", "flight_count": 88751 }, { "carrier": "US", "flight_count": 37683 }, { "carrier": "AA", "flight_count": 34577 }, { "carrier": "NW", "flight_count": 33580 }, { "carrier": "UA", "flight_count": 32757 }, { "carrier": "DL", "flight_count": 32130 }, { "carrier": "RU", "flight_count": 16074 }, { "carrier": "MQ", "flight_count": 15869 }, { "carrier": "EV", "flight_count": 15769 }, { "carrier": "HP", "flight_count": 9750 }, { "carrier": "AS", "flight_count": 8453 }, { "carrier": "CO", "flight_count": 7139 }, { "carrier": "B6", "flight_count": 4842 }, { "carrier": "OH", "flight_count": 4420 }, { "carrier": "TZ", "flight_count": 3033 } ]
SELECT base."carrier" as "carrier", COUNT(1) as "flight_count" FROM '../data/flights.parquet' as base GROUP BY 1 ORDER BY 2 desc NULLS LAST
Simply adding # bar_chart before the query tags it and tells the rendering library to show the result as a bar chart. See the docs on the Bar Chart tag for more information.
# bar_chart run: duckdb.table('../data/flights.parquet') -> { group_by: carrier aggregate: flight_count is count() }
[ { "carrier": "WN", "flight_count": 88751 }, { "carrier": "US", "flight_count": 37683 }, { "carrier": "AA", "flight_count": 34577 }, { "carrier": "NW", "flight_count": 33580 }, { "carrier": "UA", "flight_count": 32757 }, { "carrier": "DL", "flight_count": 32130 }, { "carrier": "RU", "flight_count": 16074 }, { "carrier": "MQ", "flight_count": 15869 }, { "carrier": "EV", "flight_count": 15769 }, { "carrier": "HP", "flight_count": 9750 }, { "carrier": "AS", "flight_count": 8453 }, { "carrier": "CO", "flight_count": 7139 }, { "carrier": "B6", "flight_count": 4842 }, { "carrier": "OH", "flight_count": 4420 }, { "carrier": "TZ", "flight_count": 3033 } ]
SELECT base."carrier" as "carrier", COUNT(1) as "flight_count" FROM '../data/flights.parquet' as base GROUP BY 1 ORDER BY 2 desc NULLS LAST
Malloy's rendering library uses Vega-Lite for charting. The rendering library is a separate layer from Malloy's data access layer.
Tag Quick Reference
Charts
| Tag | Description | Key Properties |
|---|---|---|
# bar_chart |
Bar chart | .stack, .size, .x, .y, .series, .title, .subtitle |
# line_chart |
Line chart | .zero_baseline, .size, .interpolate, .x, .y, .series, .title, .subtitle |
# scatter_chart |
Scatter plot | Uses field order: x, y, color, size, shape |
# shape_map |
Choropleth map (US states) | Uses field order: state, value |
# segment_map |
Segment map (US) | Uses field order: lat1, lon1, lat2, lon2, color |
# big_value |
Prominent metric cards | .size, .sparkline, .comparison_field, .down_is_good |
Layout & Structure
| Tag | Description | Key Properties |
|---|---|---|
# dashboard |
Dashboard layout | .table.max_height, # break on nested views |
# table |
Table (default) | .size=fill, # column { width, height, word_break } |
# pivot |
Pivot nested query into columns | .dimensions |
# transpose |
Swap rows and columns | .limit |
# list |
Comma-separated list | First non-hidden field |
# list_detail |
List with detail values | First two non-hidden fields |
# flatten |
Flatten nested record into parent columns | On nest: without group_by |
Field Formatting
| Tag | Description | Example |
|---|---|---|
# number |
Number format | # number="$#,##0", # number=1m, # number=auto |
# percent |
Percentage | # percent |
# currency |
Currency format | # currency, # currency=usd2m, # currency=eur |
# duration |
Duration format | # duration=seconds, # duration.terse |
# link |
Hyperlink | # link { url_template="https://...$$" field=id } |
# image |
Render as image | # image { height=40px alt.field=name } |
# data_volume |
Storage size format | # data_volume=bytes, # data_volume=mb |
Utilities
| Tag | Description |
|---|---|
# hidden |
Hide field from rendering |
# label |
Override display name, e.g. # label="Total Sales" |
# description |
Tooltip description (used in # big_value) |
# tooltip |
Include nested view in parent chart tooltip |
# break |
Layout break in dashboards |