[add] migration script
This commit is contained in:
156
api/migrations/versions/74b51dfece29_20260311000.py
Normal file
156
api/migrations/versions/74b51dfece29_20260311000.py
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
"""20260311000
|
||||||
|
|
||||||
|
Revision ID: 74b51dfece29
|
||||||
|
Revises: f017efe4831c
|
||||||
|
Create Date: 2026-03-19 10:15:42.488027
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '74b51dfece29'
|
||||||
|
down_revision: Union[str, None] = 'f017efe4831c'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# 先删除旧的触发器(如果存在)
|
||||||
|
op.execute("DROP TRIGGER IF EXISTS tr_documents_update_stats ON documents;")
|
||||||
|
|
||||||
|
# 创建或更新 knowledges 统计信息的函数
|
||||||
|
op.execute("""
|
||||||
|
CREATE OR REPLACE FUNCTION update_knowledge_stats()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
DECLARE
|
||||||
|
-- 声明变量用于存储当前处理的知识库ID
|
||||||
|
current_kb_id UUID;
|
||||||
|
-- 声明变量用于存储文件夹知识库ID(如果存在)
|
||||||
|
folder_kb_id UUID;
|
||||||
|
-- 声明变量用于存储递归查询结果
|
||||||
|
folder_ids UUID[];
|
||||||
|
BEGIN
|
||||||
|
-- 处理 documents 表的插入、更新或删除
|
||||||
|
IF TG_TABLE_NAME = 'documents' THEN
|
||||||
|
-- 1. 更新 knowledges 表的 doc_num
|
||||||
|
UPDATE knowledges SET doc_num = (
|
||||||
|
SELECT COUNT(*) FROM documents
|
||||||
|
WHERE kb_id = knowledges.id AND status = 1
|
||||||
|
)
|
||||||
|
WHERE id = NEW.kb_id OR id = OLD.kb_id;
|
||||||
|
|
||||||
|
-- 2. 更新 knowledges 表的 chunk_num
|
||||||
|
UPDATE knowledges SET chunk_num = (
|
||||||
|
SELECT COALESCE(SUM(chunk_num), 0) FROM documents
|
||||||
|
WHERE kb_id = knowledges.id AND status = 1
|
||||||
|
)
|
||||||
|
WHERE id = NEW.kb_id OR id = OLD.kb_id;
|
||||||
|
|
||||||
|
-- 通过 knowledge_shares 表同步统计信息
|
||||||
|
-- 1. 使用 source_kb_id 的 doc_num 更新 target_kb_id 的 doc_num
|
||||||
|
UPDATE knowledges AS target
|
||||||
|
SET doc_num = source.doc_num
|
||||||
|
FROM knowledge_shares ks
|
||||||
|
JOIN knowledges AS source ON source.id = ks.source_kb_id
|
||||||
|
WHERE ks.target_kb_id = target.id
|
||||||
|
AND (source.id = NEW.kb_id OR source.id = OLD.kb_id);
|
||||||
|
|
||||||
|
-- 2. 使用 source_kb_id 的 chunk_num 更新 target_kb_id 的 chunk_num
|
||||||
|
UPDATE knowledges AS target
|
||||||
|
SET chunk_num = source.chunk_num
|
||||||
|
FROM knowledge_shares ks
|
||||||
|
JOIN knowledges AS source ON source.id = ks.source_kb_id
|
||||||
|
WHERE ks.target_kb_id = target.id
|
||||||
|
AND (source.id = NEW.kb_id OR source.id = OLD.kb_id);
|
||||||
|
|
||||||
|
-- 处理文件夹知识库的统计更新
|
||||||
|
-- 获取当前处理的知识库ID(可能是NEW或OLD中的kb_id)
|
||||||
|
IF NEW.kb_id IS NOT NULL THEN
|
||||||
|
current_kb_id := NEW.kb_id;
|
||||||
|
ELSIF OLD.kb_id IS NOT NULL THEN
|
||||||
|
current_kb_id := OLD.kb_id;
|
||||||
|
ELSE
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- 查找当前知识库的父文件夹(如果有)
|
||||||
|
SELECT id INTO folder_kb_id FROM knowledges
|
||||||
|
WHERE id IN (
|
||||||
|
SELECT parent_id FROM knowledges WHERE id = current_kb_id
|
||||||
|
) AND type = 'Folder';
|
||||||
|
|
||||||
|
-- 如果存在父文件夹,递归处理所有父文件夹
|
||||||
|
IF folder_kb_id IS NOT NULL THEN
|
||||||
|
-- 使用递归CTE获取所有父文件夹ID(包括多级嵌套)
|
||||||
|
WITH RECURSIVE folder_hierarchy AS (
|
||||||
|
-- 基础查询:获取直接父文件夹
|
||||||
|
SELECT id FROM knowledges
|
||||||
|
WHERE id = folder_kb_id AND type = 'Folder'
|
||||||
|
UNION ALL
|
||||||
|
-- 递归查询:获取父文件夹的父文件夹
|
||||||
|
SELECT k.id FROM knowledges k
|
||||||
|
JOIN folder_hierarchy fh ON k.id = k.parent_id
|
||||||
|
WHERE k.type = 'Folder'
|
||||||
|
)
|
||||||
|
-- 将结果存入数组以便处理
|
||||||
|
SELECT array_agg(id) INTO folder_ids FROM folder_hierarchy;
|
||||||
|
|
||||||
|
-- 遍历所有父文件夹并更新统计信息
|
||||||
|
FOR i IN 1..array_length(folder_ids, 1) LOOP
|
||||||
|
-- 更新文件夹的doc_num(汇总所有子知识库的doc_num)
|
||||||
|
UPDATE knowledges SET doc_num = (
|
||||||
|
-- 汇总直接子知识库的doc_num
|
||||||
|
SELECT COALESCE(SUM(child.doc_num), 0)
|
||||||
|
FROM knowledges child
|
||||||
|
WHERE child.parent_id = folder_ids[i] AND child.status = 1
|
||||||
|
-- 加上直接属于该文件夹的文档数(如果有)
|
||||||
|
UNION ALL
|
||||||
|
SELECT COALESCE(COUNT(*), 0)
|
||||||
|
FROM documents
|
||||||
|
WHERE kb_id = folder_ids[i] AND status = 1
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
WHERE id = folder_ids[i];
|
||||||
|
|
||||||
|
-- 更新文件夹的chunk_num(汇总所有子知识库的chunk_num)
|
||||||
|
UPDATE knowledges SET chunk_num = (
|
||||||
|
-- 汇总直接子知识库的chunk_num
|
||||||
|
SELECT COALESCE(SUM(child.chunk_num), 0)
|
||||||
|
FROM knowledges child
|
||||||
|
WHERE child.parent_id = folder_ids[i] AND child.status = 1
|
||||||
|
-- 加上直接属于该文件夹的文档的chunk_num(如果有)
|
||||||
|
UNION ALL
|
||||||
|
SELECT COALESCE(SUM(d.chunk_num), 0)
|
||||||
|
FROM documents d
|
||||||
|
WHERE d.kb_id = folder_ids[i] AND d.status = 1
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
WHERE id = folder_ids[i];
|
||||||
|
END LOOP;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
""")
|
||||||
|
|
||||||
|
# documents 表上的触发器(插入、更新、删除后)
|
||||||
|
op.execute("""
|
||||||
|
CREATE TRIGGER tr_documents_update_stats
|
||||||
|
AFTER INSERT OR UPDATE OR DELETE ON documents
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION update_knowledge_stats();
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# 删除触发器
|
||||||
|
op.execute("DROP TRIGGER IF EXISTS tr_documents_update_stats ON documents;")
|
||||||
|
# 删除函数
|
||||||
|
op.execute("DROP FUNCTION IF EXISTS update_knowledge_stats();")
|
||||||
|
|
||||||
Reference in New Issue
Block a user