Docs / Build Workflow

Filters

What a filter is in Looky

A filter is a user-facing control on a visualization or a dashboard. The user picks a value; Looky sends it to the underlying Malloy query as a named parameter. Filters live in the visualization (or dashboard) YAML under filters: [], one entry per control.

Filters and cross-filtering are different mechanisms. Filters are declarative controls the user explicitly interacts with. Cross-filtering is automatic — clicking on a chart inside a dashboard adds a "pill" that becomes a parameter without any YAML.

Choosing a filter type

Five filter types are supported. Pick the one whose UI matches the input the user is choosing.

  • select — dropdown picker. The option list is either declared in YAML or resolved dynamically by running a Malloy query at page load.
  • cutoff_date — single date that automatically expands to a date_from / date_to range running from the first day of that month to the chosen day. Use for "month-to-date as of" reports.
  • date_range — open from / to date picker. Use when the user needs to pick both endpoints freely.
  • date_range_preset — date range chosen from named presets (last_30_days, this_month, last_month, this_year, etc.). The default for most operational dashboards.
  • month — year + month picker with configurable bounds. Use when the analysis is keyed on a single month (monthly close, monthly billing).

Other filter types are not supported.

Anatomy of a filter declaration

Every filter entry is an object with at least a type. The other keys depend on the type — see each type's reference page for the full list.

id: orders_grid
type: grid
query: "models/ec_fulfillment.malloy::detail"
filters:
  - type: select
    id: status
    label: Status
    param: status
    options_query: "models/ec_fulfillment.malloy::status_options"
    default: all
  - type: date_range_preset
    id: period
    label: Period
    default:
      preset: last_30_days

The order in the array is the order the controls render in the UI.

How a filter value reaches the Malloy query

The value picked in the filter control is sent to the query as a named parameter. The model declares the parameter and uses it inside the query.

  • For select, cutoff_date, and month, the parameter name is whatever you put in the filter's param field (or, when omitted, the filter's id).
  • For date_range and date_range_preset, the parameters are date_from and date_to by default. Override the names through the filter's bindings: { date_from, date_to } block when the model expects different parameter names.

Naming convention: parameters whose Malloy name starts with p_ have the prefix stripped for the external name. A model parameter declared as p_start_date is set by sending start_date. See Malloy support.

Where filters live

  • On a single visualization. Use the visualization's filters: [] when the control is local to that chart — for example, a per-grid status filter.
  • On a dashboard. Use the dashboard's filters: [] when the control should narrow every viz on the page — for example, a global "Period" filter at the top of an executive dashboard.

A dashboard-level filter applies to every viz the dashboard contains, but only if each viz's underlying model declares the matching parameter.

Filters vs cross-filtering

Filters are declarative — you decide in YAML which controls the user sees. Cross-filtering is interactive — the user clicks a chart and the dashboard adds the implied filter as a pill. Both end up in the same place: a Malloy parameter on the next run. The only difference is who initiates the filter.

For most dashboards, mix the two: a small set of declared filters at the top (period, region, channel) plus the implicit cross-filter that responds to clicks. See Cross-filtering.

Adapter differences

Filter UI behaves the same on any source. Differences appear when the filter value is bound to the underlying parameter — most importantly for date / timestamp filters against Postgres- or MySQL-backed models, which use the raw-SQL @param placeholder pattern (each @param declared on the source signature, value substituted into the SQL string at run time). See Source adapter differences and Models.

Common pitfalls

  • The filter shows up but does not narrow the data. The model must declare a parameter with the matching name (after the p_ prefix is stripped) inside the source's parentheses. Check the source signature in the .malloy file.
  • Two filters bind to the same parameter. Only one wins. Pick a single source of truth per parameter — either a dashboard-level filter or a per-viz filter, not both.
  • Validate fails with unbound_param. The model uses a raw-SQL @param placeholder that is not declared on the source signature. The error names the missing parameter — add it to the source's parentheses.
  • Date filter on a Postgres or MySQL model fails. Use the raw-SQL @param placeholder pattern in the model's SQL with a matching declaration on the source signature — see Source adapter differences.
  • The default value does not load. For select, the default must match the id of one of the options. For date filters, use the supported tokens (, , etc.) or a literal ISO string.
  • The filter expects a value the user does not provide. Either set a sensible default in the filter, or declare a default on the source signature so the query still runs when the parameter is missing.