MySQL وتصميم قواعد البيانات

تصميم قاعدة بيانات وسائل التواصل الاجتماعي

13 دقيقة الدرس 36 من 40

تصميم قاعدة بيانات وسائل التواصل الاجتماعي

تتطلب منصات وسائل التواصل الاجتماعي تصاميم قواعد بيانات معقدة للتعامل مع تفاعلات المستخدمين وإنشاء المحتوى والاتصالات الاجتماعية والإشعارات في الوقت الفعلي. في هذا الدرس، سنصمم قاعدة بيانات وسائل تواصل اجتماعي كاملة تدعم المنشورات والتعليقات والإعجابات والمتابعات والمراسلة.

ميزات وسائل التواصل الاجتماعي الأساسية

تحتاج منصة وسائل التواصل الاجتماعي النموذجية إلى:

إدارة المستخدمين: - ملفات تعريف المستخدمين مع السيرة الذاتية والصورة الرمزية - إعدادات خصوصية الحساب - التحقق من المستخدم/الشارات التفاعلات الاجتماعية: - علاقات المتابعة/إلغاء المتابعة - منشورات مع نص وصور وفيديوهات - تعليقات وردود متداخلة - إعجابات وتفاعلات - مشاركات/إعادة تغريد الاتصال: - الرسائل الخاصة - محادثات جماعية - نظام الإشعارات - توليد موجز النشاط

مخطط وسائل التواصل الاجتماعي الكامل

لنبني هيكل قاعدة البيانات:

-- جدول المستخدمين CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, full_name VARCHAR(100) NOT NULL, bio TEXT, avatar_url VARCHAR(500), cover_photo_url VARCHAR(500), website VARCHAR(255), location VARCHAR(100), is_verified BOOLEAN DEFAULT FALSE, is_private BOOLEAN DEFAULT FALSE, is_active BOOLEAN DEFAULT TRUE, date_of_birth DATE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, last_seen_at TIMESTAMP NULL, INDEX idx_username (username), INDEX idx_email (email), INDEX idx_verified (is_verified), FULLTEXT idx_search (username, full_name, bio) ) ENGINE=InnoDB; -- المتابعات/الاتصالات CREATE TABLE follows ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, follower_id BIGINT UNSIGNED NOT NULL COMMENT 'المستخدم الذي يتابع', following_id BIGINT UNSIGNED NOT NULL COMMENT 'المستخدم المتابَع', status ENUM('pending', 'accepted', 'blocked') DEFAULT 'accepted', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (follower_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (following_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE KEY unique_follow (follower_id, following_id), INDEX idx_follower (follower_id), INDEX idx_following (following_id), INDEX idx_status (status), CHECK (follower_id != following_id) ) ENGINE=InnoDB; -- المنشورات CREATE TABLE posts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, content TEXT NOT NULL, media_type ENUM('none', 'image', 'video', 'link') DEFAULT 'none', media_url VARCHAR(500), link_preview_title VARCHAR(255), link_preview_description TEXT, link_preview_image VARCHAR(500), is_public BOOLEAN DEFAULT TRUE, allow_comments BOOLEAN DEFAULT TRUE, location VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_user (user_id), INDEX idx_created (created_at), INDEX idx_public (is_public, created_at), FULLTEXT idx_content (content) ) ENGINE=InnoDB; -- وسائط المنشور (صور/فيديوهات متعددة لكل منشور) CREATE TABLE post_media ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, post_id BIGINT UNSIGNED NOT NULL, media_type ENUM('image', 'video') NOT NULL, media_url VARCHAR(500) NOT NULL, thumbnail_url VARCHAR(500), display_order INT DEFAULT 0, width INT UNSIGNED, height INT UNSIGNED, duration INT UNSIGNED COMMENT 'مدة الفيديو بالثواني', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE, INDEX idx_post (post_id) ) ENGINE=InnoDB; -- التعليقات CREATE TABLE comments ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, post_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, parent_comment_id BIGINT UNSIGNED NULL COMMENT 'للردود المتداخلة', content TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (parent_comment_id) REFERENCES comments(id) ON DELETE CASCADE, INDEX idx_post (post_id, created_at), INDEX idx_user (user_id), INDEX idx_parent (parent_comment_id), FULLTEXT idx_content (content) ) ENGINE=InnoDB; -- الإعجابات (متعددة الأشكال - يمكن الإعجاب بالمنشورات أو التعليقات) CREATE TABLE likes ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, likeable_type ENUM('post', 'comment') NOT NULL, likeable_id BIGINT UNSIGNED NOT NULL, reaction_type ENUM('like', 'love', 'haha', 'wow', 'sad', 'angry') DEFAULT 'like', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE KEY unique_like (user_id, likeable_type, likeable_id), INDEX idx_likeable (likeable_type, likeable_id), INDEX idx_user (user_id) ) ENGINE=InnoDB; -- المشاركات/إعادة التغريد CREATE TABLE shares ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL COMMENT 'المستخدم الذي شارك', original_post_id BIGINT UNSIGNED NOT NULL, caption TEXT COMMENT 'تعليق اختياري عند المشاركة', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (original_post_id) REFERENCES posts(id) ON DELETE CASCADE, INDEX idx_user (user_id), INDEX idx_post (original_post_id), INDEX idx_created (created_at) ) ENGINE=InnoDB; -- الهاشتاقات CREATE TABLE hashtags ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) UNIQUE NOT NULL, post_count INT UNSIGNED DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX idx_name (name), INDEX idx_trending (post_count, created_at) ) ENGINE=InnoDB; -- هاشتاقات المنشور (متعدد إلى متعدد) CREATE TABLE post_hashtags ( post_id BIGINT UNSIGNED NOT NULL, hashtag_id BIGINT UNSIGNED NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (post_id, hashtag_id), FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE, FOREIGN KEY (hashtag_id) REFERENCES hashtags(id) ON DELETE CASCADE, INDEX idx_hashtag (hashtag_id) ) ENGINE=InnoDB; -- المحادثات (للرسائل المباشرة) CREATE TABLE conversations ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, is_group BOOLEAN DEFAULT FALSE, group_name VARCHAR(100), created_by BIGINT UNSIGNED NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE RESTRICT, INDEX idx_group (is_group) ) ENGINE=InnoDB; -- مشاركو المحادثة CREATE TABLE conversation_participants ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, conversation_id BIGINT UNSIGNED NOT NULL, user_id BIGINT UNSIGNED NOT NULL, joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, left_at TIMESTAMP NULL, last_read_at TIMESTAMP NULL, is_muted BOOLEAN DEFAULT FALSE, FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE KEY unique_participant (conversation_id, user_id), INDEX idx_user (user_id), INDEX idx_active (conversation_id, left_at) ) ENGINE=InnoDB; -- الرسائل CREATE TABLE messages ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, conversation_id BIGINT UNSIGNED NOT NULL, sender_id BIGINT UNSIGNED NOT NULL, content TEXT NOT NULL, media_url VARCHAR(500), media_type ENUM('none', 'image', 'video', 'file') DEFAULT 'none', is_deleted BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE, FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE RESTRICT, INDEX idx_conversation (conversation_id, created_at), INDEX idx_sender (sender_id) ) ENGINE=InnoDB; -- الإشعارات CREATE TABLE notifications ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL COMMENT 'المستلم', actor_id BIGINT UNSIGNED NOT NULL COMMENT 'المستخدم الذي بدأ', notification_type ENUM( 'follow', 'like', 'comment', 'share', 'mention', 'message', 'tag' ) NOT NULL, entity_type ENUM('post', 'comment', 'message') NULL, entity_id BIGINT UNSIGNED NULL, content TEXT, is_read BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (actor_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_user_unread (user_id, is_read, created_at), INDEX idx_actor (actor_id), INDEX idx_type (notification_type) ) ENGINE=InnoDB; -- حظر المستخدمين CREATE TABLE blocks ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, blocker_id BIGINT UNSIGNED NOT NULL, blocked_id BIGINT UNSIGNED NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (blocker_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (blocked_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE KEY unique_block (blocker_id, blocked_id), INDEX idx_blocker (blocker_id), INDEX idx_blocked (blocked_id) ) ENGINE=InnoDB; -- المنشورات المحفوظة (الإشارات المرجعية) CREATE TABLE saved_posts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, post_id BIGINT UNSIGNED NOT NULL, collection_name VARCHAR(100) DEFAULT 'default', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE, UNIQUE KEY unique_saved (user_id, post_id), INDEX idx_user (user_id, collection_name) ) ENGINE=InnoDB; -- التقارير (للإشراف على المحتوى) CREATE TABLE reports ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, reporter_id BIGINT UNSIGNED NOT NULL, reported_type ENUM('user', 'post', 'comment') NOT NULL, reported_id BIGINT UNSIGNED NOT NULL, reason ENUM( 'spam', 'harassment', 'hate_speech', 'violence', 'false_information', 'other' ) NOT NULL, description TEXT, status ENUM('pending', 'reviewing', 'resolved', 'dismissed') DEFAULT 'pending', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, reviewed_at TIMESTAMP NULL, FOREIGN KEY (reporter_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_status (status), INDEX idx_reported (reported_type, reported_id) ) ENGINE=InnoDB;
نمط التصميم: يستخدم جدول likes تصميماً متعدد الأشكال حيث likeable_type و likeable_id يسمحان بالإعجاب على كل من المنشورات والتعليقات دون جداول مكررة.

استعلامات وسائل التواصل الاجتماعي الرئيسية

الاستعلامات الأساسية لوظائف وسائل التواصل الاجتماعي:

-- الحصول على موجز المستخدم (منشورات من المستخدمين المتابعين) SELECT p.id, p.content, p.created_at, u.username, u.full_name, u.avatar_url, COUNT(DISTINCT l.id) AS like_count, COUNT(DISTINCT c.id) AS comment_count, COUNT(DISTINCT s.id) AS share_count, EXISTS( SELECT 1 FROM likes WHERE user_id = 1 AND likeable_type = 'post' AND likeable_id = p.id ) AS is_liked_by_me FROM posts p JOIN users u ON p.user_id = u.id JOIN follows f ON p.user_id = f.following_id LEFT JOIN likes l ON p.id = l.likeable_id AND l.likeable_type = 'post' LEFT JOIN comments c ON p.id = c.post_id LEFT JOIN shares s ON p.id = s.original_post_id WHERE f.follower_id = 1 AND f.status = 'accepted' AND p.is_public = TRUE GROUP BY p.id, p.content, p.created_at, u.username, u.full_name, u.avatar_url ORDER BY p.created_at DESC LIMIT 20; -- الحصول على عدد المتابعين والمتابعات SELECT (SELECT COUNT(*) FROM follows WHERE following_id = 1 AND status = 'accepted') AS followers, (SELECT COUNT(*) FROM follows WHERE follower_id = 1 AND status = 'accepted') AS following; -- الحصول على تفاصيل المنشور مع جميع التفاعلات SELECT p.*, u.username, u.full_name, u.avatar_url, COUNT(DISTINCT l.id) AS like_count, COUNT(DISTINCT c.id) AS comment_count, COUNT(DISTINCT s.id) AS share_count FROM posts p JOIN users u ON p.user_id = u.id LEFT JOIN likes l ON p.id = l.likeable_id AND l.likeable_type = 'post' LEFT JOIN comments c ON p.id = c.post_id LEFT JOIN shares s ON p.id = s.original_post_id WHERE p.id = 100 GROUP BY p.id; -- الحصول على التعليقات مع الردود المتداخلة WITH RECURSIVE comment_tree AS ( -- تعليقات المستوى الأعلى SELECT c.id, c.post_id, c.user_id, c.parent_comment_id, c.content, c.created_at, u.username, u.avatar_url, 0 AS depth, CAST(c.id AS CHAR(500)) AS path FROM comments c JOIN users u ON c.user_id = u.id WHERE c.post_id = 100 AND c.parent_comment_id IS NULL UNION ALL -- الردود المتداخلة SELECT c.id, c.post_id, c.user_id, c.parent_comment_id, c.content, c.created_at, u.username, u.avatar_url, ct.depth + 1, CONCAT(ct.path, '->', c.id) FROM comments c JOIN users u ON c.user_id = u.id JOIN comment_tree ct ON c.parent_comment_id = ct.id WHERE ct.depth < 5 ) SELECT * FROM comment_tree ORDER BY path; -- الحصول على الهاشتاقات الرائجة SELECT h.name, h.post_count, COUNT(DISTINCT ph.post_id) AS recent_posts FROM hashtags h LEFT JOIN post_hashtags ph ON h.id = ph.hashtag_id AND ph.created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR) GROUP BY h.id, h.name, h.post_count ORDER BY recent_posts DESC, h.post_count DESC LIMIT 10; -- الحصول على الإشعارات غير المقروءة SELECT n.id, n.notification_type, n.content, n.created_at, u.username AS actor_username, u.avatar_url AS actor_avatar FROM notifications n JOIN users u ON n.actor_id = u.id WHERE n.user_id = 1 AND n.is_read = FALSE ORDER BY n.created_at DESC LIMIT 50; -- الحصول على قائمة المحادثات مع آخر رسالة SELECT c.id, c.is_group, c.group_name, c.updated_at, m.content AS last_message, m.created_at AS last_message_at, sender.username AS last_sender, COUNT(DISTINCT CASE WHEN m2.created_at > cp.last_read_at THEN m2.id END) AS unread_count FROM conversations c JOIN conversation_participants cp ON c.id = cp.conversation_id LEFT JOIN messages m ON c.id = m.conversation_id AND m.id = ( SELECT id FROM messages WHERE conversation_id = c.id ORDER BY created_at DESC LIMIT 1 ) LEFT JOIN users sender ON m.sender_id = sender.id LEFT JOIN messages m2 ON c.id = m2.conversation_id WHERE cp.user_id = 1 AND cp.left_at IS NULL GROUP BY c.id, c.is_group, c.group_name, c.updated_at, m.content, m.created_at, sender.username ORDER BY c.updated_at DESC;
نصيحة للأداء: بالنسبة لاستعلام الموجز، فكر في استخدام عرض مادي أو طبقة تخزين مؤقت حيث أن هذا هو الاستعلام الأكثر تنفيذاً بشكل متكرر في تطبيقات وسائل التواصل الاجتماعي.

تنفيذ نظام المتابعة

التعامل مع علاقات المتابعة مع الخصوصية:

-- متابعة مستخدم (قبول تلقائي للحسابات العامة) INSERT INTO follows (follower_id, following_id, status) SELECT 1, 2, CASE WHEN is_private = TRUE THEN 'pending' ELSE 'accepted' END FROM users WHERE id = 2; -- قبول طلب المتابعة UPDATE follows SET status = 'accepted' WHERE follower_id = 1 AND following_id = 2 AND status = 'pending'; -- التحقق مما إذا كان المستخدم A يتابع المستخدم B SELECT EXISTS( SELECT 1 FROM follows WHERE follower_id = 1 AND following_id = 2 AND status = 'accepted' ) AS is_following; -- الحصول على المتابعين المتبادلين (الأصدقاء) SELECT DISTINCT u.* FROM users u JOIN follows f1 ON u.id = f1.following_id JOIN follows f2 ON u.id = f2.follower_id WHERE f1.follower_id = 1 AND f2.following_id = 1 AND f1.status = 'accepted' AND f2.status = 'accepted';

موجز النشاط في الوقت الفعلي

توليد موجزات نشاط مخصصة:

-- موجز النشاط المدمج (المنشورات + المشاركات) ( SELECT 'post' AS activity_type, p.id, p.user_id, u.username, p.content, p.created_at FROM posts p JOIN users u ON p.user_id = u.id JOIN follows f ON p.user_id = f.following_id WHERE f.follower_id = 1 AND f.status = 'accepted' ) UNION ALL ( SELECT 'share' AS activity_type, s.id, s.user_id, u.username, s.caption AS content, s.created_at FROM shares s JOIN users u ON s.user_id = u.id JOIN follows f ON s.user_id = f.following_id WHERE f.follower_id = 1 AND f.status = 'accepted' ) ORDER BY created_at DESC LIMIT 50;
تحدي قابلية التوسع: يصبح توليد الموجز بطيئاً مع العديد من المتابعين. فكر في استخدام موجز قائم على الدفع حيث يتم حساب المنشورات مسبقاً وتخزينها في جدول موجز غير منظم أو Redis.

نظام المراسلة المباشرة

تنفيذ المراسلة الكامل:

-- إنشاء محادثة بين مستخدمين INSERT INTO conversations (is_group, created_by) VALUES (FALSE, 1); SET @conv_id = LAST_INSERT_ID(); INSERT INTO conversation_participants (conversation_id, user_id) VALUES (@conv_id, 1), (@conv_id, 2); -- إرسال رسالة INSERT INTO messages (conversation_id, sender_id, content) VALUES (@conv_id, 1, 'مرحباً، كيف حالك؟'); -- وضع علامة مقروءة على الرسائل UPDATE conversation_participants SET last_read_at = NOW() WHERE conversation_id = @conv_id AND user_id = 1; -- الحصول على الرسائل في محادثة SELECT m.id, m.content, m.created_at, u.username, u.avatar_url, m.sender_id = 1 AS is_mine FROM messages m JOIN users u ON m.sender_id = u.id WHERE m.conversation_id = @conv_id AND m.is_deleted = FALSE ORDER BY m.created_at ASC;

استراتيجيات قابلية التوسع

توليد الموجز: - استخدم نموذج الدفع للمستخدمين الذين لديهم أقل من 1000 متابع - استخدم نموذج السحب للمشاهير الذين لديهم ملايين المتابعين - نفذ التوزيع عند الكتابة للمستخدمين النشطين - خزن الموجزات مؤقتاً في Redis مع TTL تجزئة قاعدة البيانات: - قسم المستخدمين حسب نطاقات user_id - احتفظ بالرسم البياني الاجتماعي في مجموعة مخصصة - استخدم التجزئة المتسقة للرسائل - كرر للعمليات ذات القراءة الكثيفة تحسين الأداء: - ترقيم جميع الموجزات بترقيم قائم على المؤشر - غير طبيعية أعداد التفاعل (like_count، comment_count) - استخدم قوائم انتظار الرسائل للإشعارات - أرشف المنشورات القديمة بعد عام واحد - نفذ تحديد المعدل لكل مستخدم

تمرين تطبيقي:

المهمة: إضافة ميزة القصص (منشورات مؤقتة لمدة 24 ساعة).

المتطلبات:

  1. إنشاء جدول قصص مع انتهاء الصلاحية
  2. تتبع من شاهد كل قصة
  3. حذف تلقائي للقصص بعد 24 ساعة
  4. إظهار القصص من المستخدمين المتابعين فقط

الحل:

CREATE TABLE stories ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, media_type ENUM('image', 'video') NOT NULL, media_url VARCHAR(500) NOT NULL, duration INT DEFAULT 15 COMMENT 'مدة العرض بالثواني', expires_at TIMESTAMP NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, INDEX idx_user_active (user_id, expires_at) ) ENGINE=InnoDB; CREATE TABLE story_views ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, story_id BIGINT UNSIGNED NOT NULL, viewer_id BIGINT UNSIGNED NOT NULL, viewed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (story_id) REFERENCES stories(id) ON DELETE CASCADE, FOREIGN KEY (viewer_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE KEY unique_view (story_id, viewer_id), INDEX idx_story (story_id) ) ENGINE=InnoDB; -- الحصول على القصص النشطة من المستخدمين المتابعين SELECT s.id, s.media_url, s.duration, u.username, u.avatar_url, COUNT(sv.id) AS view_count, EXISTS( SELECT 1 FROM story_views WHERE story_id = s.id AND viewer_id = 1 ) AS viewed_by_me FROM stories s JOIN users u ON s.user_id = u.id JOIN follows f ON s.user_id = f.following_id LEFT JOIN story_views sv ON s.id = sv.story_id WHERE f.follower_id = 1 AND s.expires_at > NOW() GROUP BY s.id ORDER BY viewed_by_me ASC, s.created_at DESC;

الملخص

في هذا الدرس، تعلمت:

  • كيفية تصميم قاعدة بيانات وسائل تواصل اجتماعي كاملة
  • تنفيذ علاقات المتابعة مع ضوابط الخصوصية
  • بناء نظام إعجابات متعدد الأشكال
  • إنشاء تعليقات متسلسلة مع ردود متداخلة
  • تنفيذ المراسلة المباشرة والإشعارات
  • توليد موجزات نشاط مخصصة
  • استراتيجيات قابلية التوسع لمنصات التواصل الاجتماعي
التالي: في الدرس التالي، سنصمم قاعدة بيانات للمدونة ونظام إدارة المحتوى مع الفئات والعلامات ودعم اللغات المتعددة!