-- Dating App Database Schema -- PostgreSQL 15+ Compatible -- Run this script to create the database and all tables -- ============================================================================ -- DATABASE CREATION -- ============================================================================ -- Create the database user (if doesn't exist) DO $do$ BEGIN CREATE ROLE dating_app_user WITH LOGIN PASSWORD 'Aa123456'; EXCEPTION WHEN DUPLICATE_OBJECT THEN RAISE NOTICE 'Role dating_app_user already exists'; END $do$; -- Grant privileges to user before database creation ALTER DEFAULT PRIVILEGES GRANT ALL ON TABLES TO dating_app_user; ALTER DEFAULT PRIVILEGES GRANT ALL ON SEQUENCES TO dating_app_user; -- Create the database owned by dating_app_user CREATE DATABASE dating_app OWNER dating_app_user; -- Grant connection privileges GRANT CONNECT ON DATABASE dating_app TO dating_app_user; GRANT USAGE ON SCHEMA public TO dating_app_user; GRANT CREATE ON SCHEMA public TO dating_app_user; -- ============================================================================ -- TABLE: USERS -- ============================================================================ -- Stores user account information CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, hashed_password VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); -- ============================================================================ -- TABLE: PROFILES -- ============================================================================ -- Stores user profile information CREATE TABLE IF NOT EXISTS profiles ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL UNIQUE, display_name VARCHAR(255) NOT NULL, age INTEGER NOT NULL, gender VARCHAR(50) NOT NULL, location VARCHAR(255) NOT NULL, bio TEXT, interests JSONB DEFAULT '[]', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_profiles_user_id ON profiles(user_id); -- ============================================================================ -- TABLE: PHOTOS -- ============================================================================ -- Stores user profile photos CREATE TABLE IF NOT EXISTS photos ( id SERIAL PRIMARY KEY, profile_id INTEGER NOT NULL, file_path VARCHAR(255) NOT NULL, display_order INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_photos_profile_id ON photos(profile_id); -- ============================================================================ -- TABLE: LIKES -- ============================================================================ -- Tracks which users like which other users CREATE TABLE IF NOT EXISTS likes ( id SERIAL PRIMARY KEY, liker_id INTEGER NOT NULL, liked_id INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(liker_id, liked_id), FOREIGN KEY (liker_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (liked_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_likes_liker_id ON likes(liker_id); CREATE INDEX IF NOT EXISTS idx_likes_liked_id ON likes(liked_id); -- ============================================================================ -- TABLE: CONVERSATIONS -- ============================================================================ -- Stores 1:1 chat conversations between users CREATE TABLE IF NOT EXISTS conversations ( id SERIAL PRIMARY KEY, user_id_1 INTEGER NOT NULL, user_id_2 INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id_1, user_id_2), FOREIGN KEY (user_id_1) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (user_id_2) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_conversations_users ON conversations(user_id_1, user_id_2); -- ============================================================================ -- TABLE: MESSAGES -- ============================================================================ -- Stores individual messages in conversations CREATE TABLE IF NOT EXISTS messages ( id SERIAL PRIMARY KEY, conversation_id INTEGER NOT NULL, sender_id INTEGER NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE, FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_messages_conversation_id ON messages(conversation_id); CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at); -- ============================================================================ -- SAMPLE DATA (Optional - Uncomment to insert test users) -- ============================================================================ -- Test user 1: Alice (Password hash for 'password123') INSERT INTO users (email, hashed_password) VALUES ('alice@example.com', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5YmMxSUmGEJiq') ON CONFLICT (email) DO NOTHING; -- Test user 2: Bob INSERT INTO users (email, hashed_password) VALUES ('bob@example.com', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5YmMxSUmGEJiq') ON CONFLICT (email) DO NOTHING; -- Test user 3: Charlie INSERT INTO users (email, hashed_password) VALUES ('charlie@example.com', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5YmMxSUmGEJiq') ON CONFLICT (email) DO NOTHING; -- Test user 4: Diana INSERT INTO users (email, hashed_password) VALUES ('diana@example.com', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5YmMxSUmGEJiq') ON CONFLICT (email) DO NOTHING; -- ============================================================================ -- SAMPLE PROFILES (Optional - Uncomment to create test profiles) -- ============================================================================ -- Alice's profile INSERT INTO profiles (user_id, display_name, age, gender, location, bio, interests) VALUES ( (SELECT id FROM users WHERE email = 'alice@example.com'), 'Alice', 28, 'Female', 'San Francisco, CA', 'Love hiking and coffee. Looking for genuine connection.', '["hiking", "coffee", "reading", "travel"]' ) ON CONFLICT (user_id) DO NOTHING; -- Bob's profile INSERT INTO profiles (user_id, display_name, age, gender, location, bio, interests) VALUES ( (SELECT id FROM users WHERE email = 'bob@example.com'), 'Bob', 30, 'Male', 'San Francisco, CA', 'Software engineer who enjoys cooking and photography.', '["cooking", "photography", "gaming", "travel"]' ) ON CONFLICT (user_id) DO NOTHING; -- Charlie's profile INSERT INTO profiles (user_id, display_name, age, gender, location, bio, interests) VALUES ( (SELECT id FROM users WHERE email = 'charlie@example.com'), 'Charlie', 27, 'Male', 'Los Angeles, CA', 'Designer and musician. Love live music and good conversation.', '["music", "design", "art", "travel"]' ) ON CONFLICT (user_id) DO NOTHING; -- Diana's profile INSERT INTO profiles (user_id, display_name, age, gender, location, bio, interests) VALUES ( (SELECT id FROM users WHERE email = 'diana@example.com'), 'Diana', 26, 'Female', 'Los Angeles, CA', 'Yoga instructor and nature lover. Adventure seeker!', '["yoga", "hiking", "nature", "travel"]' ) ON CONFLICT (user_id) DO NOTHING; -- ============================================================================ -- SAMPLE LIKES (Optional - Uncomment to create test likes) -- ============================================================================ -- Alice likes Bob INSERT INTO likes (liker_id, liked_id) SELECT (SELECT id FROM users WHERE email = 'alice@example.com'), (SELECT id FROM users WHERE email = 'bob@example.com') WHERE NOT EXISTS ( SELECT 1 FROM likes WHERE liker_id = (SELECT id FROM users WHERE email = 'alice@example.com') AND liked_id = (SELECT id FROM users WHERE email = 'bob@example.com') ); -- Bob likes Alice (MATCH!) INSERT INTO likes (liker_id, liked_id) SELECT (SELECT id FROM users WHERE email = 'bob@example.com'), (SELECT id FROM users WHERE email = 'alice@example.com') WHERE NOT EXISTS ( SELECT 1 FROM likes WHERE liker_id = (SELECT id FROM users WHERE email = 'bob@example.com') AND liked_id = (SELECT id FROM users WHERE email = 'alice@example.com') ); -- Charlie likes Diana INSERT INTO likes (liker_id, liked_id) SELECT (SELECT id FROM users WHERE email = 'charlie@example.com'), (SELECT id FROM users WHERE email = 'diana@example.com') WHERE NOT EXISTS ( SELECT 1 FROM likes WHERE liker_id = (SELECT id FROM users WHERE email = 'charlie@example.com') AND liked_id = (SELECT id FROM users WHERE email = 'diana@example.com') ); -- ============================================================================ -- SAMPLE CONVERSATION (Optional - Uncomment for test chat) -- ============================================================================ -- Create conversation between Alice and Bob (they matched!) INSERT INTO conversations (user_id_1, user_id_2) SELECT (SELECT id FROM users WHERE email = 'alice@example.com'), (SELECT id FROM users WHERE email = 'bob@example.com') WHERE NOT EXISTS ( SELECT 1 FROM conversations WHERE (user_id_1 = (SELECT id FROM users WHERE email = 'alice@example.com') AND user_id_2 = (SELECT id FROM users WHERE email = 'bob@example.com')) ); -- Sample messages in conversation INSERT INTO messages (conversation_id, sender_id, content) SELECT c.id, (SELECT id FROM users WHERE email = 'alice@example.com'), 'Hi Bob! Love your photography page.' FROM conversations c WHERE c.user_id_1 = (SELECT id FROM users WHERE email = 'alice@example.com') AND c.user_id_2 = (SELECT id FROM users WHERE email = 'bob@example.com') AND NOT EXISTS ( SELECT 1 FROM messages WHERE conversation_id = c.id ); INSERT INTO messages (conversation_id, sender_id, content) SELECT c.id, (SELECT id FROM users WHERE email = 'bob@example.com'), 'Thanks Alice! Would love to grab coffee sometime?' FROM conversations c WHERE c.user_id_1 = (SELECT id FROM users WHERE email = 'alice@example.com') AND c.user_id_2 = (SELECT id FROM users WHERE email = 'bob@example.com') AND (SELECT COUNT(*) FROM messages WHERE conversation_id = c.id) < 2; -- ============================================================================ -- VERIFICATION QUERIES -- ============================================================================ -- Run these queries to verify everything was created correctly: -- SELECT COUNT(*) as user_count FROM users; -- SELECT COUNT(*) as profile_count FROM profiles; -- SELECT COUNT(*) as photo_count FROM photos; -- SELECT COUNT(*) as like_count FROM likes; -- SELECT COUNT(*) as conversation_count FROM conversations; -- SELECT COUNT(*) as message_count FROM messages; -- ============================================================================ -- NOTES -- ============================================================================ -- -- Password hashes used in sample data: -- - Hash: $2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5YmMxSUmGEJiq -- - Password: 'password123' -- -- To generate your own bcrypt hash, use Python: -- from passlib.context import CryptContext -- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") -- hash = pwd_context.hash("your_password_here") -- -- IMPORTANT BEFORE PRODUCTION: -- 1. Change all password hashes to actual user passwords -- 2. Update email addresses to real users -- 3. Consider using proper user import/registration instead of direct inserts -- 4. Remove sample data if not needed -- -- DATABASE CONNECTION INFO: -- Database: dating_app -- Host: localhost (or your PostgreSQL host) -- Port: 5432 (default) -- User: postgres (or your database user) -- Password: (set when installing PostgreSQL) -- -- ============================================================================