120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
"""chat stats
|
|
|
|
Revision ID: d4a7e2b91f38
|
|
Revises: c1f6b3d84a92
|
|
Create Date: 2026-08-06 12:30:00.000000
|
|
|
|
"""
|
|
|
|
from collections.abc import Sequence
|
|
|
|
from alembic import op
|
|
|
|
revision: str = "d4a7e2b91f38"
|
|
down_revision: str | None = "c1f6b3d84a92"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
_APPLY = """
|
|
CREATE FUNCTION chat_stats_apply() RETURNS trigger LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
IF TG_OP = 'INSERT' THEN
|
|
INSERT INTO chat_stats AS cs (account_id, chat_id, message_count,
|
|
last_date, last_message_id,
|
|
last_text, last_sender_id)
|
|
VALUES (NEW.account_id, NEW.chat_id, 1,
|
|
CASE WHEN NEW.date <= now() + interval '1 day'
|
|
THEN NEW.date END,
|
|
CASE WHEN NEW.date <= now() + interval '1 day'
|
|
THEN NEW.message_id END,
|
|
NEW.text, NEW.sender_id)
|
|
ON CONFLICT (account_id, chat_id) DO UPDATE SET
|
|
message_count = cs.message_count + 1,
|
|
last_date = CASE WHEN chat_stats_newer(cs, EXCLUDED)
|
|
THEN EXCLUDED.last_date ELSE cs.last_date END,
|
|
last_message_id = CASE WHEN chat_stats_newer(cs, EXCLUDED)
|
|
THEN EXCLUDED.last_message_id
|
|
ELSE cs.last_message_id END,
|
|
last_text = CASE WHEN chat_stats_newer(cs, EXCLUDED)
|
|
THEN EXCLUDED.last_text ELSE cs.last_text END,
|
|
last_sender_id = CASE WHEN chat_stats_newer(cs, EXCLUDED)
|
|
THEN EXCLUDED.last_sender_id
|
|
ELSE cs.last_sender_id END;
|
|
ELSE
|
|
UPDATE chat_stats
|
|
SET last_text = NEW.text, last_sender_id = NEW.sender_id
|
|
WHERE account_id = NEW.account_id
|
|
AND chat_id = NEW.chat_id
|
|
AND last_message_id = NEW.message_id;
|
|
END IF;
|
|
RETURN NULL;
|
|
END;
|
|
$$
|
|
"""
|
|
|
|
_NEWER = """
|
|
CREATE FUNCTION chat_stats_newer(current chat_stats, incoming chat_stats)
|
|
RETURNS boolean LANGUAGE sql IMMUTABLE AS $$
|
|
SELECT incoming.last_date IS NOT NULL
|
|
AND (current.last_date IS NULL
|
|
OR (incoming.last_date, incoming.last_message_id)
|
|
> (current.last_date, current.last_message_id))
|
|
$$
|
|
"""
|
|
|
|
_BACKFILL = """
|
|
INSERT INTO chat_stats (account_id, chat_id, message_count, last_date,
|
|
last_message_id, last_text, last_sender_id)
|
|
SELECT agg.account_id, agg.chat_id, agg.message_count,
|
|
last.date, last.message_id, last.text, last.sender_id
|
|
FROM (
|
|
SELECT account_id, chat_id, count(*) AS message_count
|
|
FROM messages GROUP BY account_id, chat_id
|
|
) agg
|
|
LEFT JOIN LATERAL (
|
|
SELECT date, message_id, text, sender_id FROM messages m
|
|
WHERE m.account_id = agg.account_id AND m.chat_id = agg.chat_id
|
|
AND m.date <= now() + interval '1 day'
|
|
ORDER BY m.date DESC, m.message_id DESC LIMIT 1
|
|
) last ON true
|
|
"""
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.execute(
|
|
"CREATE TABLE chat_stats ("
|
|
"account_id integer NOT NULL, "
|
|
"chat_id bigint NOT NULL, "
|
|
"message_count bigint NOT NULL DEFAULT 0, "
|
|
"last_date timestamptz, "
|
|
"last_message_id bigint, "
|
|
"last_text text, "
|
|
"last_sender_id bigint, "
|
|
"PRIMARY KEY (account_id, chat_id))"
|
|
)
|
|
op.execute(_BACKFILL)
|
|
op.execute(
|
|
"CREATE INDEX ix_chat_stats_recent ON chat_stats "
|
|
"(account_id, last_date DESC, chat_id DESC)"
|
|
)
|
|
op.execute(_NEWER)
|
|
op.execute(_APPLY)
|
|
op.execute(
|
|
"CREATE TRIGGER messages_chat_stats_insert AFTER INSERT ON messages "
|
|
"FOR EACH ROW EXECUTE FUNCTION chat_stats_apply()"
|
|
)
|
|
op.execute(
|
|
"CREATE TRIGGER messages_chat_stats_update AFTER UPDATE ON messages "
|
|
"FOR EACH ROW WHEN (OLD.text IS DISTINCT FROM NEW.text "
|
|
"OR OLD.sender_id IS DISTINCT FROM NEW.sender_id) "
|
|
"EXECUTE FUNCTION chat_stats_apply()"
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.execute("DROP TRIGGER IF EXISTS messages_chat_stats_update ON messages")
|
|
op.execute("DROP TRIGGER IF EXISTS messages_chat_stats_insert ON messages")
|
|
op.execute("DROP FUNCTION IF EXISTS chat_stats_apply()")
|
|
op.execute("DROP FUNCTION IF EXISTS chat_stats_newer(chat_stats, chat_stats)")
|
|
op.execute("DROP TABLE IF EXISTS chat_stats")
|