Cohorts
Cohort analysis follows a group of things over time: the customers who first ordered in March, the products introduced last quarter, the locations opened this year — and what happened to each group since.
It is the one feature in the studio with a hard requirement on how a dataset was declared.
What a cohort needs from your view
Two roles, both of them:
| Role | Why the cohort needs it |
|---|---|
roles.entity | The key that identifies the thing being followed. Without it there is nothing to group into a cohort. |
roles.time | The axis the cohort is followed along, and what defines which cohort a row belongs to. |
A dataset missing either role cannot be used for cohorts. Not "works badly" — unavailable. The type-based fallback that guesses measures and dimensions cannot produce an entity, because no column type says "this is the identity of the thing somebody wants to follow".
Two lines in your analytics.json are the difference:
"roles": {
"entity": "location_id",
"time": "created_at"
}
Choosing the entity
The entity is the identity of the thing being followed over time — which is not always the view's key.
| View | key | Useful entity |
|---|---|---|
| One row per location | the location id | the location id |
| One row per stock movement | the movement id | the product, or the location |
| One row per order line | the line id | the customer, or the product |
The rule: ask what somebody would want to follow month over month. Following individual movements is meaningless; following the products those movements happened to is the analysis.
Use a stable id, not a label. An id that changes when somebody edits a name breaks a cohort silently, and — as Tenant isolation notes — an id is also the right choice when the alternative is exposing a person's name or email.
Choosing the time
The timestamp that answers "when did this happen", not "when did we last touch the row". A creation timestamp produces meaningful cohorts; an update timestamp produces cohorts of your own write pattern.
Cast it to timestamptz or date, and mark it "optional": false. A null in the time column is a row that belongs to no cohort at all, and nothing will tell the operator it was dropped.
What the operator does with it
They pick your dataset, define the cohort by a period of the time column, and read what the entity group did afterwards. Measures you declared are what they can quantify per cohort.
That is the extent of it in Cockpit today. There is no funnel builder, no retention-curve API, and no way to export a cohort on a schedule — see What does not exist yet. If a customer needs cohort analysis inside your own product, build it on your own store: the same two ideas, an entity key and an event-time axis, are all a cohort query needs. See Modelling.
Where to go next
- Semantic roles — all four roles, and the fallback.
- Datasets and charts — the rest of what an operator can build.
- Example — a view declared with both roles.