-- ===================================================== -- TABLE: education.instructors -- ===================================================== -- Proyecto: OrbiQuant IA (Trading Platform) -- Modulo: OQI-002 - Education -- Purpose: Instructor profiles for educational content -- Related: OQI-002 Education Module -- Gap: GAP-DDL-001 -- Created: 2026-02-03 -- Updated: 2026-02-03 -- ===================================================== CREATE TABLE IF NOT EXISTS education.instructors ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL UNIQUE REFERENCES auth.users(id) ON DELETE CASCADE, display_name VARCHAR(100) NOT NULL, bio TEXT, avatar_url VARCHAR(500), expertise TEXT[] DEFAULT '{}', social_links JSONB DEFAULT '{}', total_courses INTEGER NOT NULL DEFAULT 0, total_students INTEGER NOT NULL DEFAULT 0, total_reviews INTEGER NOT NULL DEFAULT 0, average_rating DECIMAL(3, 2), is_verified BOOLEAN NOT NULL DEFAULT false, verified_at TIMESTAMPTZ, is_active BOOLEAN NOT NULL DEFAULT true, metadata JSONB DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT instructors_rating_range CHECK (average_rating IS NULL OR (average_rating >= 0 AND average_rating <= 5)), CONSTRAINT instructors_counts_positive CHECK (total_courses >= 0 AND total_students >= 0 AND total_reviews >= 0) ); -- Indexes CREATE INDEX idx_instructors_user_id ON education.instructors(user_id); CREATE INDEX idx_instructors_is_verified ON education.instructors(is_verified) WHERE is_verified = true; CREATE INDEX idx_instructors_is_active ON education.instructors(is_active) WHERE is_active = true; CREATE INDEX idx_instructors_average_rating ON education.instructors(average_rating DESC NULLS LAST); CREATE INDEX idx_instructors_expertise ON education.instructors USING GIN(expertise); -- Trigger for updated_at CREATE TRIGGER trg_instructors_updated_at BEFORE UPDATE ON education.instructors FOR EACH ROW EXECUTE FUNCTION education.update_updated_at(); -- Comments COMMENT ON TABLE education.instructors IS 'Instructor profiles linked to auth.users'; COMMENT ON COLUMN education.instructors.user_id IS 'Reference to auth.users - one user can only be one instructor'; COMMENT ON COLUMN education.instructors.display_name IS 'Public display name for the instructor'; COMMENT ON COLUMN education.instructors.expertise IS 'Array of expertise areas (e.g., trading, forex, crypto)'; COMMENT ON COLUMN education.instructors.social_links IS 'JSON with social media links (twitter, linkedin, youtube)'; COMMENT ON COLUMN education.instructors.total_courses IS 'Denormalized count of active courses'; COMMENT ON COLUMN education.instructors.total_students IS 'Denormalized count of unique students'; COMMENT ON COLUMN education.instructors.total_reviews IS 'Denormalized count of course reviews'; COMMENT ON COLUMN education.instructors.average_rating IS 'Calculated average rating from course reviews'; COMMENT ON COLUMN education.instructors.metadata IS 'Additional metadata in JSON format'; -- ===================================================== -- MIGRATION NOTE: education.courses.instructor_id -- ===================================================== -- The current education.courses table has instructor_id -- referencing auth.users(id). A migration is needed to: -- -- 1. Ensure instructors exist for current course instructors: -- INSERT INTO education.instructors (user_id, display_name) -- SELECT DISTINCT instructor_id, instructor_name -- FROM education.courses -- WHERE instructor_id NOT IN (SELECT user_id FROM education.instructors); -- -- 2. Change the FK reference (requires careful migration): -- ALTER TABLE education.courses -- DROP CONSTRAINT courses_instructor_id_fkey; -- -- ALTER TABLE education.courses -- ADD CONSTRAINT courses_instructor_id_fkey -- FOREIGN KEY (instructor_id) REFERENCES education.instructors(id); -- -- NOTE: This migration should be created in migrations/ folder -- when the team decides to implement the full instructor system. -- =====================================================