* [add]修改迁移文件新建空白表结构 * Add installation guide and environment setup Added installation instructions and environment requirements for MemoryBear. * [delete]删除api,web的readme.md。只保留唯一readme.md * Fix database connection example in README Update database connection configuration example in README.
441 lines
26 KiB
Python
441 lines
26 KiB
Python
"""init_db_table
|
||
|
||
Revision ID: b012ab0089b9
|
||
Revises:
|
||
Create Date: 2025-11-12 14:07:56.132912
|
||
|
||
"""
|
||
from typing import Sequence, Union
|
||
|
||
from alembic import op
|
||
import sqlalchemy as sa
|
||
from sqlalchemy.dialects import postgresql
|
||
|
||
# revision identifiers, used by Alembic.
|
||
revision: str = 'b012ab0089b9'
|
||
down_revision: Union[str, None] = None
|
||
branch_labels: Union[str, Sequence[str], None] = None
|
||
depends_on: Union[str, Sequence[str], None] = None
|
||
|
||
|
||
def upgrade() -> None:
|
||
# ### commands auto generated by Alembic - please adjust! ###
|
||
op.create_table('apps',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('workspace_id', sa.UUID(), nullable=False, comment='workspaces.id'),
|
||
sa.Column('created_by', sa.UUID(), nullable=False, comment='users.id'),
|
||
sa.Column('name', sa.String(), nullable=False),
|
||
sa.Column('description', sa.String(), nullable=True),
|
||
sa.Column('icon', sa.String(), nullable=True),
|
||
sa.Column('icon_type', sa.String(), nullable=True),
|
||
sa.Column('type', sa.String(), nullable=False),
|
||
sa.Column('visibility', sa.String(), nullable=True),
|
||
sa.Column('status', sa.String(), nullable=True),
|
||
sa.Column('tags', postgresql.JSON(astext_type=sa.Text()), nullable=True),
|
||
sa.Column('current_release_id', sa.UUID(), nullable=True),
|
||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.ForeignKeyConstraint(['current_release_id'], ['app_releases.id'], name='fk_apps_current_release_id', use_alter=True),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_apps_current_release_id'), 'apps', ['current_release_id'], unique=False)
|
||
op.create_index(op.f('ix_apps_id'), 'apps', ['id'], unique=False)
|
||
op.create_index(op.f('ix_apps_name'), 'apps', ['name'], unique=False)
|
||
op.create_index(op.f('ix_apps_type'), 'apps', ['type'], unique=False)
|
||
op.create_table('documents',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('kb_id', sa.UUID(), nullable=False, comment='knowledges.id'),
|
||
sa.Column('created_by', sa.UUID(), nullable=False, comment='users.id'),
|
||
sa.Column('file_id', sa.UUID(), nullable=False, comment='files.id'),
|
||
sa.Column('file_name', sa.String(), nullable=False, comment='file name'),
|
||
sa.Column('file_ext', sa.String(), nullable=False, comment='file extension'),
|
||
sa.Column('file_size', sa.Integer(), nullable=True, comment='file size(byte)'),
|
||
sa.Column('file_meta', sa.JSON(), nullable=False),
|
||
sa.Column('parser_id', sa.String(), nullable=False, comment='default parser ID'),
|
||
sa.Column('parser_config', sa.JSON(), nullable=False, comment='default parser config'),
|
||
sa.Column('chunk_num', sa.Integer(), nullable=True, comment='chunk num'),
|
||
sa.Column('progress', sa.Float(), nullable=True),
|
||
sa.Column('progress_msg', sa.String(), nullable=True, comment='process message'),
|
||
sa.Column('process_begin_at', sa.DateTime(), nullable=True),
|
||
sa.Column('process_duration', sa.Float(), nullable=True),
|
||
sa.Column('run', sa.Integer(), nullable=True, comment='start to run processing or cancel.(1: run it; 2: cancel)'),
|
||
sa.Column('status', sa.Integer(), nullable=True, comment='is it validate(0: wasted, 1: validate)'),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_documents_file_ext'), 'documents', ['file_ext'], unique=False)
|
||
op.create_index(op.f('ix_documents_file_name'), 'documents', ['file_name'], unique=False)
|
||
op.create_index(op.f('ix_documents_id'), 'documents', ['id'], unique=False)
|
||
op.create_index(op.f('ix_documents_parser_id'), 'documents', ['parser_id'], unique=False)
|
||
op.create_table('files',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('kb_id', sa.UUID(), nullable=False, comment='knowledges.id'),
|
||
sa.Column('created_by', sa.UUID(), nullable=False, comment='users.id'),
|
||
sa.Column('parent_id', sa.UUID(), nullable=True, comment='parent folder id'),
|
||
sa.Column('file_name', sa.String(), nullable=False, comment='file name or folder name,default folder name is /'),
|
||
sa.Column('file_ext', sa.String(), nullable=False, comment='file extension:folder|pdf'),
|
||
sa.Column('file_size', sa.Integer(), nullable=True, comment='file size(byte)'),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_files_file_ext'), 'files', ['file_ext'], unique=False)
|
||
op.create_index(op.f('ix_files_file_name'), 'files', ['file_name'], unique=False)
|
||
op.create_index(op.f('ix_files_id'), 'files', ['id'], unique=False)
|
||
op.create_table('generic_files',
|
||
sa.Column('id', sa.UUID(), nullable=False, comment='文件唯一标识'),
|
||
sa.Column('tenant_id', sa.UUID(), nullable=False, comment='租户ID'),
|
||
sa.Column('created_by', sa.UUID(), nullable=False, comment='创建者用户ID'),
|
||
sa.Column('file_name', sa.String(), nullable=False, comment='原始文件名'),
|
||
sa.Column('file_ext', sa.String(), nullable=False, comment='文件扩展名'),
|
||
sa.Column('file_size', sa.Integer(), nullable=False, comment='文件大小(字节)'),
|
||
sa.Column('mime_type', sa.String(), nullable=True, comment='MIME类型'),
|
||
sa.Column('context', sa.String(), nullable=False, comment='上传上下文(avatar/app_icon/knowledge_base/temp/attachment)'),
|
||
sa.Column('storage_path', sa.String(), nullable=False, comment='文件存储路径'),
|
||
sa.Column('file_metadata', sa.JSON(), nullable=True, comment='业务元数据'),
|
||
sa.Column('status', sa.String(), nullable=True, comment='文件状态(active/processing/deleted)'),
|
||
sa.Column('is_public', sa.Boolean(), nullable=True, comment='是否公开访问'),
|
||
sa.Column('access_url', sa.String(), nullable=True, comment='访问URL'),
|
||
sa.Column('reference_count', sa.Integer(), nullable=True, comment='引用计数'),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'),
|
||
sa.Column('deleted_at', sa.DateTime(), nullable=True, comment='删除时间(软删除)'),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index('idx_created_at', 'generic_files', ['created_at'], unique=False)
|
||
op.create_index('idx_tenant_context', 'generic_files', ['tenant_id', 'context'], unique=False)
|
||
op.create_index('idx_tenant_status', 'generic_files', ['tenant_id', 'status'], unique=False)
|
||
op.create_index(op.f('ix_generic_files_context'), 'generic_files', ['context'], unique=False)
|
||
op.create_index(op.f('ix_generic_files_created_by'), 'generic_files', ['created_by'], unique=False)
|
||
op.create_index(op.f('ix_generic_files_file_ext'), 'generic_files', ['file_ext'], unique=False)
|
||
op.create_index(op.f('ix_generic_files_id'), 'generic_files', ['id'], unique=False)
|
||
op.create_index(op.f('ix_generic_files_status'), 'generic_files', ['status'], unique=False)
|
||
op.create_index(op.f('ix_generic_files_tenant_id'), 'generic_files', ['tenant_id'], unique=False)
|
||
op.create_table('model_configs',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('name', sa.String(), nullable=False, comment='模型显示名称'),
|
||
sa.Column('type', sa.String(), nullable=False, comment='模型类型'),
|
||
sa.Column('description', sa.String(), nullable=True, comment='模型描述'),
|
||
sa.Column('config', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='模型配置参数'),
|
||
sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否激活'),
|
||
sa.Column('is_public', sa.Boolean(), nullable=False, comment='是否公开'),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_model_configs_id'), 'model_configs', ['id'], unique=False)
|
||
op.create_index(op.f('ix_model_configs_type'), 'model_configs', ['type'], unique=False)
|
||
op.create_table('tenants',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('name', sa.String(), nullable=False),
|
||
sa.Column('description', sa.String(), nullable=True),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.Column('is_active', sa.Boolean(), nullable=True),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_tenants_id'), 'tenants', ['id'], unique=False)
|
||
op.create_index(op.f('ix_tenants_name'), 'tenants', ['name'], unique=False)
|
||
op.create_table('agent_configs',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('app_id', sa.UUID(), nullable=False),
|
||
sa.Column('system_prompt', sa.Text(), nullable=True),
|
||
sa.Column('default_model_config_id', sa.UUID(), nullable=True),
|
||
sa.Column('tools_config', postgresql.JSON(astext_type=sa.Text()), nullable=True),
|
||
sa.Column('memory_strategy', sa.String(), nullable=True),
|
||
sa.Column('conversation_prefs', postgresql.JSON(astext_type=sa.Text()), nullable=True),
|
||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.ForeignKeyConstraint(['app_id'], ['apps.id'], ),
|
||
sa.ForeignKeyConstraint(['default_model_config_id'], ['model_configs.id'], ),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_agent_configs_app_id'), 'agent_configs', ['app_id'], unique=True)
|
||
op.create_index(op.f('ix_agent_configs_default_model_config_id'), 'agent_configs', ['default_model_config_id'], unique=False)
|
||
op.create_index(op.f('ix_agent_configs_id'), 'agent_configs', ['id'], unique=False)
|
||
op.create_table('app_releases',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('app_id', sa.UUID(), nullable=False),
|
||
sa.Column('version', sa.Integer(), nullable=False),
|
||
sa.Column('name', sa.String(), nullable=False),
|
||
sa.Column('description', sa.String(), nullable=True),
|
||
sa.Column('avatar', sa.String(), nullable=True),
|
||
sa.Column('type', sa.String(), nullable=False),
|
||
sa.Column('visibility', sa.String(), nullable=True),
|
||
sa.Column('status', sa.String(), nullable=True),
|
||
sa.Column('config', postgresql.JSON(astext_type=sa.Text()), nullable=True),
|
||
sa.Column('default_model_config_id', sa.UUID(), nullable=True),
|
||
sa.Column('published_by', sa.UUID(), nullable=False, comment='users.id'),
|
||
sa.Column('published_at', sa.DateTime(), nullable=True),
|
||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.ForeignKeyConstraint(['app_id'], ['apps.id'], ),
|
||
sa.ForeignKeyConstraint(['default_model_config_id'], ['model_configs.id'], ),
|
||
sa.PrimaryKeyConstraint('id'),
|
||
sa.UniqueConstraint('app_id', 'version', name='uq_app_release_app_version')
|
||
)
|
||
op.create_index(op.f('ix_app_releases_app_id'), 'app_releases', ['app_id'], unique=False)
|
||
op.create_index(op.f('ix_app_releases_default_model_config_id'), 'app_releases', ['default_model_config_id'], unique=False)
|
||
op.create_index(op.f('ix_app_releases_id'), 'app_releases', ['id'], unique=False)
|
||
op.create_index(op.f('ix_app_releases_version'), 'app_releases', ['version'], unique=False)
|
||
op.create_table('model_api_keys',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('model_config_id', sa.UUID(), nullable=False, comment='模型配置ID'),
|
||
sa.Column('model_name', sa.String(), nullable=False, comment='模型实际名称'),
|
||
sa.Column('provider', sa.String(), nullable=False, comment='API Key提供商'),
|
||
sa.Column('api_key', sa.String(), nullable=False, comment='API密钥'),
|
||
sa.Column('api_base', sa.String(), nullable=True, comment='API基础URL'),
|
||
sa.Column('config', postgresql.JSON(astext_type=sa.Text()), nullable=True, comment='API Key特定配置'),
|
||
sa.Column('usage_count', sa.String(), nullable=True, comment='使用次数'),
|
||
sa.Column('last_used_at', sa.DateTime(), nullable=True, comment='最后使用时间'),
|
||
sa.Column('is_active', sa.Boolean(), nullable=False, comment='是否激活'),
|
||
sa.Column('priority', sa.String(), nullable=True, comment='优先级'),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True, comment='创建时间'),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True, comment='更新时间'),
|
||
sa.ForeignKeyConstraint(['model_config_id'], ['model_configs.id'], ),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_model_api_keys_id'), 'model_api_keys', ['id'], unique=False)
|
||
op.create_table('users',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('username', sa.String(), nullable=False),
|
||
sa.Column('email', sa.String(), nullable=False),
|
||
sa.Column('hashed_password', sa.String(), nullable=False),
|
||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||
sa.Column('is_superuser', sa.Boolean(), nullable=False),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.Column('last_login_at', sa.DateTime(), nullable=True),
|
||
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_users_email'), 'users', ['email'], unique=True)
|
||
op.create_index(op.f('ix_users_id'), 'users', ['id'], unique=False)
|
||
op.create_index(op.f('ix_users_username'), 'users', ['username'], unique=True)
|
||
op.create_table('workspaces',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('name', sa.String(), nullable=False),
|
||
sa.Column('icon', sa.String(), nullable=True),
|
||
sa.Column('iconType', sa.String(), nullable=True),
|
||
sa.Column('description', sa.String(), nullable=True),
|
||
sa.Column('tenant_id', sa.UUID(), nullable=False),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.Column('is_active', sa.Boolean(), nullable=True),
|
||
sa.ForeignKeyConstraint(['tenant_id'], ['tenants.id'], ),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_workspaces_id'), 'workspaces', ['id'], unique=False)
|
||
op.create_index(op.f('ix_workspaces_name'), 'workspaces', ['name'], unique=False)
|
||
op.create_table('knowledges',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('workspace_id', sa.UUID(), nullable=False, comment='workspaces.id'),
|
||
sa.Column('created_by', sa.UUID(), nullable=False, comment='users.id'),
|
||
sa.Column('parent_id', sa.UUID(), nullable=True, comment='parent folder id when type is Folder'),
|
||
sa.Column('name', sa.String(), nullable=False, comment='KB name'),
|
||
sa.Column('description', sa.String(), nullable=True, comment='KB description'),
|
||
sa.Column('avatar', sa.String(), nullable=True, comment='avatar url'),
|
||
sa.Column('type', sa.String(), nullable=True, comment='Type:General|Web|Third-party|Folder'),
|
||
sa.Column('permission_id', sa.String(), nullable=True, comment='permission ID:Private|Share'),
|
||
sa.Column('embedding_id', sa.UUID(), nullable=True, comment='default embedding model ID'),
|
||
sa.Column('reranker_id', sa.UUID(), nullable=True, comment='default reranker model ID'),
|
||
sa.Column('llm_id', sa.UUID(), nullable=True, comment='default llm model ID'),
|
||
sa.Column('image2text_id', sa.UUID(), nullable=True, comment='default image2text model ID'),
|
||
sa.Column('doc_num', sa.Integer(), nullable=True, comment='doc num'),
|
||
sa.Column('chunk_num', sa.Integer(), nullable=True, comment='chunk num'),
|
||
sa.Column('parser_id', sa.String(), nullable=True, comment='default parser ID'),
|
||
sa.Column('parser_config', sa.JSON(), nullable=False, comment='default parser config'),
|
||
sa.Column('status', sa.Integer(), nullable=True, comment='is it validate(0: wasted, 1: validate)'),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.ForeignKeyConstraint(['created_by'], ['users.id'], ),
|
||
sa.ForeignKeyConstraint(['embedding_id'], ['model_configs.id'], ondelete='SET NULL'),
|
||
sa.ForeignKeyConstraint(['image2text_id'], ['model_configs.id'], ondelete='SET NULL'),
|
||
sa.ForeignKeyConstraint(['llm_id'], ['model_configs.id'], ondelete='SET NULL'),
|
||
sa.ForeignKeyConstraint(['reranker_id'], ['model_configs.id'], ondelete='SET NULL'),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_knowledges_id'), 'knowledges', ['id'], unique=False)
|
||
op.create_index(op.f('ix_knowledges_name'), 'knowledges', ['name'], unique=False)
|
||
op.create_index(op.f('ix_knowledges_parser_id'), 'knowledges', ['parser_id'], unique=False)
|
||
op.create_index(op.f('ix_knowledges_status'), 'knowledges', ['status'], unique=False)
|
||
op.create_table('workspace_invites',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('workspace_id', sa.UUID(), nullable=False),
|
||
sa.Column('email', sa.String(), nullable=False),
|
||
sa.Column('role', sa.String(), nullable=False),
|
||
sa.Column('token_hash', sa.String(), nullable=False),
|
||
sa.Column('status', sa.String(), nullable=False),
|
||
sa.Column('expires_at', sa.DateTime(), nullable=False),
|
||
sa.Column('accepted_at', sa.DateTime(), nullable=True),
|
||
sa.Column('created_by_user_id', sa.UUID(), nullable=False),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.ForeignKeyConstraint(['created_by_user_id'], ['users.id'], ),
|
||
sa.ForeignKeyConstraint(['workspace_id'], ['workspaces.id'], ),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_workspace_invites_email'), 'workspace_invites', ['email'], unique=False)
|
||
op.create_index(op.f('ix_workspace_invites_id'), 'workspace_invites', ['id'], unique=False)
|
||
op.create_index(op.f('ix_workspace_invites_token_hash'), 'workspace_invites', ['token_hash'], unique=True)
|
||
op.create_table('workspace_members',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('user_id', sa.UUID(), nullable=False),
|
||
sa.Column('workspace_id', sa.UUID(), nullable=False),
|
||
sa.Column('role', sa.String(), nullable=False),
|
||
sa.Column('is_active', sa.Boolean(), nullable=True),
|
||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||
sa.ForeignKeyConstraint(['workspace_id'], ['workspaces.id'], ),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_workspace_members_id'), 'workspace_members', ['id'], unique=False)
|
||
op.create_table('knowledge_shares',
|
||
sa.Column('id', sa.UUID(), nullable=False),
|
||
sa.Column('source_kb_id', sa.UUID(), nullable=False, comment='source knowledges.id'),
|
||
sa.Column('source_workspace_id', sa.UUID(), nullable=False, comment='source workspaces.id'),
|
||
sa.Column('target_kb_id', sa.UUID(), nullable=False, comment='target knowledges.id'),
|
||
sa.Column('target_workspace_id', sa.UUID(), nullable=False, comment='target workspaces.id'),
|
||
sa.Column('shared_by', sa.UUID(), nullable=False, comment='shared users.id'),
|
||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||
sa.ForeignKeyConstraint(['shared_by'], ['users.id'], ),
|
||
sa.ForeignKeyConstraint(['target_kb_id'], ['knowledges.id'], ),
|
||
sa.ForeignKeyConstraint(['target_workspace_id'], ['workspaces.id'], ),
|
||
sa.PrimaryKeyConstraint('id')
|
||
)
|
||
op.create_index(op.f('ix_knowledge_shares_id'), 'knowledge_shares', ['id'], unique=False)
|
||
# ### end Alembic commands ###
|
||
# 创建或更新统计信息的函数
|
||
op.execute("""
|
||
CREATE OR REPLACE FUNCTION update_knowledge_stats()
|
||
RETURNS TRIGGER AS $$
|
||
BEGIN
|
||
-- 处理 documents 表的插入、更新或删除
|
||
IF TG_TABLE_NAME = 'documents' THEN
|
||
-- 更新 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;
|
||
|
||
-- 更新 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);
|
||
|
||
END IF;
|
||
|
||
RETURN NULL;
|
||
END;
|
||
$$ LANGUAGE plpgsql;
|
||
""")
|
||
|
||
# 创建 documents 表上的触发器(如果不存在)
|
||
op.execute("""
|
||
DROP TRIGGER IF EXISTS tr_documents_update_stats ON documents;
|
||
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:
|
||
# ### commands auto generated by Alembic - please adjust! ###
|
||
op.drop_index(op.f('ix_knowledge_shares_id'), table_name='knowledge_shares')
|
||
op.drop_table('knowledge_shares')
|
||
op.drop_index(op.f('ix_workspace_members_id'), table_name='workspace_members')
|
||
op.drop_table('workspace_members')
|
||
op.drop_index(op.f('ix_workspace_invites_token_hash'), table_name='workspace_invites')
|
||
op.drop_index(op.f('ix_workspace_invites_id'), table_name='workspace_invites')
|
||
op.drop_index(op.f('ix_workspace_invites_email'), table_name='workspace_invites')
|
||
op.drop_table('workspace_invites')
|
||
op.drop_index(op.f('ix_knowledges_status'), table_name='knowledges')
|
||
op.drop_index(op.f('ix_knowledges_parser_id'), table_name='knowledges')
|
||
op.drop_index(op.f('ix_knowledges_name'), table_name='knowledges')
|
||
op.drop_index(op.f('ix_knowledges_id'), table_name='knowledges')
|
||
op.drop_table('knowledges')
|
||
op.drop_index(op.f('ix_workspaces_name'), table_name='workspaces')
|
||
op.drop_index(op.f('ix_workspaces_id'), table_name='workspaces')
|
||
op.drop_table('workspaces')
|
||
op.drop_index(op.f('ix_users_username'), table_name='users')
|
||
op.drop_index(op.f('ix_users_id'), table_name='users')
|
||
op.drop_index(op.f('ix_users_email'), table_name='users')
|
||
op.drop_table('users')
|
||
op.drop_index(op.f('ix_model_api_keys_id'), table_name='model_api_keys')
|
||
op.drop_table('model_api_keys')
|
||
op.drop_index(op.f('ix_app_releases_version'), table_name='app_releases')
|
||
op.drop_index(op.f('ix_app_releases_id'), table_name='app_releases')
|
||
op.drop_index(op.f('ix_app_releases_default_model_config_id'), table_name='app_releases')
|
||
op.drop_index(op.f('ix_app_releases_app_id'), table_name='app_releases')
|
||
op.drop_table('app_releases')
|
||
op.drop_index(op.f('ix_agent_configs_id'), table_name='agent_configs')
|
||
op.drop_index(op.f('ix_agent_configs_default_model_config_id'), table_name='agent_configs')
|
||
op.drop_index(op.f('ix_agent_configs_app_id'), table_name='agent_configs')
|
||
op.drop_table('agent_configs')
|
||
op.drop_index(op.f('ix_tenants_name'), table_name='tenants')
|
||
op.drop_index(op.f('ix_tenants_id'), table_name='tenants')
|
||
op.drop_table('tenants')
|
||
op.drop_index(op.f('ix_model_configs_type'), table_name='model_configs')
|
||
op.drop_index(op.f('ix_model_configs_id'), table_name='model_configs')
|
||
op.drop_table('model_configs')
|
||
op.drop_index(op.f('ix_generic_files_tenant_id'), table_name='generic_files')
|
||
op.drop_index(op.f('ix_generic_files_status'), table_name='generic_files')
|
||
op.drop_index(op.f('ix_generic_files_id'), table_name='generic_files')
|
||
op.drop_index(op.f('ix_generic_files_file_ext'), table_name='generic_files')
|
||
op.drop_index(op.f('ix_generic_files_created_by'), table_name='generic_files')
|
||
op.drop_index(op.f('ix_generic_files_context'), table_name='generic_files')
|
||
op.drop_index('idx_tenant_status', table_name='generic_files')
|
||
op.drop_index('idx_tenant_context', table_name='generic_files')
|
||
op.drop_index('idx_created_at', table_name='generic_files')
|
||
op.drop_table('generic_files')
|
||
op.drop_index(op.f('ix_files_id'), table_name='files')
|
||
op.drop_index(op.f('ix_files_file_name'), table_name='files')
|
||
op.drop_index(op.f('ix_files_file_ext'), table_name='files')
|
||
op.drop_table('files')
|
||
op.drop_index(op.f('ix_documents_parser_id'), table_name='documents')
|
||
op.drop_index(op.f('ix_documents_id'), table_name='documents')
|
||
op.drop_index(op.f('ix_documents_file_name'), table_name='documents')
|
||
op.drop_index(op.f('ix_documents_file_ext'), table_name='documents')
|
||
op.drop_table('documents')
|
||
op.drop_index(op.f('ix_apps_type'), table_name='apps')
|
||
op.drop_index(op.f('ix_apps_name'), table_name='apps')
|
||
op.drop_index(op.f('ix_apps_id'), table_name='apps')
|
||
op.drop_index(op.f('ix_apps_current_release_id'), table_name='apps')
|
||
op.drop_table('apps')
|
||
# ### end Alembic commands ###
|
||
# 删除触发器
|
||
op.execute("DROP TRIGGER IF EXISTS tr_documents_update_stats ON documents;")
|
||
|
||
# 删除函数
|
||
op.execute("DROP FUNCTION IF EXISTS update_knowledge_stats();")
|