"""清理废案 search_title_suggestions 相关对象。 设计:产品迭代决定只展示 keyword 续接词,titles 段(真实文章 id 提示)被砍。 但 0009 迁移里建的相关对象还在: - search_title_suggestions 表 + 3 个 GIN/B-tree 索引 - articles trigger trg_articles_rebuild_title_suggestions - rebuild_title_suggestions() PL/pgSQL 函数 每篇文章 INSERT/UPDATE 都要跑 trigger(性能损耗),删干净。 也清理: - app/models/search_title_suggestion.py 文件(本次 commit 不删文件,只删 __init__ 引用) - app/scripts/backfill_search_suggestions.py 现在回灌的也是 search_title_suggestions → 整体替换成"无 op 脚本"(或者改名为只跑一次 refresh_search_keywords) Revision ID: 0011 Revises: 0010 Create Date: 2026-06-15 """ from __future__ import annotations from typing import Sequence, Union import sqlalchemy as sa from alembic import op revision: str = "0011" down_revision: Union[str, None] = "0010" branch_labels = None depends_on = None def upgrade() -> None: # 1) 删 trigger(挂 articles 上) op.execute("DROP TRIGGER IF EXISTS trg_articles_rebuild_title_suggestions ON articles") # 2) 删 trigger 函数 op.execute("DROP FUNCTION IF EXISTS rebuild_title_suggestions()") # 3) 删表 + 索引(ON DELETE CASCADE 让 articles delete 一起清) op.drop_index("ix_search_title_suggestions_published", table_name="search_title_suggestions") op.drop_index("ix_search_title_suggestions_article", table_name="search_title_suggestions") op.drop_index("ix_search_title_suggestions_prefix", table_name="search_title_suggestions") op.drop_table("search_title_suggestions") def downgrade() -> None: # 重新建表(产品迭代回滚时用 — schema 跟 0009 一致) op.create_table( "search_title_suggestions", sa.Column("id", sa.BigInteger, primary_key=True), sa.Column( "article_id", sa.BigInteger, sa.ForeignKey("articles.id", ondelete="CASCADE"), nullable=False, ), sa.Column("title_lang", sa.String(8), nullable=False, server_default="zh"), sa.Column("prefix_keys", sa.ARRAY(sa.Text), nullable=False), sa.Column("published_at", sa.DateTime(timezone=True), nullable=True), sa.Column( "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()"), ), ) op.create_index( "ix_search_title_suggestions_prefix", "search_title_suggestions", ["prefix_keys"], postgresql_using="gin", ) op.create_index( "ix_search_title_suggestions_article", "search_title_suggestions", ["article_id"], ) op.create_index( "ix_search_title_suggestions_published", "search_title_suggestions", ["published_at"], ) op.execute( """ CREATE OR REPLACE FUNCTION rebuild_title_suggestions() RETURNS TRIGGER AS $$ DECLARE src_text text; src_lang text; max_len int := 50; BEGIN DELETE FROM search_title_suggestions WHERE article_id = NEW.id; IF NEW.title_zh IS NOT NULL AND length(NEW.title_zh) > 0 THEN src_text := NEW.title_zh; src_lang := 'zh'; ELSIF NEW.title IS NOT NULL AND length(NEW.title) > 0 THEN src_text := NEW.title; src_lang := 'src'; ELSE RETURN NEW; END IF; src_text := substring(src_text, 1, max_len); INSERT INTO search_title_suggestions (article_id, title_lang, prefix_keys, published_at) SELECT NEW.id, src_lang, ARRAY(SELECT substring(src_text, 1, n) FROM generate_series(1, length(src_text)) AS n), NEW.published_at; RETURN NEW; END; $$ LANGUAGE plpgsql; """ ) op.execute( """ CREATE TRIGGER trg_articles_rebuild_title_suggestions AFTER INSERT OR UPDATE OF title_zh, title, published_at ON articles FOR EACH ROW EXECUTE FUNCTION rebuild_title_suggestions(); """ )