Files
webmail-client/backend/migrations/versions/0019_thread_senders.py
T

59 lines
2.2 KiB
Python
Raw Normal View History

"""threads.senders — who wrote, as distinct from who was involved
The message list showed a thread's `participants`, which the threader builds from
from + to + cc because the subject-fallback join matches on recipients. In a list
that meant every ordinary email read "Sender, You" — and the "+N" badge counted
the user themselves.
`senders` holds only the from addresses, newest writer first. Backfilled here so
existing threads do not have to wait to be touched again.
Revision ID: 0019
Revises: 0018
Create Date: 2026-09-18
"""
from __future__ import annotations
from collections.abc import Sequence
from alembic import op
revision: str = "0019"
down_revision: str | None = "0018"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.execute("ALTER TABLE threads ADD COLUMN senders jsonb NOT NULL DEFAULT '[]'")
# Newest writer first, de-duplicated by lower-cased address, matching what
# denormalize_thread computes from now on.
op.execute(
"""
WITH ranked AS (
SELECT m.thread_id,
lower(a->>'email') AS email_key,
jsonb_build_object('name', coalesce(a->>'name', ''),
'email', coalesce(a->>'email', '')) AS addr,
row_number() OVER (PARTITION BY m.thread_id, lower(a->>'email')
ORDER BY m.internal_date DESC) AS dup_rank,
max(m.internal_date) OVER (PARTITION BY m.thread_id, lower(a->>'email'))
AS recency
FROM messages m
CROSS JOIN LATERAL jsonb_array_elements(m.from_addr) AS a
WHERE m.thread_id IS NOT NULL AND coalesce(a->>'email', '') <> ''
),
collected AS (
SELECT thread_id, jsonb_agg(addr ORDER BY recency DESC) AS senders
FROM ranked WHERE dup_rank = 1 GROUP BY thread_id
)
UPDATE threads t SET senders = c.senders
FROM collected c WHERE c.thread_id = t.id
"""
)
def downgrade() -> None:
op.execute("ALTER TABLE threads DROP COLUMN IF EXISTS senders")