שאילתות על קשרי וידאו באמצעות Apache AGE ב-Postgres
מסדי נתונים רלציוניים מתקשים עם קשרים עמוקים.
ב-TrendVidStream, אנחנו מנהלים קטלוג של כותרים להזרמה (streaming) בשמונה אזורים. בתחילה השתמשנו ב-SQLite עם FTS5. זה עבד טוב עבור חיפוש טקסט מלא ופריסות פשוטות.
הבעיה החלה כאשר משתמשים התחילו לשאול שאלות מורכבות. הם רצו לדעת: "מה עוד נמצא במרחק של שניים או שלושה hops מהסרט המותח הזה, עם הבמאי הספציפי הזה והשחקנים האלה?"
ב-SQL, זה דורש הרבה self-joins. כל hop נוסף הופך את השאילתה לקשה יותר לתחזוקה ואיטית יותר להרצה. ה-join fan-out הופך לסיוט.
פתרנו זאת על ידי הוספת שכבת גרף באמצעות התוסף Apache AGE ב-PostgreSQL.
הנה הסיבות לכך שהחלוקה הזו עובדת:
• אתם שומרים על PostgreSQL. אתם משתמשים באותם חיבורים, טרנזקציות וכלי גיבוי. אין צורך במסד נתונים נפרד כמו Neo4j. • אתם מערבבים נתוני גרף ונתונים רלציוניים. אתם יכולים להריץ שאילתות Cypher ולחבר את התוצאות עם טבלאות SQL סטנדרטיות. • אתם מטפלים בעומק משתנה בקלות. מציאת קשרים במרחק של hop אחד עד שלושה היא פקודה פשוטה ב-Cypher. • אתם שומרים על פשטות. הגרף הוא הטלה (projection) של הנתונים שלכם, ולא מקור אמת (source of truth) נפרד.
המודל שלנו משתמש בארבע תוויות צמתים (vertex labels):
- Video
- Person
- Platform
- Region
אנחנו משתמשים בסוגי קשתות (edge types) ספציפיים כמו SIMILAR_TO, WORKED_ON, ו-AVAILABLE_IN.
לקח קריטי אחד: תמיד אינדקסו את נקודות הכניסה שלכם. אנחנו מאנדקסים את המאפיין ext_id בצמתי Video. ללא זה, כל traversal מתחיל בסריקה סדרתית (sequential scan) איטית.
התוצאה היא שאילתה נקייה וקריאה עבור המלצות:
SELECT * FROM cypher('discovery', $$ MATCH (seed:Video {ext_id: 'vid_8842'}) MATCH (seed)-[:SIMILAR_TO|WORKED_ON*1..3]-(rec:Video)-[:AVAILABLE_IN]->(:Region {code: 'DE'}) WHERE rec.ext_id <> 'vid_8842' RETURN DISTINCT rec.ext_id, rec.title, rec.year LIMIT 24 $$) AS (ext_id agtype, title agtype, year agtype);
שאילתה זו מוצאת סרטונים קשורים בטווח של שלושה hops הזמינים בגרמניה. ב-SQL, זה היה ערימה עצומה של joins. ב-Cypher, זהו pattern פשוט.
שמרנו על SQLite לחיפוש טקסט והשתמשנו ב-AGE עבור קישוריות. כל כלי עושה את מה שהוא הכי טוב בו.
