"""Add database indexes for performance and security. Revision ID: 20260117_add_database_indexes Revises: 20260117_add_automod_thresholds Create Date: 2026-01-17 12:00:00.000000 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = "20260117_add_database_indexes" down_revision = "20260117_add_automod_thresholds" branch_labels = None depends_on = None def upgrade() -> None: """Add indexes for common query patterns and performance optimization.""" # Indexes for moderation_logs table # Primary lookup patterns: by guild, by target user, by moderator, by timestamp op.create_index("idx_moderation_logs_guild_id", "moderation_logs", ["guild_id"]) op.create_index("idx_moderation_logs_target_id", "moderation_logs", ["target_id"]) op.create_index("idx_moderation_logs_moderator_id", "moderation_logs", ["moderator_id"]) op.create_index("idx_moderation_logs_created_at", "moderation_logs", ["created_at"]) op.create_index("idx_moderation_logs_action", "moderation_logs", ["action"]) op.create_index("idx_moderation_logs_is_automatic", "moderation_logs", ["is_automatic"]) # Compound indexes for common filtering patterns op.create_index("idx_moderation_logs_guild_target", "moderation_logs", ["guild_id", "target_id"]) op.create_index("idx_moderation_logs_guild_created", "moderation_logs", ["guild_id", "created_at"]) op.create_index("idx_moderation_logs_target_created", "moderation_logs", ["target_id", "created_at"]) # Indexes for strikes table # Primary lookup patterns: by guild, by user, active strikes, expiration op.create_index("idx_strikes_guild_id", "strikes", ["guild_id"]) op.create_index("idx_strikes_user_id", "strikes", ["user_id"]) op.create_index("idx_strikes_moderator_id", "strikes", ["moderator_id"]) op.create_index("idx_strikes_is_active", "strikes", ["is_active"]) op.create_index("idx_strikes_expires_at", "strikes", ["expires_at"]) op.create_index("idx_strikes_created_at", "strikes", ["created_at"]) # Compound indexes for active strike counting and user history op.create_index("idx_strikes_guild_user_active", "strikes", ["guild_id", "user_id", "is_active"]) op.create_index("idx_strikes_user_active", "strikes", ["user_id", "is_active"]) op.create_index("idx_strikes_guild_active", "strikes", ["guild_id", "is_active"]) # Indexes for banned_words table # Primary lookup patterns: by guild, by pattern (for admin management) op.create_index("idx_banned_words_guild_id", "banned_words", ["guild_id"]) op.create_index("idx_banned_words_is_regex", "banned_words", ["is_regex"]) op.create_index("idx_banned_words_action", "banned_words", ["action"]) op.create_index("idx_banned_words_added_by", "banned_words", ["added_by"]) # Compound index for guild-specific lookups op.create_index("idx_banned_words_guild_regex", "banned_words", ["guild_id", "is_regex"]) # Indexes for user_notes table (if it exists) # Primary lookup patterns: by guild, by user, by moderator op.create_index("idx_user_notes_guild_id", "user_notes", ["guild_id"]) op.create_index("idx_user_notes_user_id", "user_notes", ["user_id"]) op.create_index("idx_user_notes_moderator_id", "user_notes", ["moderator_id"]) op.create_index("idx_user_notes_created_at", "user_notes", ["created_at"]) # Compound indexes for user note history op.create_index("idx_user_notes_guild_user", "user_notes", ["guild_id", "user_id"]) op.create_index("idx_user_notes_user_created", "user_notes", ["user_id", "created_at"]) # Indexes for guild_settings table # These are mostly for admin dashboard filtering op.create_index("idx_guild_settings_automod_enabled", "guild_settings", ["automod_enabled"]) op.create_index("idx_guild_settings_ai_enabled", "guild_settings", ["ai_moderation_enabled"]) op.create_index("idx_guild_settings_verification_enabled", "guild_settings", ["verification_enabled"]) # Indexes for guilds table op.create_index("idx_guilds_owner_id", "guilds", ["owner_id"]) op.create_index("idx_guilds_premium", "guilds", ["premium"]) op.create_index("idx_guilds_created_at", "guilds", ["created_at"]) def downgrade() -> None: """Remove the indexes.""" # Remove all indexes in reverse order op.drop_index("idx_guilds_created_at") op.drop_index("idx_guilds_premium") op.drop_index("idx_guilds_owner_id") op.drop_index("idx_guild_settings_verification_enabled") op.drop_index("idx_guild_settings_ai_enabled") op.drop_index("idx_guild_settings_automod_enabled") op.drop_index("idx_user_notes_user_created") op.drop_index("idx_user_notes_guild_user") op.drop_index("idx_user_notes_created_at") op.drop_index("idx_user_notes_moderator_id") op.drop_index("idx_user_notes_user_id") op.drop_index("idx_user_notes_guild_id") op.drop_index("idx_banned_words_guild_regex") op.drop_index("idx_banned_words_added_by") op.drop_index("idx_banned_words_action") op.drop_index("idx_banned_words_is_regex") op.drop_index("idx_banned_words_guild_id") op.drop_index("idx_strikes_guild_active") op.drop_index("idx_strikes_user_active") op.drop_index("idx_strikes_guild_user_active") op.drop_index("idx_strikes_created_at") op.drop_index("idx_strikes_expires_at") op.drop_index("idx_strikes_is_active") op.drop_index("idx_strikes_moderator_id") op.drop_index("idx_strikes_user_id") op.drop_index("idx_strikes_guild_id") op.drop_index("idx_moderation_logs_target_created") op.drop_index("idx_moderation_logs_guild_created") op.drop_index("idx_moderation_logs_guild_target") op.drop_index("idx_moderation_logs_is_automatic") op.drop_index("idx_moderation_logs_action") op.drop_index("idx_moderation_logs_created_at") op.drop_index("idx_moderation_logs_moderator_id") op.drop_index("idx_moderation_logs_target_id") op.drop_index("idx_moderation_logs_guild_id")