# Database Migration Plan for Narasi Ahli ## Migration Overview This document outlines the database schema changes and migration steps needed to implement the Narasi Ahli features. ## Current Database State Analysis ### Existing Users Table The current `users` table already contains most of the required profile fields: - ✅ `fullname` (nama lengkap) - ✅ `email` (email) - ✅ `phone_number` (can be used for WhatsApp) - ✅ `last_education` (pendidikan terakhir) - ❌ Missing: `degree` (gelar) - ❌ Missing: `whatsapp_number` (separate from phone) - ❌ Missing: `last_job_title` (pekerjaan terakhir) ## Migration Steps ### Step 1: Update Users Table ```sql -- Add new columns to users table ALTER TABLE users ADD COLUMN degree VARCHAR(100), ADD COLUMN whatsapp_number VARCHAR(20), ADD COLUMN last_job_title VARCHAR(255); -- Add indexes for better query performance CREATE INDEX idx_users_whatsapp_number ON users(whatsapp_number); CREATE INDEX idx_users_last_job_title ON users(last_job_title); ``` ### Step 2: Create Education History Table ```sql CREATE TABLE education_histories ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL, school_name VARCHAR(255) NOT NULL, major VARCHAR(255) NOT NULL, education_level VARCHAR(100) NOT NULL, graduation_year INTEGER NOT NULL, certificate_image VARCHAR(500), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_education_histories_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); -- Indexes CREATE INDEX idx_education_histories_user_id ON education_histories(user_id); CREATE INDEX idx_education_histories_graduation_year ON education_histories(graduation_year); CREATE INDEX idx_education_histories_education_level ON education_histories(education_level); -- Constraints ALTER TABLE education_histories ADD CONSTRAINT chk_graduation_year CHECK (graduation_year >= 1950 AND graduation_year <= 2030); ``` ### Step 3: Create Work History Table ```sql CREATE TABLE work_histories ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL, job_title VARCHAR(255) NOT NULL, company_name VARCHAR(255) NOT NULL, start_date DATE NOT NULL, end_date DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_work_histories_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); -- Indexes CREATE INDEX idx_work_histories_user_id ON work_histories(user_id); CREATE INDEX idx_work_histories_start_date ON work_histories(start_date); CREATE INDEX idx_work_histories_company_name ON work_histories(company_name); -- Constraints ALTER TABLE work_histories ADD CONSTRAINT chk_work_dates CHECK (end_date IS NULL OR end_date >= start_date); ``` ### Step 4: Create Research Journals Table ```sql CREATE TABLE research_journals ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL, journal_title VARCHAR(500) NOT NULL, publisher VARCHAR(255) NOT NULL, journal_url VARCHAR(1000) NOT NULL, published_date DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_research_journals_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); -- Indexes CREATE INDEX idx_research_journals_user_id ON research_journals(user_id); CREATE INDEX idx_research_journals_publisher ON research_journals(publisher); CREATE INDEX idx_research_journals_published_date ON research_journals(published_date); ``` ### Step 5: Create Communication Tables ```sql -- Conversations table CREATE TABLE conversations ( id SERIAL PRIMARY KEY, participant1_id INTEGER NOT NULL, participant2_id INTEGER NOT NULL, last_message_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_conversations_participant1 FOREIGN KEY (participant1_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_conversations_participant2 FOREIGN KEY (participant2_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT chk_different_participants CHECK (participant1_id != participant2_id) ); -- Unique constraint to prevent duplicate conversations CREATE UNIQUE INDEX idx_conversations_participants ON conversations(LEAST(participant1_id, participant2_id), GREATEST(participant1_id, participant2_id)); -- Indexes CREATE INDEX idx_conversations_participant1 ON conversations(participant1_id); CREATE INDEX idx_conversations_participant2 ON conversations(participant2_id); CREATE INDEX idx_conversations_last_message_at ON conversations(last_message_at); -- Chat messages table CREATE TABLE chat_messages ( id SERIAL PRIMARY KEY, conversation_id INTEGER NOT NULL, sender_id INTEGER NOT NULL, message_text TEXT, message_type VARCHAR(20) NOT NULL DEFAULT 'text', file_url VARCHAR(500), file_name VARCHAR(255), file_size BIGINT, is_read BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_chat_messages_conversation FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE, CONSTRAINT fk_chat_messages_sender FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT chk_message_type CHECK (message_type IN ('text', 'image', 'file', 'audio')), CONSTRAINT chk_message_content CHECK ( (message_type = 'text' AND message_text IS NOT NULL) OR (message_type != 'text' AND file_url IS NOT NULL) ) ); -- Indexes CREATE INDEX idx_chat_messages_conversation_id ON chat_messages(conversation_id); CREATE INDEX idx_chat_messages_sender_id ON chat_messages(sender_id); CREATE INDEX idx_chat_messages_created_at ON chat_messages(created_at); CREATE INDEX idx_chat_messages_is_read ON chat_messages(is_read); ``` ### Step 6: Create AI Chat Tables ```sql -- AI Chat Sessions table CREATE TABLE ai_chat_sessions ( id SERIAL PRIMARY KEY, ai_session_id VARCHAR(100) NOT NULL UNIQUE, user_id INTEGER NOT NULL, agent_id VARCHAR(100), title VARCHAR(255) NOT NULL, message_count INTEGER DEFAULT 0, status VARCHAR(20) DEFAULT 'active', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_ai_chat_sessions_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT chk_session_status CHECK (status IN ('active', 'archived', 'deleted')), CONSTRAINT chk_message_count CHECK (message_count >= 0) ); -- Indexes CREATE INDEX idx_ai_chat_sessions_user_id ON ai_chat_sessions(user_id); CREATE INDEX idx_ai_chat_sessions_status ON ai_chat_sessions(status); CREATE INDEX idx_ai_chat_sessions_created_at ON ai_chat_sessions(created_at); -- AI Chat Messages table CREATE TABLE ai_chat_messages ( id SERIAL PRIMARY KEY, session_id INTEGER NOT NULL, message_type VARCHAR(20) NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_ai_chat_messages_session FOREIGN KEY (session_id) REFERENCES ai_chat_sessions(id) ON DELETE CASCADE, CONSTRAINT chk_ai_message_type CHECK (message_type IN ('user', 'assistant')) ); -- Indexes CREATE INDEX idx_ai_chat_messages_session_id ON ai_chat_messages(session_id); CREATE INDEX idx_ai_chat_messages_created_at ON ai_chat_messages(created_at); CREATE INDEX idx_ai_chat_messages_message_type ON ai_chat_messages(message_type); ``` ### Step 7: Create AI Chat Logs Table ```sql CREATE TABLE ai_chat_logs ( id SERIAL PRIMARY KEY, session_id INTEGER NOT NULL, user_id INTEGER NOT NULL, start_date TIMESTAMP NOT NULL, end_date TIMESTAMP, total_duration BIGINT DEFAULT 0, -- in seconds created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_ai_chat_logs_session FOREIGN KEY (session_id) REFERENCES ai_chat_sessions(id) ON DELETE CASCADE, CONSTRAINT fk_ai_chat_logs_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT chk_chat_log_dates CHECK (end_date IS NULL OR end_date >= start_date), CONSTRAINT chk_total_duration CHECK (total_duration >= 0) ); -- Indexes CREATE INDEX idx_ai_chat_logs_session_id ON ai_chat_logs(session_id); CREATE INDEX idx_ai_chat_logs_user_id ON ai_chat_logs(user_id); CREATE INDEX idx_ai_chat_logs_start_date ON ai_chat_logs(start_date); ``` ## GORM Entity Updates ### Updated Users Entity ```go // Add to existing Users struct in app/database/entity/users/users.entity.go type Users struct { // ... existing fields ... // New fields for Narasi Ahli Degree *string `json:"degree" gorm:"type:varchar(100)"` WhatsappNumber *string `json:"whatsapp_number" gorm:"type:varchar(20);index"` LastJobTitle *string `json:"last_job_title" gorm:"type:varchar(255);index"` // ... rest of existing fields ... } ``` ### Register New Entities in Database Models Update `app/database/index.database.go`: ```go func Models() []interface{} { return []interface{}{ // ... existing entities ... // New Narasi Ahli entities entity.EducationHistory{}, entity.WorkHistory{}, entity.ResearchJournals{}, entity.Conversations{}, entity.ChatMessages{}, entity.AIChatSessions{}, entity.AIChatMessages{}, entity.AIChatLogs{}, } } ``` ## Data Migration Considerations ### Existing Data Handling 1. **Users Table Updates**: The new columns are nullable, so existing users won't be affected 2. **Profile Completion**: Consider adding a profile completion percentage field 3. **Data Validation**: Implement validation for phone number formats and email addresses ### Migration Scripts ```sql -- Migration: 001_add_narasi_ahli_user_fields -- Run this first ALTER TABLE users ADD COLUMN IF NOT EXISTS degree VARCHAR(100), ADD COLUMN IF NOT EXISTS whatsapp_number VARCHAR(20), ADD COLUMN IF NOT EXISTS last_job_title VARCHAR(255); -- Migration: 002_create_education_histories -- Run after users table update -- (Include the education_histories table creation from Step 2) -- Migration: 003_create_work_histories -- (Include the work_histories table creation from Step 3) -- Migration: 004_create_research_journals -- (Include the research_journals table creation from Step 4) -- Migration: 005_create_communication_tables -- (Include both conversations and chat_messages tables from Step 5) -- Migration: 006_create_ai_chat_tables -- (Include all AI chat related tables from Steps 6 and 7) ``` ## Performance Optimization ### Indexing Strategy 1. **User-based queries**: All tables have user_id indexes 2. **Date-based queries**: Indexes on created_at, start_date, end_date 3. **Search functionality**: Indexes on searchable text fields 4. **Unique constraints**: Prevent duplicate conversations and AI sessions ### Query Optimization 1. **Pagination**: All list queries should use LIMIT/OFFSET 2. **Preloading**: Use GORM's Preload for related data 3. **Caching**: Consider Redis for frequently accessed data 4. **Connection pooling**: Optimize database connection settings ## Security Considerations ### Data Protection 1. **User isolation**: All queries filter by user_id 2. **File upload validation**: Strict file type and size limits 3. **SQL injection prevention**: Use parameterized queries 4. **Data encryption**: Consider encrypting sensitive fields ### Privacy Compliance 1. **Data retention**: Implement data cleanup policies 2. **User consent**: Track user consent for data processing 3. **Data export**: Provide user data export functionality 4. **Right to delete**: Implement cascading deletes for user data ## Rollback Plan ### Rollback Steps 1. **Remove new entities** from GORM Models() function 2. **Drop new tables** in reverse order: ```sql DROP TABLE IF EXISTS ai_chat_logs; DROP TABLE IF EXISTS ai_chat_messages; DROP TABLE IF EXISTS ai_chat_sessions; DROP TABLE IF EXISTS chat_messages; DROP TABLE IF EXISTS conversations; DROP TABLE IF EXISTS research_journals; DROP TABLE IF EXISTS work_histories; DROP TABLE IF EXISTS education_histories; ``` 3. **Remove new columns** from users table: ```sql ALTER TABLE users DROP COLUMN IF EXISTS degree, DROP COLUMN IF EXISTS whatsapp_number, DROP COLUMN IF EXISTS last_job_title; ``` ### Backup Strategy 1. **Full database backup** before migration 2. **Table-level backups** for critical tables 3. **Test restoration** on staging environment 4. **Monitor disk space** during migration ## Testing Plan ### Migration Testing 1. **Unit tests** for each entity 2. **Integration tests** for relationships 3. **Performance tests** for large datasets 4. **Migration rollback tests** ### Data Integrity Tests 1. **Foreign key constraints** validation 2. **Check constraints** validation 3. **Unique constraints** validation 4. **Data type validation** This migration plan ensures a smooth transition to the new Narasi Ahli database schema while maintaining data integrity and performance.