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. cumplimiento, conversion rate).
  • 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 ZONA
    columns: [unidades, ventas]
    computed:
      - field: cumplimiento
        type: percent_delta
        numerator: ventas
        denominator: presupuesto
  overall_totals:
    enabled: true
    label: TOTAL GENERAL
    columns: [unidades, ventas]

Behavior

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

  • web_mode"all_open", "single_open", "expand_all", or empty. How sections behave in the browser when the user clicks a header.
  • 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 in at least one model on the dashboard 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 (tableContext: document) suppresses cell-click wiring entirely — document/PDF rendering has no interactive target.
  • Disable per viz with chart.cross_filter: false.

See Cross-filtering for the full mechanism.

Worked example

id: nv_visitador_resumen
title: Resumen por visitador
query: "models/nv_comercial.malloy::resumen_por_visitador"
type: report_matrix
matrix:
  group_by: zona
  group_key_field: visitador
  group_columns: [periodo, unidades, ventas, cumplimiento]
  key_columns: [cumplimiento]
  trend_columns: [cumplimiento]
  group_totals:
    enabled: true
    label: TOTAL ZONA
    columns: [unidades, ventas]
    computed:
      - field: cumplimiento
        type: percent_delta
        numerator: ventas
        denominator: presupuesto
  overall_totals:
    enabled: true
    label: TOTAL GENERAL
    columns: [unidades, ventas]
  column_formats:
    ventas: currency
    presupuesto: currency
    cumplimiento: percent
  formats:
    currency: "$#,##0"
    percent: "#,##0.0%"
  behavior:
    web_mode: single_open
    default_open: all
    pdf_expand_all: true
published: true

Common pitfalls

  • PDF export shows fewer rows than the browser. Set matrix.behavior.pdf_expand_all: true. Without it, collapsed groups stay collapsed in the printed document.
  • Auto-summed totals include the wrong columns. List the columns you want summed explicitly in group_totals.columns / overall_totals.columns; leaving them empty auto-sums all numeric columns, which can include things like ID-style numbers.
  • Computed total field shows a strange number. Both numerator and denominator must reference fields present in the rows being summed. Check spelling against the query result.
  • Sections do not start expanded. Set matrix.behavior.default_open: "all", or the specific section id you want open on first load.
  • Click on a cell does nothing. Check that (a) the column name is declared as a parameter in at least one model on the dashboard, (b) the value is non-null, (c) the cell isn't in a totals or trend row (those are non-clickable by design), and (d) matrix.cross_filter isn't set to false.
  • Cell formats are inconsistent across columns. Use the matrix.column_formats + matrix.formats indirection so the same currency / percent pattern applies everywhere it should.