CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE IF NOT EXISTS thoughts ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, user_id TEXT NOT NULL DEFAULT 'default', content TEXT NOT NULL, embedding VECTOR(1536), metadata JSONB DEFAULT '{}'::jsonb, source TEXT NOT NULL DEFAULT 'mcp', created_at TIMESTAMPTZ DEFAULT now(), updated_at TIMESTAMPTZ DEFAULT now() ); CREATE INDEX IF NOT EXISTS thoughts_embedding_idx ON thoughts USING hnsw (embedding vector_cosine_ops); CREATE INDEX IF NOT EXISTS thoughts_metadata_idx ON thoughts USING gin (metadata); CREATE INDEX IF NOT EXISTS thoughts_created_at_idx ON thoughts (created_at DESC); CREATE INDEX IF NOT EXISTS thoughts_user_id_idx ON thoughts (user_id); -- Auto-update trigger for updated_at CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS thoughts_updated_at ON thoughts; CREATE TRIGGER thoughts_updated_at BEFORE UPDATE ON thoughts FOR EACH ROW EXECUTE FUNCTION update_updated_at(); -- Semantic search function CREATE OR REPLACE FUNCTION match_thoughts( query_embedding VECTOR(1536), match_threshold FLOAT DEFAULT 0.7, match_count INT DEFAULT 10, filter JSONB DEFAULT '{}'::jsonb ) RETURNS TABLE ( id UUID, content TEXT, metadata JSONB, similarity FLOAT, created_at TIMESTAMPTZ ) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT t.id, t.content, t.metadata, (1 - (t.embedding <=> query_embedding))::FLOAT AS similarity, t.created_at FROM thoughts t WHERE 1 - (t.embedding <=> query_embedding) > match_threshold ORDER BY t.embedding <=> query_embedding LIMIT match_count; END; $$;