Materialised views
Combine columns from several entities into one table, with a row per record, and put it on a dashboard without writing queries.
Last updated
A materialised view is a saved table definition. You pick one entity to supply the rows, such as your users or customers, then add columns that pull in data from other entities: whether each row has a matching record, or a field from the latest one. Put the view on a dashboard as a widget and your team reads it as a table.
Note: Anythink calculates a materialised view each time it's opened. Nothing is stored, so there's no refresh to schedule and the table is always current. For a count, total or trend over time, use a chart instead: see Dashboards and charts.
When to use one#
| You want | Use |
|---|---|
| One row per customer, with columns showing whether they have ordered, and the amount of their latest order | A materialised view |
| One number or one line over time | A Chart widget |
| The latest rows of a single entity | A List widget |
A view fits best when the question is "which of these records have a matching record somewhere else?".
How a view is built#
| Part | What it does |
|---|---|
| Base entity | The entity that supplies one row per record. Leave it empty for a totals only view, which shows just the TOTAL row |
| Base row filter | Conditions that limit which base records appear |
| Columns | The data shown beside each row |
| Timeframe | Limits the records that columns look at: all time, 24 hours, 7, 30, 90 or 365 days, or a custom range. Offset (days back) shifts the window |
| Compare vs prior | Measures each column's total against an earlier window and shows the percentage change in the column header. The window is the same length as the timeframe unless you set Compare previous (days) |
| Row limit, sort | How many rows per page, and which base field they're sorted by |
Columns#
Each column has a Label, a Source and a Display.
Source says where the column's data comes from:
| Source | Reads from |
|---|---|
entity |
Another entity. You choose the Entity and Match on, the field on that entity that holds the base record's id |
base_field |
A field on the base row itself |
payments, subscriptions, subscription_events, offer_redemptions |
AnythinkPay, if your project uses it: see How payments and subscriptions work |
Display says how each cell looks:
| Display | Cell shows |
|---|---|
exists |
A marker (x unless you set another one) when the row has a matching record, otherwise empty |
count |
1 when the row has a matching record, 0 when it doesn't |
value |
The value of Value field from the matching record. Order picks the latest or first record by creation date |
A column can have its own filters and, on an entity source, a since_days limit, so one view can hold both "has any order" and "has a paid order in the last 30 days".
The TOTAL row#
The TOTAL row sits under the table and counts across the whole project, not just the rows on the current page. For an entity column it's the number of matching records, after the column's filters and the timeframe.
Create a view#
In the Anythink dashboard
- Open Dashboard and select the pencil (Customize dashboard).
- Select Add widget and choose Materialised view. The design window opens.
- Enter a Name and, if you like, a Description.
- Under Show one row per, pick the base entity, or leave it as Totals only.
- Optionally open Base row filter and add conditions.
- Open Timeframe & compare to set the window and, if you want it, Compare vs prior.
- Under Columns, select Add column. Set the Source, the Entity and Match on field for an entity source, the Display, and the Value field for a value column. Add a column-level filter if you need one.
- Check the Preview, then save. The widget is added to the dashboard. Select Save on the dashboard.
To change a view, edit its widget in customise mode.
With the CLI
Views are created and edited in the Anythink dashboard. Use the steps above.
With an AI assistant (MCP)
Views are created and edited in the Anythink dashboard. Your assistant can walk you through the steps above.
Show a view on a dashboard#
A view appears on a dashboard as a Materialised view widget. It shows the columns as a table with the TOTAL row pinned under it and pages through the rows. Percentage changes from Compare vs prior show in the column headers.
In the Anythink dashboard
Create the view from Add widget as above, or edit the widget later in customise mode. The table widget is a view of the data only: to change the definition, open the widget's editor.
With the CLI
Put an existing view on a dashboard with a materialised_view widget whose config_json holds the view's id:
anythink fetch /dashboards --method POST --body '{
"name": "Customers",
"widgets": [
{"type": "materialised_view", "title": "Customer orders", "config_json": "{\"viewId\":6}", "sort_order": 0}
]
}'
Add the widget to an existing dashboard by sending the whole widget list on PUT /dashboards/<id>: see Dashboards and charts.
With an AI assistant (MCP)
Add the Customer orders materialised view to my Customers dashboard.
Positioning the widget is easier in the Anythink dashboard.
Permissions#
Materialised views use the anythink_entities permission, the same one that guards entities and charts.
| Action | Permission |
|---|---|
| List and read views, and read their data | anythink_entities:read |
| Create a view | anythink_entities:create |
| Change a view | anythink_entities:update |
| Delete a view | anythink_entities:delete |
Showing a view on a dashboard also needs anythink_dashboards: see Dashboards and charts. For a custom role, tick the permissions on the role: see Roles and permissions. A missing permission shows as a 403.
Limits#
- Calculated live. There's no stored result, refresh schedule or export. A wide timeframe over a large entity takes longer to open.
- One row per base record. Rows are paged: 100 per page by default, up to 1,000.
countis a match flag. It shows1or0for each row, not the number of matching records. The TOTAL row holds the project-wide count.- Sort by a base field. The sort applies to a field on the base entity, not to one of the view's columns.
- One join field per column. An entity column joins on a single field of the other entity that holds the base record's
id. - Value columns need a
value_field. A column that can't be calculated reports why indiagnostics, and the other columns still load. - AnythinkPay sources need AnythinkPay set up in the project.
- Widget editing is dashboard-only. The CLI and your assistant create views and dashboards, while placing and sizing the widget is done in the Anythink dashboard.
Next steps#
- Dashboards and charts: chart a count, total or trend over time
- Records and views: browse and filter records in the Anythink dashboard
- Roles and permissions: grant
anythink_entitiesto a role - Model your data: create the entities a view reads