עבודה עם נתיבים

בדף הזה מוסבר איך לעבוד עם נתיבי גרפים ב-Spanner Graph.

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

באמצעות Spanner Graph Language‏ (GQL), אפשר ליצור נתיבי גרף ולבצע עליהם שאילתות. בדוגמאות במסמך הזה נעשה שימוש באותה סכימת Spanner Graph שמופיעה בדף הגדרה של Spanner Graph והפעלת שאילתות.

בניית נתיב בגרף

אפשר ליצור נתיב גרף על ידי יצירת משתנה נתיב בתבנית גרף או באמצעות הפונקציה PATH.

מומלץ ליצור נתיב גרף באמצעות משתנה הנתיב. הפורמט ליצירת משתנה נתיב הוא:

MATCH p = PATH_PATTERN

מידע נוסף זמין במאמר בנושא דפוסים בגרף.

דוגמה

בדוגמה הבאה, השאילתה מוצאת דפוסים של העברות כספים בין חשבונות בתוך FinGraph.

GRAPH FinGraph
MATCH p = (src:Account {id: 16})-[t:Transfers]->{2}(dst:Account {id: 7})
RETURN TO_JSON(p) AS full_path;

תוצאה

full_path
‪[{"identifier": ..., "properties": {"id": 16, ...}, ...}, {"identifier": ..., "properties": {"amount": 300.0, ...}, ...}, ...]

התוצאה מציינת שהשאילתה מצאה את התבנית Account -> Transfers -> Account במסד הנתונים.

שאילתת נתיב גרף

אפשר להשתמש בפונקציות הבאות שספציפיות לנתיבים כדי לשלוח שאילתה לגבי נתיב בתרשים. מידע כללי נוסף על שאילתות של Spanner Graph זמין במאמר סקירה כללית של שאילתות.

EDGES

הפונקציה EDGES מחזירה את כל הקצוות בנתיב של גרף. למידע מפורט על הסמנטיקה, אפשר לעיין במאמר EDGES.

דוגמה

השאילתה הזו מוצאת נתיב בין שני חשבונות שעובר דרך חשבון אמצעי. הפונקציה מחזירה את הסכום של הקצה השני Transfers בנתיב, שיכול להיות בין src ל-mid או בין mid ל-dst.

GRAPH FinGraph
MATCH p = (src:Account {id: 7})-[t1:Transfers]->{1,3}(mid:Account)-[t2:Transfers]->
  {1,3}(dst:Account {id: 16})
LET second_edge = EDGES(p)[1]
RETURN DISTINCT src.id AS src, dst.id AS dst, second_edge.amount AS second_edge_amount;

תוצאה

src dst second_edge_amount
7 16 300

NODES

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

דוגמה

השאילתה הזו מוצאת את נתיב הגרף של שתי העברות, ואז מחזירה רשימת JSON שמייצגת את הנתיב.

GRAPH FinGraph
MATCH p = (src:Account)-[t:Transfers]->{2}(dst:Account)
RETURN TO_JSON(NODES(p)) AS nodes;

תוצאה

צמתים
[{"identifier": "...", "properties": {"id": 16}, ...}, {"identifier": "...", "properties": {"id": 20, ...}, ...]
...

PATH_FIRST

הפונקציה PATH_FIRST מוצאת את הצומת הראשון בנתיב של גרף. למידע מפורט על הסמנטיקה, אפשר לעיין במאמר PATH_FIRST.

דוגמה

השאילתה הזו מוצאת את הצומת הראשון בנתיב של תרשים של שני העברות. הפונקציה מחזירה את התווית של הצומת Account ואת הכינוי של החשבון.

GRAPH FinGraph
MATCH p = -[:Transfers]->{1,3}(dst:Account{id: 7})
RETURN DISTINCT PATH_FIRST(p).id AS can_reach_target;

תוצאה

can_reach_target
7
16
20

PATH_LAST

הפונקציה PATH_LAST מוצאת את הצומת האחרון בנתיב של גרף. למידע מפורט על הסמנטיקה, אפשר לעיין במאמר PATH_LAST.

דוגמה

השאילתה הזו מוצאת את הצומת האחרון בנתיב גרף של שתי העברות. הפונקציה מחזירה את התווית של הצומת Account ואת הכינוי של החשבון.

GRAPH FinGraph
MATCH p =(start:Account{id: 7})-[:Transfers]->{1,3}
RETURN DISTINCT PATH_LAST(p).id as can_reach_target;

תוצאה

can_reach_target
7
16
20

PATH_LENGTH

הפונקציה PATH_LENGTH מחזירה את מספר הקשתות בנתיב של גרף. מידע נוסף על הסמנטיקה זמין במאמר PATH_LENGTH.

דוגמה

השאילתה הזו מוצאת את מספר הקצוות בנתיב של תרשים שמכיל העברה אחת עד שלוש.

GRAPH FinGraph
MATCH p = (src:Account)-[e:Transfers]->{1,3}(dst:Account)
RETURN PATH_LENGTH(p) AS num_transfers, COUNT(*) AS num_paths;

תוצאה

num_transfers num_paths
1 5
2 7
3 11

IS_ACYCLIC

הפונקציה IS_ACYCLIC בודקת אם יש נתונים חוזרים בנתיב של גרף. הפונקציה מחזירה TRUE אם נמצאת חזרה, אחרת היא מחזירה FALSE. למידע מפורט על הסמנטיקה, אפשר לעיין במאמר IS_ACYCLIC.

דוגמה

השאילתה הזו בודקת אם יש צמתים חוזרים בנתיב הגרף הזה.

GRAPH FinGraph
MATCH p = (src:Account)-[t:Transfers]->{2}(dst:Account)
RETURN IS_ACYCLIC(p) AS is_acyclic_path,
       ARRAY_TRANSFORM(NODES(p), n->n.id) AS account_ids;

תוצאה

is_acyclic_path account_ids
TRUE ‫16,20,7
TRUE 20,7,16
TRUE 20,7,16
לא נכון ‫16,20,16
TRUE ‫7,16,20
TRUE ‫7,16,20
לא נכון 20,16,20

IS_TRAIL

הפונקציה IS_TRAIL בודקת אם יש נתיב בגרף עם קצוות חוזרים. הפונקציה מחזירה TRUE אם נמצאת חזרה, אחרת היא מחזירה FALSE. למידע מפורט על הסמנטיקה, אפשר לעיין במאמר IS_TRAIL.

דוגמה

השאילתה הזו בודקת אם יש חזרות על קשתות בנתיב הגרף הזה.

GRAPH FinGraph
MATCH p = (src:Account)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN IS_TRAIL(p) AS is_trail_path,
       ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

תוצאה

is_trail_path transfer_ids
לא נכון ‫16,20,16
TRUE ‫7,16,20
TRUE ‫7,16,20

מצבי נתיב

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

WALK

מצב הנתיב WALK מחזיר את כל הנתיבים, כולל נתיבים עם צמתים וקצוות חוזרים. WALK הוא מצב הנתיב שמוגדר כברירת מחדל.

דוגמה

השאילתה הבאה מדגימה את השימוש במצב הנתיב WALK בתבנית נתיב כמותית. הנתיב הראשון בתוצאות כולל קצוות חוזרים.

GRAPH FinGraph
MATCH p = WALK (src:Account)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

תוצאה

transfer_ids
‫16,20,16
‫7,16,20
‫7,16,20

ACYCLIC

מסנן ACYCLIC path mode מסנן נתיבים עם צמתים חוזרים.

דוגמה

השאילתה הבאה מדגימה את השימוש במצב הנתיב ACYCLIC בתבנית נתיב כמותית. הנתיב עם הצמתים src ו-dst שווים מסונן החוצה.

GRAPH FinGraph
MATCH p = ACYCLIC (src:Account)-[t:Transfers]->{2}(dst:Account)
RETURN ARRAY_TRANSFORM(NODES(p), n->n.id) AS account_ids

תוצאה

account_ids
‫16,20,7
20,7,16
20,7,16
‫7,16,20
‫7,16,20

TRAIL

מסנן TRAIL path mode מסנן נתיבים עם קצוות חוזרים.

דוגמה

השאילתה הבאה מדגימה את השימוש במצב הנתיב TRAIL בתבנית נתיב כמותית. המסלולים עם קצוות חוזרים מסוננים.

GRAPH FinGraph
MATCH p = TRAIL (src:Account)-[t:Transfers]->{3}(dst:Account)
WHERE src.id < dst.id
RETURN ARRAY_TRANSFORM(t, t->t.id) AS transfer_ids

תוצאה

transfer_ids
‫7,16,20
‫7,16,20

קידומת לחיפוש נתיב

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

ANY SHORTEST

הקידומת ANY SHORTEST path search מחזירה את הנתיב הקצר ביותר (הנתיב עם המספר הקטן ביותר של קצוות) שתואם לתבנית מכל מחיצת נתונים. אם יש יותר מנתיב קצר אחד לכל מחיצה, הפונקציה מחזירה אחד מהם.

דוגמה

השאילתה הבאה מתאימה לכל נתיב בין כל זוג של [a, b].

GRAPH FinGraph
MATCH p = ANY SHORTEST (a:Account {is_blocked:true})-[t:Transfers]->{1,4}(b:Account)
LET total_amount = SUM(t.amount)
RETURN a.id AS account1_id, total_amount, b.id AS account2_id;

תוצאה

account1_id total_amount account2_id
16 500 16
16 800 7
16 300 20

ANY CHEAPEST

הקידומת ANY CHEAPEST של חיפוש הנתיב מוודאת שכל שאילתה תחזיר רק נתיב אחד עם עלות החישוב הכוללת המינימלית, לכל צמד של חשבונות מקור ויעד. הנתיב הזול ביותר הוא הנתיב עם עלות החישוב הכוללת המינימלית, שמחושבת מביטויי COST בקצוות של הנתיב. אם יש כמה נתיבים זולים בכל מחיצה, השאילתה מחזירה אחד מהם.

דוגמה

השאילתה הבאה מתאימה לאחד מהמסלולים הזולים ביותר בין כל זוג חשבונות, שמיוצגים על ידי a ו-b, על סמך סכום ההעברה.

GRAPH FinGraph
MATCH ANY CHEAPEST (a:Account)-[t:Transfers COST t.amount]->{1,3}(b:Account)
LET total_cost = sum(t.amount)
RETURN a.id AS account1_id, b.id AS account2_id, total_cost

תוצאה

account1_id account2_id total_cost
7 7 900
7 16 100
7 20 400
16 7 800
16 16 500
16 20 300
20 7 500
20 16 200
20 20 500

כללי המרה

מידע נוסף זמין במאמר כללי המרה של GRAPH_PATH.

תרחיש שימוש לדוגמה

בדוגמה הבאה לתרחיש שימוש, אפשר לראות שכל החשבונות מנותבים דרך חשבון אחד עד שלושה, החל ממזהה החשבון 20.

GRAPH FinGraph
MATCH p = (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN DISTINCT dst.id AS dst;

תוצאה

dst
7
16
20

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

  • משתמשים ב-MATCH p = ACYCLIC <path_pattern> או ב-
  • החלת מסנן IS_ACYCLIC(p) בשאילתה

השאילתה הבאה משתמשת ב-MATCH p = ACYCLIC PATH_PATTERN:

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN DISTINCT dst.id AS dst;

תוצאה

dst
7
16

אם רוצים לדעת דרך איזה חשבון הכסף מועבר בפעם הראשונה, אפשר להריץ את השאילתה הבאה:

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})(-[:Transfers]->
  (nexts:Account)){1,3}(dst:Account)
RETURN dst.id AS dst, ARRAY_AGG(DISTINCT nexts[0].id) AS unique_starts;

השאילתה הזו לא שגרתית כי היא כוללת משתנה חדש בתוך הנתיב הכמותי באמצעות nexts כדי לקבל את התוצאה. בעזרת משתני נתיב, אפשר לפשט את השאילתה:

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id AS dst, ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS unique_starts;

השימוש ב-NODES(p) מחזיר את כל הצמתים לאורך הנתיב. מכיוון שהחשבון הראשון בצומת מוגדר כ-start, החשבון הבא (בקיזוז הראשון) הוא החשבון הראשון שדרכו הכסף מועבר.

תוצאה

dst unique_starts
7 ‫16, 7

הדוחות על נתיבי המרה שימושיים יותר כשיש כמה נתיבים עם נתונים כמותיים. אפשר להוסיף אילוץ שלפיו הנתיבים שנמצאו מ-start חייבים לעבור דרך מזהה החשבון 7:

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->
  {1,3}(mid:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
RETURN dst.id AS dst,
  ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS unique_starts;

למרות שההצהרה MATCH השתנתה, לא צריך לשנות את שאר השאילתה. בלי להשתמש במשתני נתיב, יש מקרים שבהם Spanner לא יכול לדעת באופן סטטי איזה נתיב כמותי לבדוק.

אפשר להשתמש במשתנה נתיב כדי לקבל את הסכום של כל ההעברות:

GRAPH FinGraph
MATCH p = ACYCLIC (start:Account {id: 20})-[:Transfers]->
  {1,3}(mid:Account {id: 7})-[:Transfers]->{1,3}(dst:Account)
LET all_transfers = EDGES(p)
LET transfer_amounts = SUM(all_transfers.amount)
RETURN dst.id AS dst,
  ARRAY_AGG(DISTINCT NODES(p)[OFFSET(1)].id) AS participating_neighbor_nodes, transfer_amounts;

תוצאה

dst participating_neighbor_nodes transfer_amounts
16 7 600
16 7 800

המאמרים הבאים