Docs / Build Workflow

Visualization — report_matrix

When to use report_matrix

report_matrix is the right choice when the audience needs a structured report with grouped rows, nested sections, subtotals, and group totals. It is designed for document-mode dashboards and PDF export, while still supporting click-to-filter inside interactive dashboards.

Use grid instead when the rows do not need grouping. Use a chart-style viz when the question is about a single metric across categories rather than a structured tabular report.

Mapping

report_matrix takes its column structure from the query result. There is no required mapping block; the matrix configuration lives under the matrix block instead.

Columns & presentation

These keys control which columns the matrix shows and how each column renders:

  • matrix.key_columns — array of column names rendered as key cells (bold, polarity-based coloring). Use for the metric column readers should focus on (e.g. attainment %, conversion rate).
  • matrix.cross_filter — boolean, default true. Set to false to opt the matrix out of click emission (see Cross-filter behavior below).
  • matrix.trend_columns — array of column names rendered with a trend icon (up / down / dash) next to the value.
  • matrix.labels — object mapping column name to display label (overrides the raw column name in the header).
  • matrix.column_formats — object mapping column name to a format key.
  • matrix.formats — object mapping format key to a pattern (two-step indirection, like grid.formats).
  • matrix.column_widths — object mapping column name to a width ("150px", "20%", or numeric).
  • matrix.uniform_column_widths — boolean or "auto". When set, all columns share the same auto-fit width.
  • matrix.uniform_column_widths_min_px — integer. Default 80.
  • matrix.uniform_column_widths_max_px — integer. Default 520.

Summary table

The matrix can render a comparative summary above the detail rows:

  • matrix.show_summary — boolean. Default true.
  • matrix.summary_title — string title above the summary.
  • matrix.summary_columns — array of column names included in the summary.

Grouping & sections

Two ways to organise the rows: automatic grouping by a column, or manual sections.

Automatic grouping

  • matrix.group_by — column name. Group result rows by this field; each group becomes a collapsible section.
  • matrix.group_key_field — column name to render as the first column inside each group section.
  • matrix.group_columns — array of column names rendered inside each group.

Manual sections

matrix:
  sections:
    - id: revenue
      title: Revenue breakdown
      columns: [period, units, revenue]
    - id: cost
      title: Cost breakdown
      columns: [period, cogs, opex]

Totals

Totals live in two siblings under matrix: group_totals (one row per group section) and overall_totals (one row across all groups). Both share the same shape:

  • enabled — boolean.
  • label — string. Default "TOTAL".
  • columns — array of column names to sum. If empty, numeric columns are summed automatically.
  • computed — array of computed field objects rendered alongside the sums. Each item:
    • field — output field name.
    • type"ratio" or "percent_delta".
    • numerator — field name for the dividend (ratio) or current period (delta).
    • denominator — field name for the divisor (ratio) or baseline (delta).
matrix:
  group_totals:
    enabled: true
    label: TOTAL
    columns: [units, revenue]
    computed:
      - field: attainment_pct
        type: percent_delta
        numerator: revenue
        denominator: budget
  overall_totals:
    enabled: true
    label: GRAND TOTAL
    columns: [units, revenue]

Behavior

matrix.behavior controls how sections expand and how the matrix renders for PDF export:

  • web_mode"all_open" (every section expandable independently), "single_open" (opening a section closes the others), or "expand_all" (everything open, headers inert). Omit the key for the default accordion behavior.
  • default_open"all" or a specific section id. Which sections start expanded on first load.
  • pdf_expand_all — boolean. Default false. Critical for PDF export: without this, collapsed groups disappear from the printed document.

format

Use matrix.column_formats + matrix.formats as the primary formatting path; the root format field is checked as a fallback only.

Cross-filter behavior

  • Clicking a body cell whose column is declared as a parameter on a source signature in this viz's own model file emits a pill { field: <column name>, value: <cell value> }.
  • Cells in group totals and overall totals rows are not clickable — they aggregate across multiple key values, so a click would be ambiguous.
  • Cells in trend columns (rendered with up/down direction badges) are not clickable — they show direction over a numeric value, not a categorical pick.
  • Section headers (auto-grouped via matrix.group_by or hand-crafted via matrix.sections) are not clickable in this version. The <details> click is reserved for expand/collapse.
  • Document-mode dashboards suppress cell-click wiring entirely — document/PDF rendering has no interactive target.
  • Disable per viz with matrix.cross_filter: false.

See Cross-filtering for the full mechanism.

Worked example

A revenue matrix grouped by category with the brands of each category as detail rows. The average ticket in the totals rows is a computed ratio — summing an average would be wrong, so it is re-derived from the summed columns:

id: ec_category_brand_matrix
title: Revenue matrix by category
query: "models/ec_category_brand_matrix.malloy::by_category_brand"
type: report_matrix
matrix:
  show_summary: false
  group_by: category
  group_key_field: brand
  group_columns:
    - revenue
    - order_count
    - avg_ticket
    - revenue_prev_year
    - yoy_delta_pct
  key_columns: [revenue, yoy_delta_pct]
  trend_columns: [yoy_delta_pct]
  group_totals:
    enabled: true
    label: TOTAL
    columns: [revenue, order_count, revenue_prev_year]
    computed:
      - field: avg_ticket
        type: ratio
        numerator: revenue
        denominator: order_count
  overall_totals:
    enabled: true
    label: GRAND TOTAL
    columns: [revenue, order_count, revenue_prev_year]
  labels:
    brand: Brand
    revenue: Revenue
    order_count: Orders
    avg_ticket: Avg ticket
    revenue_prev_year: Revenue LY
    yoy_delta_pct: YoY
  column_formats:
    revenue: currency
    avg_ticket: currency
    revenue_prev_year: currency
    yoy_delta_pct: percent
  formats:
    currency: "$#,##0"
    percent: "#,##0.0%"
  behavior:
    web_mode: all_open
    default_open: all
    pdf_expand_all: true
pagination:
  page_size: 50
published: true

Design notes

  • For PDF export, set matrix.behavior.pdf_expand_all: true so every group prints expanded.
  • List summed columns explicitly in group_totals.columns / overall_totals.columns — the empty form auto-sums every numeric column, including ID-style numbers.
  • Computed total fields reference columns present in the summed rows — both numerator and denominator.
  • Control the first-load state with matrix.behavior.default_open: "all", or a specific section id.
  • Use the matrix.column_formats + matrix.formats indirection so the same currency / percent pattern applies everywhere it should.