# ZEE Quiz Application - Database Schema app_ver: 1.0.0 doc_ver: A1 ## Database Schema ### 1. Primary Database (PostgreSQL 14) * **DB Type:** Relational * **Technology:** PostgreSQL 14 * **Purpose:** Primary data store for all application data * **Connection String:** `postgresql://user:password@host:5432/zee_quiz` #### Tables ##### 1.1. users ```sql CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, full_name VARCHAR(255) NOT NULL, role VARCHAR(50) NOT NULL DEFAULT 'user', is_active BOOLEAN DEFAULT true, last_login_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Indexes CREATE INDEX idx_users_email ON users(email); CREATE INDEX idx_users_role ON users(role); ``` ##### 1.2. profiles ```sql CREATE TABLE profiles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, date_of_birth DATE NOT NULL, gender VARCHAR(20) NOT NULL, preferred_gender VARCHAR(20) NOT NULL, phone_number VARCHAR(20), facebook_link VARCHAR(255), location VARCHAR(100), team VARCHAR(50), hobbies TEXT[], things_not_tried TEXT, hopes_for_partner TEXT, dealbreakers TEXT, message_to_partner TEXT, zodiac_sign VARCHAR(20), access_code VARCHAR(20) UNIQUE, personal_link VARCHAR(255), is_lucky_winner BOOLEAN DEFAULT false, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Indexes CREATE INDEX idx_profiles_user_id ON profiles(user_id); CREATE INDEX idx_profiles_access_code ON profiles(access_code); ``` ##### 1.3. photos ```sql CREATE TABLE photos ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), profile_id UUID REFERENCES profiles(id) ON DELETE CASCADE, url VARCHAR(512) NOT NULL, is_primary BOOLEAN DEFAULT false, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Indexes CREATE INDEX idx_photos_profile_id ON photos(profile_id); ``` ##### 1.4. quizzes ```sql CREATE TABLE quizzes ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title VARCHAR(255) NOT NULL, description TEXT, start_time TIMESTAMP WITH TIME ZONE NOT NULL, end_time TIMESTAMP WITH TIME ZONE NOT NULL, time_limit_seconds INT DEFAULT 300, -- 5 minutes is_active BOOLEAN DEFAULT true, created_by UUID REFERENCES users(id), created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Indexes CREATE INDEX idx_quizzes_active ON quizzes(is_active, start_time, end_time); ``` ##### 1.5. questions ```sql CREATE TABLE questions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), quiz_id UUID REFERENCES quizzes(id) ON DELETE CASCADE, question_text TEXT NOT NULL, question_type VARCHAR(50) NOT NULL, -- multiple_choice, true_false, short_answer options JSONB, correct_answer TEXT, points INT DEFAULT 1, display_order INT NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Indexes CREATE INDEX idx_questions_quiz_id ON questions(quiz_id); ``` ##### 1.6. user_answers ```sql CREATE TABLE user_answers ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id) ON DELETE CASCADE, quiz_id UUID REFERENCES quizzes(id) ON DELETE CASCADE, question_id UUID REFERENCES questions(id) ON DELETE CASCADE, answer_text TEXT, is_correct BOOLEAN, points_earned INT DEFAULT 0, time_taken_seconds INT, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Indexes CREATE INDEX idx_user_answers_user_quiz ON user_answers(user_id, quiz_id); CREATE INDEX idx_user_answers_question ON user_answers(question_id); ``` ### 2. Cache (Redis 7) * **DB Type:** Key-Value * **Technology:** Redis 7 * **Purpose:** Caching, rate limiting, and session storage * **Connection String:** `redis://:password@host:6379/0` #### Key Patterns - `user:sessions:{session_id}` - User session data - `quiz:leaderboard:{quiz_id}` - Sorted set for quiz leaderboard - `rate_limit:{ip_address}` - Rate limiting counters - `user:profile:{user_id}` - Cached user profile data - `quiz:active` - Set of active quiz IDs ### 3. File Storage (AWS S3) * **DB Type:** Object Storage * **Technology:** AWS S3 * **Purpose:** Storing user-uploaded files (profile photos, quiz images) * **Bucket Name:** `zee-quiz-{environment}` #### Directory Structure ``` zquiz/ ├── profiles/ │ └── {user_id}/ │ ├── {photo_id}.jpg │ └── {photo_id}_thumbnail.jpg └── quizzes/ └── {quiz_id}/ └── {question_id}.{ext} ``` ## Data Structures ### 1. User Session ```go type UserSession struct { ID string `json:"id"` UserID string `json:"user_id"` Email string `json:"email"` Role string `json:"role"` IPAddress string `json:"ip_address"` UserAgent string `json:"user_agent"` LastActivity time.Time `json:"last_activity"` ExpiresAt time.Time `json:"expires_at"` } ``` ### 2. Quiz Question ```go type Question struct { ID string `json:"id"` QuizID string `json:"quiz_id"` QuestionText string `json:"question_text"` QuestionType string `json:"question_type"` // multiple_choice, true_false, short_answer Options []string `json:"options,omitempty"` CorrectAnswer string `json:"correct_answer,omitempty"` Points int `json:"points"` DisplayOrder int `json:"display_order"` MediaURL string `json:"media_url,omitempty"` CreatedAt time.Time `json:"created_at"` } ``` ### 3. Quiz Result ```go type QuizResult struct { UserID string `json:"user_id"` QuizID string `json:"quiz_id"` TotalQuestions int `json:"total_questions"` CorrectAnswers int `json:"correct_answers"` TotalPoints int `json:"total_points"` Score float64 `json:"score"` // Percentage TimeCompleted time.Time `json:"time_completed"` Rank int `json:"rank,omitempty"` } ``` ## Database Migrations ### Initial Migration (0001_initial.up.sql) ```sql -- This file is auto-generated and managed by the golang-migrate tool -- It contains all the initial table creation statements -- Enable UUID extension CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- Create users table -- (Include all table creation SQL from above) ``` ## Performance Considerations ### Indexing Strategy - All foreign keys are indexed - Frequently queried columns have appropriate indexes - Composite indexes for common query patterns ### Partitioning - User answers table is partitioned by `quiz_id` for large-scale deployments - Old quiz data can be archived to cold storage ### Caching Strategy - User sessions and active quiz data are cached in Redis - Leaderboards are maintained in Redis sorted sets - Profile data is cached with TTL --- *Document Version: A1* *Last Updated: 2025-06-06*