טיפ neo4j - בחירת צומת באקראי
בחודשים שאני עובד עם neo4j הוא הפך מהר מאוד לבסיס הנתונים המועדף עליי. הגמישות בהקמה והכח של שפת Cypher הם שילוב מנצח. אבל גם סוס מנצח יכול לפשל מדי פעם, וכך קרה שנתקעתי עם משימה לכאורה סופר פשוטה הרבה יותר מדי זמן.
בשביל הדוגמה אני לוקח את בסיס הנתונים Movies מתוך התבנית באתר https://sandbox.neo4j.com. אתר זה מאפשר להקים בסיסי נתונים של neo4j בקלות למשחקים אונליין. בסיס הנתונים של הסרטים כולל מידע על סרטים והאנשים שלקחו בהם חלק, למשל השאילתה:
MATCH (m:Movie) WHERE m.title = 'The Polar Express'
RETURN m.released
תחזיר לי באיזה שנה יצא הסרט The Polar Express, והשאילתה:
MATCH (m:Movie) WHERE m.title = 'The Polar Express'
MATCH (p:Person)-[:ACTED_IN]-(m)
RETURN p.name
תחזיר את שמות השחקנים מהסרט.
אם אנחנו רוצים לבחור פשוט שם של סרט אקראי הטריק הראשון שעולה בגוגל והכי פשוט לשימוש הוא להשתמש ב ORDER BY
ולבחור את הפונקציה RAND
בתור מפתח החיפוש. לדוגמה השאילתה הזו תחזיר שם של סרט אקראי:
MATCH (m:Movie)
RETURN m.title
ORDER BY rand()
LIMIT 1
אבל אם ננסה להרחיב את השאילתה דברים כבר מפסיקים לעבוד. השאילתה הזו תחזיר לי את כל הסרטים שתום הנקס שיחק בהם:
MATCH (p:Person{name: "Tom Hanks"})
MATCH (p)-[:ACTED_IN]-(m:Movie)
RETURN m.title
אבל טריק ה rand כבר לא ייתן לי סרט אקראי:
// always returns Apollo 13
MATCH (p:Person{name: "Tom Hanks"})
MATCH (p)-[:ACTED_IN]-(m:Movie)
RETURN m.title
ORDER BY rand()
LIMIT 1
לא משנה כמה פעמים נריץ את השאילתה, התוצאה תמיד תהיה הסרט הראשון. ה ORDER BY
מסדר את השחקנים שנבחרו (ויש רק אחד כזה - תום הנקס), ולא את הסרטים של אותו שחקן.
הפיתרון למצבים כאלה הוא ספריית פונקציות מובנות של neo4j שנקראת apoc. בעזרת פונקציות מספריה זו אפשר לבחור צומת אקראי, לאסוף צמתים ולבצע עוד אינסוף פעולות בבסיס הנתונים. השאילתה הבאה כבר תחזיר כל הפעלה סרט אחר של תום הנקס באקראי:
MATCH (p:Person{name: "Tom Hanks"})
MATCH (p)-[:ACTED_IN]-(m:Movie)
WITH collect(m) as movies
RETURN apoc.coll.randomItem(movies)
סך הכל אפשר למצוא את כל הפונקציות המובנות ב neo4j בתיעוד בקישור: https://neo4j.com/labs/apoc/4.2/overview/, ולקוות לעתיד וורוד ונטול SQL.