-- ============================================================ -- schema.sql – схема базы данных PostgreSQL для RAG-сервера -- Поддерживает личные, глобальные и комнатные документы, -- а также историю диалогов (личных и групповых). -- Используется единой базой данных для всех ботов. -- =================-- ============================================================ -- schema.sql – схема базы данных PostgreSQL для RAG-сервера -- Добавлена поддержка флага indexed для транзакционности индексации -- ============================================================ -- ------------------------------------------------------------------ -- 1. Таблица documents (метаданные загруженных документов) -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS documents ( id SERIAL PRIMARY KEY, source_name VARCHAR(255) NOT NULL, upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, owner_jid VARCHAR(255) NOT NULL, is_global BOOLEAN DEFAULT FALSE, metadata JSONB, -- PostgreSQL JSONB room_jid VARCHAR(255) DEFAULT NULL, -- NULL – личный/глобальный; иначе JID комнаты file_hash VARCHAR(64) DEFAULT NULL, -- SHA-256 хеш содержимого (для обновления версий) indexed BOOLEAN DEFAULT FALSE -- TRUE, если чанки успешно загружены в Qdrant ); -- Индексы для ускорения запросов CREATE INDEX IF NOT EXISTS idx_documents_owner ON documents(owner_jid); CREATE INDEX IF NOT EXISTS idx_documents_global ON documents(is_global); CREATE INDEX IF NOT EXISTS idx_documents_room ON documents(room_jid); CREATE INDEX IF NOT EXISTS idx_documents_indexed ON documents(indexed); -- новый индекс -- Полнотекстовый индекс (GIN) для source_name (русский язык) CREATE INDEX IF NOT EXISTS ft_source_name ON documents USING GIN (to_tsvector('russian', source_name)); -- ------------------------------------------------------------------ -- 2. Таблица document_access (права доступа к личным документам) -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS document_access ( doc_id INT, user_jid VARCHAR(255), PRIMARY KEY (doc_id, user_jid) ); CREATE INDEX IF NOT EXISTS idx_access_user ON document_access(user_jid); -- ------------------------------------------------------------------ -- 3. Таблица history (история диалогов) -- ------------------------------------------------------------------ DO $$ BEGIN CREATE TYPE history_role AS ENUM ('user', 'assistant'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TABLE IF NOT EXISTS history ( id SERIAL PRIMARY KEY, user_jid VARCHAR(255) NOT NULL, role history_role NOT NULL, content TEXT, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, room_jid VARCHAR(255) DEFAULT NULL -- NULL – личный диалог; иначе JID комнаты ); CREATE INDEX IF NOT EXISTS idx_history_user ON history(user_jid); CREATE INDEX IF NOT EXISTS idx_history_room ON history(room_jid); CREATE INDEX IF NOT EXISTS idx_history_time ON history(timestamp); -- ------------------------------------------------------------------ -- 4. Таблица rooms (список MUC комнат, в которых бот должен состоять) -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS rooms ( room_jid VARCHAR(255) PRIMARY KEY ); -- ------------------------------------------------------------------ -- 5. Таблица room_templates (шаблоны коробочных решений, сохранённые в комнатах) -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS room_templates ( room_jid VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, file_path TEXT NOT NULL, file_hash VARCHAR(64), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (room_jid, name) ); -- ------------------------------------------------------------------ -- 6. Таблица feedback (оценки пользователей) – для будущего сбора обратной связи -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS feedback ( id SERIAL PRIMARY KEY, user_jid VARCHAR(255) NOT NULL, rating VARCHAR(50) NOT NULL, comment TEXT, room_jid VARCHAR(255) DEFAULT NULL, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_feedback_user ON feedback(user_jid); -- ============================================================ -- Миграции (если переезжаете с предыдущей версии) -- ============================================================ -- Добавление колонки room_jid в documents (если отсутствует) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='documents' AND column_name='room_jid') THEN ALTER TABLE documents ADD COLUMN room_jid VARCHAR(255) DEFAULT NULL; END IF; END $$; -- Добавление колонки file_hash в documents DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='documents' AND column_name='file_hash') THEN ALTER TABLE documents ADD COLUMN file_hash VARCHAR(64) DEFAULT NULL; END IF; END $$; -- Добавление колонки room_jid в history DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='history' AND column_name='room_jid') THEN ALTER TABLE history ADD COLUMN room_jid VARCHAR(255) DEFAULT NULL; END IF; END $$; -- Добавление колонки indexed (новое) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='documents' AND column_name='indexed') THEN ALTER TABLE documents ADD COLUMN indexed BOOLEAN DEFAULT FALSE; -- Для существующих документов считаем, что они уже проиндексированы UPDATE documents SET indexed = TRUE; -- Создаём индекс CREATE INDEX IF NOT EXISTS idx_documents_indexed ON documents(indexed); END IF; END $$; -- Создание индексов (если отсутствуют) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname='idx_documents_room') THEN CREATE INDEX idx_documents_room ON documents(room_jid); END IF; END $$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname='idx_history_room') THEN CREATE INDEX idx_history_room ON history(room_jid); END IF; END $$;=========================================== -- ------------------------------------------------------------------ -- 1. Таблица documents (метаданные загруженных документов) -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS documents ( id SERIAL PRIMARY KEY, source_name VARCHAR(255) NOT NULL, upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, owner_jid VARCHAR(255) NOT NULL, is_global BOOLEAN DEFAULT FALSE, metadata JSONB, -- PostgreSQL JSONB room_jid VARCHAR(255) DEFAULT NULL, -- NULL – личный/глобальный; иначе JID комнаты file_hash VARCHAR(64) DEFAULT NULL -- SHA-256 хеш содержимого (для обновления версий) ); -- Индексы для ускорения запросов CREATE INDEX IF NOT EXISTS idx_documents_owner ON documents(owner_jid); CREATE INDEX IF NOT EXISTS idx_documents_global ON documents(is_global); CREATE INDEX IF NOT EXISTS idx_documents_room ON documents(room_jid); -- Полнотекстовый индекс (GIN) для source_name (русский язык) CREATE INDEX IF NOT EXISTS ft_source_name ON documents USING GIN (to_tsvector('russian', source_name)); -- ------------------------------------------------------------------ -- 2. Таблица document_access (права доступа к личным документам) -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS document_access ( doc_id INT, user_jid VARCHAR(255), PRIMARY KEY (doc_id, user_jid) ); CREATE INDEX IF NOT EXISTS idx_access_user ON document_access(user_jid); -- ------------------------------------------------------------------ -- 3. Таблица history (история диалогов) -- ------------------------------------------------------------------ DO $$ BEGIN CREATE TYPE history_role AS ENUM ('user', 'assistant'); EXCEPTION WHEN duplicate_object THEN NULL; END $$; CREATE TABLE IF NOT EXISTS history ( id SERIAL PRIMARY KEY, user_jid VARCHAR(255) NOT NULL, role history_role NOT NULL, content TEXT, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, room_jid VARCHAR(255) DEFAULT NULL -- NULL – личный диалог; иначе JID комнаты ); CREATE INDEX IF NOT EXISTS idx_history_user ON history(user_jid); CREATE INDEX IF NOT EXISTS idx_history_room ON history(room_jid); CREATE INDEX IF NOT EXISTS idx_history_time ON history(timestamp); -- ------------------------------------------------------------------ -- 4. Таблица rooms (список MUC комнат, в которых бот должен состоять) -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS rooms ( room_jid VARCHAR(255) PRIMARY KEY ); -- ------------------------------------------------------------------ -- 5. Таблица room_templates (шаблоны коробочных решений, сохранённые в комнатах) -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS room_templates ( room_jid VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, file_path TEXT NOT NULL, file_hash VARCHAR(64), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (room_jid, name) ); -- ------------------------------------------------------------------ -- 6. Таблица feedback (оценки пользователей) – для будущего сбора обратной связи -- ------------------------------------------------------------------ CREATE TABLE IF NOT EXISTS feedback ( id SERIAL PRIMARY KEY, user_jid VARCHAR(255) NOT NULL, rating VARCHAR(50) NOT NULL, comment TEXT, room_jid VARCHAR(255) DEFAULT NULL, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_feedback_user ON feedback(user_jid); -- ============================================================ -- Миграции (если переезжаете с предыдущей версии) -- ============================================================ -- Добавление колонки room_jid в documents (если отсутствует) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='documents' AND column_name='room_jid') THEN ALTER TABLE documents ADD COLUMN room_jid VARCHAR(255) DEFAULT NULL; END IF; END $$; -- Добавление колонки file_hash в documents DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='documents' AND column_name='file_hash') THEN ALTER TABLE documents ADD COLUMN file_hash VARCHAR(64) DEFAULT NULL; END IF; END $$; -- Добавление колонки room_jid в history DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name='history' AND column_name='room_jid') THEN ALTER TABLE history ADD COLUMN room_jid VARCHAR(255) DEFAULT NULL; END IF; END $$; -- Создание индексов (если отсутствуют) DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname='idx_documents_room') THEN CREATE INDEX idx_documents_room ON documents(room_jid); END IF; END $$; DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname='idx_history_room') THEN CREATE INDEX idx_history_room ON history(room_jid); END IF; END $$;