• בלוג
  • שימו לב: אין Merge חלקי ב neo4j

שימו לב: אין Merge חלקי ב neo4j

21/08/2023

הפקודה MERGE ב neo4j יוצרת צמתים חדשים וקשתות חדשות אבל בצורה מעניינת - במקום שנגיד לה מה אנחנו רוצים ליצור אנחנו מתארים לה את מצב העניינים אחרי היצירה. אם הכל כבר קיים merge לא תעשה כלום, ואם החיבור לא קיים היא תיצור אותו.

איפה הבעיה?

בניגוד לכלים חכמים כמו קוברנטיס שמקבלים מצב סופי ויודעים להבין מה צריך לעשות כדי להגיע מאיפה שאני נמצא למצב שרציתי להגיע, פקודת merge ב neo4j עובדת בשיטת "הכל או כלום". אם החיבור קיים היא לא תעשה כלום, אם החיבור לא קיים היא תיצור אותו, אבל אם חלק מהחיבור קיים אז היא תנסה ליצור חיבור חדש לגמרי שיתאים למה שביקשתי.

ניקח דוגמה מבסיס הנתונים של הסרטים מארגז החול של neo4j. בסיס הנתונים מכיל Person בשם Jack Nicholson ו Person נוסף בשם Al Pacino. אני רוצה לחבר בין שניהם בקשר שנקרא FRIEND אז אני מריץ:

MERGE (n:Person{name:"Jack Nicholson"})-[:FRIENDS]-(m:Person{name:"Al Pacino"}) RETURN n LIMIT 25

אבל במקום ליצור את הקשר אני מקבל שגיאה:

Node(17) already exists with label `Person` and property `name` = 'Jack Nicholson'

מסתבר ש MERGE זיהה שאין קשר של חברות בין ג'ק ניקולסון לאל פאצ'ינו, ולכן ניסה ליצור את כל השלישייה - בן אדם בשם ג'ק ניקולסון, בן אדם נוסף בשם אל פאצ'ינו וקשר של חברות ביניהם. בארגז החול מוגדר אינדקס ייחודי על שמות של אנשים ולכן הניסיון ליצור בן אדם נוסף עם שם קיים נכשל. בלי האינדקס המצב היה עוד יותר רע, והייתי מקבל בסיס נתונים עם שני שחקנים באותו שם - מה שיגרום לבאגים בשאילתות בהמשך.

הפיתרון, מעבר להבנת החשיבות של אינדקסים, הוא לחלק את ה MERGE לפקודות MATCH כשרוצים להשתמש במידע קיים, כלומר השאילתה תהיה:

MATCH (n:Person{name:"Jack Nicholson"})
MATCH (m:Person{name:"Al Pacino"})
MERGE (n)-[:FRIENDS]-(m) RETURN n LIMIT 25