في DailyWatch، بدأت لوحة "الفيديوهات ذات الصلة" لدينا باستعلام SQLite بسيط؛ حيث قام بدمج ثلاث جداول، وحسب الوسوم المتداخلة، وأرجع قائمة مرتبة. بالنسبة لكتالوج صغير، كان ذلك كافياً. ففيديو طبخ يحمل وسوم "italian" و "pasta" كان سيظهر فيديوهات أخرى تحمل نفس الوسوم، وكان المستخدمون ينقرون عليها. بدا الناتج ذا صلة لأن البيانات الوصفية (metadata) كانت نظيفة والمكتبة كانت محدودة.

ثم نما الكتالوج، وتغيرت توقعات الجمهور. لم يعد الناس يريدون مجرد المزيد من الفيديوهات ذات الوسوم المتطابقة، بل أرادوا المقطع الذي شاهده أربعون بالمائة من المشاهدين فوراً بعد الفيديو الحالي. أرادوا تلك القناة المتخصصة التي تستمر في الظهور في نفس جلسات المشاهدة المتأخرة من الليل، على الرغم من أن وصفها لم يذكر شيئاً عن الفيديو الذي بدأوا به وكانت وسومها شحيحة. هذه أنماط سلوكية، وليست مجرد تطابقات في البيانات الوصفية. لم يستطع SQLite التعبير عنها دون الانزلاق إلى عشّ غير قابل للصيانة من عمليات الربط الذاتي (self-joins) وتعبيرات الجدول المشترك المتكررة (recursive common table expressions). كل إشارة جديدة حاولنا نمذجتها، سواء كانت المشاهدة المشتركة أو تجاور الجلسات، كانت تضيف تأخراً (latency) وعبئاً ذهنياً. لقد كانت مشكلة رسوم بيانية (graph problem) حُشرت في قالب علاقي (relational).

قمت بنقل طبقة التوصيات إلى Apache AGE.

Apache AGE هو إضافة لـ PostgreSQL تضيف استعلامات الرسوم البيانية من نوع openCypher إلى قاعدة البيانات التي تشغلها بالفعل. إنه ليس خادماً منفصلاً، وليس "sidecar". إنه يعمل داخل Postgres، مما يعني أنه كان بإمكاننا بناء شبكة مشاهدة مشتركة دون الحاجة إلى إعداد عنقود (cluster) Neo4j مخصص أو تعلم دليل تشغيلي جديد تماماً. بالنسبة لفريق صغير لا يملك مهندس موثوقية قواعد بيانات، كان هذا الفرق جوهرياً للغاية.

لماذا تتفوق الإضافة على قاعدة بيانات جديدة

إضافة قاعدة بيانات رسوم بيانية (graph database) إلى بنيتك التقنية أمر سهل على السبورة البيضاء ولكنه مكلف في بيئة الإنتاج. ستحتاج إلى لوحات تحكم جديدة للمراقبة، وإجراءات نسخ احتياطي جديدة، ومجمعات اتصال (connection pools) جديدة، ومنطق تجاوز فشل (failover logic) جديد. يتجاوز AGE كل ذلك لأنه يعيش داخل نسخة Postgres الحالية لديك.

هناك أربعة أسباب عملية جعلت هذا الأمر ينجح معنا:

  • لا توجد بنية تحتية جديدة. بما أن AGE هو إضافة، فإن جدول pg_dump الحالي، والنسخ المتماثلة (replicas) الموجودة، وفحوصات الحالة القياسية لـ Postgres ستستمر جميعها في العمل. لا تحتاج إلى إقناع فريق العمليات بالإشراف على مخزن بيانات آخر.
  • أعباء عمل مختلطة في استعلام واحد. يتيح لك AGE كتابة Cypher داخل SQL. يمكنك إجراء عبور للرسم البياني (graph traversal) للعثور على الفيديوهات المرشحة، ثم دمج مجموعة النتائج تلك مع جدول users العلاقي لفرض قيود المحتوى الإقليمية، أو مع جدول sponsorships العلاقي لتقليل أولوية قنوات معينة. رحلة ذهاب وإياب واحدة، ولغتان للاستعلام تتعاونان معاً.
  • سهولة النقل (Portability). لغة Cypher هي لغة استعلام رسوم بيانية مفتوحة وموثقة جيداً. إذا تجاوزت DailyWatch قدرات AGE واحتاجت إلى الانتقال إلى Neo4j أو Memgraph لاحقاً، فإن منطق الاستعلام سينتقل مع الحد الأدنى من إعادة الكتابة. أنت لست مقيداً بلهجة برمجية مملوكة (proprietary dialect).
  • التوافق. بما أن البيانات تعيش في نهاية المطاف في PostgreSQL، فإن أدوات PHP أو Python الحالية لديك لن تتغير. ستتصل باستخدام نفس برنامج التشغيل (driver)، وتتعامل مع نفس سلاسل الاتصال، وتجلب الصفوف بنفس الطريقة. منطق الرسم البياني يقع في طبقة الاستعلام، وليس في طبقة التطبيق.

نمذجة المشاهدة المشتركة

التنفيذ مباشر وبسيط. قمنا بتعريف عقد (nodes) للكيانات التي تهمنا: Video و Channel. ثم عرفنا حواف (edges) للعلاقات بينهما. تربط حافة PUBLISHED بين Channel و Video. وتربط حافة CO_VIEWED بين Video وآخر، حاملةً خاصية weight التي تمثل عدد مرات ظهور الفيديوين في نفس جلسة المشاهدة.

يلتقط هذا النموذج شيئاً لا تستطيع لغة SQL القائمة على الوسوم التقاطه: البنية الضمنية