|
|
|
|
|
|
|
|
|
|
|
CREATE TABLE IF NOT EXISTS topics ( |
|
|
id SERIAL PRIMARY KEY, |
|
|
name VARCHAR(100) NOT NULL UNIQUE, |
|
|
description TEXT, |
|
|
difficulty VARCHAR(20) DEFAULT 'medium' CHECK (difficulty IN ('easy', 'medium', 'hard')), |
|
|
word_count INTEGER DEFAULT 0, |
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, |
|
|
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP |
|
|
); |
|
|
|
|
|
CREATE TABLE IF NOT EXISTS words ( |
|
|
id SERIAL PRIMARY KEY, |
|
|
word VARCHAR(50) NOT NULL, |
|
|
length INTEGER NOT NULL, |
|
|
topic_id INTEGER REFERENCES topics(id) ON DELETE CASCADE, |
|
|
difficulty VARCHAR(20) DEFAULT 'medium' CHECK (difficulty IN ('easy', 'medium', 'hard')), |
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, |
|
|
CONSTRAINT unique_word_topic UNIQUE (word, topic_id) |
|
|
); |
|
|
|
|
|
CREATE TABLE IF NOT EXISTS clues ( |
|
|
id SERIAL PRIMARY KEY, |
|
|
word_id INTEGER REFERENCES words(id) ON DELETE CASCADE, |
|
|
clue_text TEXT NOT NULL, |
|
|
difficulty VARCHAR(20) DEFAULT 'medium' CHECK (difficulty IN ('easy', 'medium', 'hard')), |
|
|
is_primary BOOLEAN DEFAULT true, |
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP |
|
|
); |
|
|
|
|
|
CREATE TABLE IF NOT EXISTS generated_puzzles ( |
|
|
id SERIAL PRIMARY KEY, |
|
|
grid_data JSONB NOT NULL, |
|
|
clues_data JSONB NOT NULL, |
|
|
metadata JSONB, |
|
|
topics TEXT[] NOT NULL, |
|
|
difficulty VARCHAR(20) DEFAULT 'medium', |
|
|
word_count INTEGER, |
|
|
grid_size INTEGER, |
|
|
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, |
|
|
expires_at TIMESTAMP WITH TIME ZONE DEFAULT (CURRENT_TIMESTAMP + INTERVAL '24 hours') |
|
|
); |
|
|
|
|
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_words_topic_id ON words(topic_id); |
|
|
CREATE INDEX IF NOT EXISTS idx_words_length ON words(length); |
|
|
CREATE INDEX IF NOT EXISTS idx_words_difficulty ON words(difficulty); |
|
|
CREATE INDEX IF NOT EXISTS idx_clues_word_id ON clues(word_id); |
|
|
CREATE INDEX IF NOT EXISTS idx_clues_primary ON clues(is_primary); |
|
|
CREATE INDEX IF NOT EXISTS idx_puzzles_topics ON generated_puzzles USING GIN(topics); |
|
|
CREATE INDEX IF NOT EXISTS idx_puzzles_difficulty ON generated_puzzles(difficulty); |
|
|
CREATE INDEX IF NOT EXISTS idx_puzzles_expires ON generated_puzzles(expires_at); |
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION update_topic_word_count() |
|
|
RETURNS TRIGGER AS $$ |
|
|
BEGIN |
|
|
IF TG_OP = 'INSERT' THEN |
|
|
UPDATE topics |
|
|
SET word_count = word_count + 1, updated_at = CURRENT_TIMESTAMP |
|
|
WHERE id = NEW.topic_id; |
|
|
RETURN NEW; |
|
|
ELSIF TG_OP = 'DELETE' THEN |
|
|
UPDATE topics |
|
|
SET word_count = word_count - 1, updated_at = CURRENT_TIMESTAMP |
|
|
WHERE id = OLD.topic_id; |
|
|
RETURN OLD; |
|
|
END IF; |
|
|
RETURN NULL; |
|
|
END; |
|
|
$$ LANGUAGE plpgsql; |
|
|
|
|
|
|
|
|
CREATE TRIGGER trigger_update_word_count |
|
|
AFTER INSERT OR DELETE ON words |
|
|
FOR EACH ROW |
|
|
EXECUTE FUNCTION update_topic_word_count(); |
|
|
|
|
|
|
|
|
CREATE OR REPLACE FUNCTION cleanup_expired_puzzles() |
|
|
RETURNS INTEGER AS $$ |
|
|
DECLARE |
|
|
deleted_count INTEGER; |
|
|
BEGIN |
|
|
DELETE FROM generated_puzzles WHERE expires_at < CURRENT_TIMESTAMP; |
|
|
GET DIAGNOSTICS deleted_count = ROW_COUNT; |
|
|
RETURN deleted_count; |
|
|
END; |
|
|
$$ LANGUAGE plpgsql; |