chore(cohorts): migrate legacy cohort `groups` field to `filters`
@patricio-posthog is already working on this.
Since Mar 24, 2026.
- Dominant language
- Python
- Stars
- 39.9k
- Forks
- 3.4k
- Avg merge
- 6h 51m
- Merged PRs (30d)
- 232
Description
Is your feature request related to a problem?
The Cohort model has two fields for storing cohort conditions: the legacy groups (a JSONField(default=list)) and the newer filters (added in migration 0230_cohort_filters, May 2022). The filters field is the canonical source, but ~32,500 cohorts in production (31,688 US, 812 EU) still have data only in groups with no filters populated.
At read time, the properties property (cohort.py:221-284) checks filters first and falls back to converting groups on-the-fly. This conversion handles several legacy formats:
- Old
event-type properties that should bepersontype - Ancient
{'key': 'value'}flat dicts - Action/event-based groups → behavioral properties
This runtime conversion works, but it means ~32k cohorts pay a per-access conversion cost and the codebase carries multiple legacy format handlers indefinitely.
Additionally, calculate_people_ch() includes groups in its update_fields list even though it never modifies it — a historical artifact that creates confusion (see PR review discussion).
Describe the solution you'd like
A batched data migration that:
- For each cohort where
filtersis empty/null andgroupsis populated, convertgroupsto the equivalentfiltersJSON using the same logic as thepropertiesproperty - Write the converted
filtersvalue while leavinggroupsintact as a safety net - After validation, clean up
calculate_people_ch()to removegroupsfromupdate_fields - Eventually remove the runtime
groups→ properties conversion path and thegroupscolumn
The API still allows passing in groups. We probably need to either make that invalid OR automatically convert groups to filters. We may want to add a metric to see how prevalent this is before making a decision.
Describe alternatives you've considered
- Do nothing: The runtime conversion is cheap (dict manipulation, no DB queries) and works correctly. The main cost is code complexity, not performance.
- Stop writing
groupson new cohorts only: Would prevent growth but not reduce existing legacy data.
Additional context
- Scale: ~32.5k cohorts total (31,688 US + 812 EU) — manageable for a batched migration
- Edge case: Some
groupsentries are in an "invalid state" (no properties, no action_id, no event_id). The current code returns an emptyPropertyGroupfor these. Migration should flag or skip these. - Priority: Low — opportunistic cleanup, not urgent. Good candidate for when someone is already working in the cohort area.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.