גרמלין נשך אותי

22/06/2024

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

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

import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerTransactionGraph
import org.apache.tinkerpop.gremlin.process.traversal.AnonymousTraversalSource.traversal
import scala.jdk.CollectionConverters._

val graph = TinkerTransactionGraph.open
val g = traversal.withEmbedded(graph)

ואז יוצר כמה פריטים:

g.addV("item").next()
g.addV("item").next()
g.addV("item").next()

וגם יכול להדפיס את המידע עליהם עם השאילתה:

scala> g.V().hasLabel("item").limit(2).elementMap().toList.asScala

val res21: scala.collection.mutable.Buffer[java.util.Map[Object, Object]] = Buffer({id=0, label=item}, {id=1, label=item})

עכשיו לחלק שהפתיע אותי (למרות שבמבט שני על הקוד האשמה כולה עליי). נניח שאני רוצה לפצל את השאילתה לשני חלקים, קודם לקחת את כל ה IDs של הצמתים שיצרתי ורק אחר כך להתחיל מעבר חדש על הצמתים לפי ה ID ולהדפיס את המידע. אני יכול להתפתות ולכתוב:

scala> val allItems = g.V().hasLabel("item").limit(2).toList.asScala.toList
val allItems: List[org.apache.tinkerpop.gremlin.structure.Vertex] = List(v[0], v[1])

scala> g.V(allItems: _*).elementMap().toList
val res22: java.util.List[java.util.Map[Object, Object]] = [{id=0, label=item}, {id=1, label=item}]

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

scala> val allItems = g.V().hasLabel("x").limit(2).toList.asScala.toList
val allItems: List[org.apache.tinkerpop.gremlin.structure.Vertex] = List()

scala> g.V(allItems: _*).elementMap().toList
val res23: java.util.List[java.util.Map[Object, Object]] = [{id=0, label=item}, {id=1, label=item}, {id=2, label=item}]

השאילתה הראשונה אכן מבינה שאין פריטים עם התווית x ומחזירה רשימה ריקה, אבל הפקודה השנייה מופעלת על רשימה ריקה שזה בדיוק כמו להפעיל g.V() בלי פרמטרים, שזאת פקודה שרצה על כל הצמתים בגרף, ולכן אני מקבל את המידע על כל הצמתים.

והלקח מהסיפור - לא להשתמש ב spread operator, או לפחות אם משתמשים בו תמיד לבדוק מה קורה כשהרשימה ריקה.