-- Enable the pgvector extension to work with embeddings CREATE EXTENSION IF NOT EXISTS vector; -- Users Table (Recruiters / Managers) CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT UNIQUE NOT NULL, role TEXT NOT NULL CHECK (role IN ('recruiter', 'manager')), created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- Jobs Table CREATE TABLE IF NOT EXISTS jobs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title TEXT NOT NULL, description TEXT NOT NULL, requirements TEXT, embedding vector(1536), created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- Candidates Table CREATE TABLE IF NOT EXISTS candidates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, phone TEXT, resume_url TEXT, summary TEXT, seniority TEXT, embedding vector(1536), created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- Interviews Table CREATE TABLE IF NOT EXISTS interviews ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), candidate_id UUID REFERENCES candidates(id) ON DELETE CASCADE NOT NULL, job_id UUID REFERENCES jobs(id) ON DELETE CASCADE NOT NULL, scheduled_at TIMESTAMP WITH TIME ZONE NOT NULL, status TEXT NOT NULL CHECK (status IN ('scheduled', 'completed', 'cancelled')) DEFAULT 'scheduled', notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- Scores Table CREATE TABLE IF NOT EXISTS scores ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), candidate_id UUID REFERENCES candidates(id) ON DELETE CASCADE NOT NULL, job_id UUID REFERENCES jobs(id) ON DELETE CASCADE NOT NULL, interviewer_id UUID REFERENCES users(id) ON DELETE CASCADE, score NUMERIC NOT NULL CHECK (score >= 0 AND score <= 100), evaluation JSONB, -- MUST contain exactly: summary, classification, suggestions, riskLevel created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now()) NOT NULL ); -- RPC Function to Match Candidates using Cosine Distance CREATE OR REPLACE FUNCTION match_candidates ( query_embedding vector(1536), match_threshold FLOAT, match_count INT ) RETURNS TABLE ( id UUID, name TEXT, email TEXT, seniority TEXT, similarity FLOAT ) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT candidates.id, candidates.name, candidates.email, candidates.seniority, (1 - (candidates.embedding <=> query_embedding))::FLOAT AS similarity FROM candidates WHERE (1 - (candidates.embedding <=> query_embedding)) > match_threshold ORDER BY candidates.embedding <=> query_embedding ASC LIMIT match_count; END; $$;