So far, two fundamental traversal patterns have been established.
· out(): follow outgoing edges
· in(): follow incoming edges
These steps are ideal when the direction of the relationship is semantically important, such as “customer placed order” or “order delivered to city”.
However, not all graph questions care about direction. Sometimes the question is simply:
· “What is connected to this vertex?”
· “What are all the immediate neighbors?”
· “Show me everything one hop away, regardless of direction.”
This is exactly where the both() step is used.
1. Incoming and Outgoing Adjacency
A vertex U is considered adjacent to vertex V if there exists any edge between them—regardless of whether the edge points from U to V or from V to U.
In other words:
· U --(edge)--> V
· V --(edge)--> U
Both cases establish adjacency. The both() step traverses all such adjacent vertices.
The both() step performs the following operation:
· Start at the current vertex
· Traverse all incoming and outgoing edges
· Emit the adjacent vertices at the other end of those edges
Formally, both() is equivalent to out() + in() combined into a single step.
Edge Label Filtering
Just like out() and in(), both() can be restricted by edge labels:
· both(): follow all edges (incoming and outgoing)
· both('contains'): follow only contains edges, in both directions
· both('placed', 'contains'): follow multiple edge labels
This allows controlled bidirectional traversal without losing semantic intent.
2. Example: An E-Commerce Graph
To make the discussion concrete, consider an e-commerce domain modeled as a property graph. The graph contains:
Vertex Types
· city: Bengaluru, Hyderabad, Chennai, Pune
· category: Electronics, Books, Clothing
· product: Laptop, Smartphone, Java Programming Book, T-Shirt
· customer: Amit Sharma, Priya Iyer, Rohit Verma
· order: O9001 to O9005
Edge Types
· belongsTo: product → category
· placed: customer → order
· contains: order → product
· deliveredTo: order → city
Each edge has a clear semantic direction that aligns with real-world meaning.
Gremlin Statements to build the Graph
graph = TinkerGraph.open() g = graph.traversal() blr = g.addV('city').property('name', 'Bengaluru').next() hyd = g.addV('city').property('name', 'Hyderabad').next() chn = g.addV('city').property('name', 'Chennai').next() pne = g.addV('city').property('name', 'Pune').next() catElectronics = g.addV('category').property('name', 'Electronics').next() catBooks = g.addV('category').property('name', 'Books').next() catClothing = g.addV('category').property('name', 'Clothing').next() p1 = g.addV('product').property('sku', 'P1001').property('name', 'Laptop').next() p2 = g.addV('product').property('sku', 'P1002').property('name', 'Smartphone').next() p3 = g.addV('product').property('sku', 'P2001').property('name', 'Java Programming Book').next() p4 = g.addV('product').property('sku', 'P3001').property('name', 'T-Shirt').next() g.V(p1).addE('belongsTo').to(catElectronics).next() g.V(p2).addE('belongsTo').to(catElectronics).next() g.V(p3).addE('belongsTo').to(catBooks).next() g.V(p4).addE('belongsTo').to(catClothing).next() c1 = g.addV('customer').property('customerId', 'C101').property('name', 'Amit Sharma').next() c2 = g.addV('customer').property('customerId', 'C102').property('name', 'Priya Iyer').next() c3 = g.addV('customer').property('customerId', 'C103').property('name', 'Rohit Verma').next() o1 = g.addV('order').property('orderId', 'O9001').next() o2 = g.addV('order').property('orderId', 'O9002').next() o3 = g.addV('order').property('orderId', 'O9003').next() o4 = g.addV('order').property('orderId', 'O9004').next() o5 = g.addV('order').property('orderId', 'O9005').next() g.V(c1).addE('placed').to(o1).next() g.V(c1).addE('placed').to(o2).next() g.V(c2).addE('placed').to(o3).next() g.V(c3).addE('placed').to(o4).next() g.V(c3).addE('placed').to(o5).next() g.V(o1).addE('contains').to(p1).next() g.V(o1).addE('contains').to(p3).next() g.V(o2).addE('contains').to(p2).next() g.V(o3).addE('contains').to(p4).next() g.V(o4).addE('contains').to(p1).next() g.V(o4).addE('contains').to(p4).next() g.V(o5).addE('contains').to(p3).next() g.V(o1).addE('deliveredTo').to(blr).next() g.V(o2).addE('deliveredTo').to(blr).next() g.V(o3).addE('deliveredTo').to(hyd).next() g.V(o4).addE('deliveredTo').to(chn).next() g.V(o5).addE('deliveredTo').to(pne).next()
Example 1: Print all vertices and edges
gremlin> g.V().valueMap(true) ==>[id:0,label:city,name:[Bengaluru]] ==>[id:33,label:customer,customerId:[C102],name:[Priya Iyer]] ==>[id:2,label:city,name:[Hyderabad]] ==>[id:4,label:city,name:[Chennai]] ==>[id:36,label:customer,customerId:[C103],name:[Rohit Verma]] ==>[id:6,label:city,name:[Pune]] ==>[id:39,label:order,orderId:[O9001]] ==>[id:8,label:category,name:[Electronics]] ==>[id:41,label:order,orderId:[O9002]] ==>[id:10,label:category,name:[Books]] ==>[id:43,label:order,orderId:[O9003]] ==>[id:12,label:category,name:[Clothing]] ==>[id:45,label:order,orderId:[O9004]] ==>[id:14,label:product,name:[Laptop],sku:[P1001]] ==>[id:47,label:order,orderId:[O9005]] ==>[id:17,label:product,name:[Smartphone],sku:[P1002]] ==>[id:20,label:product,name:[Java Programming Book],sku:[P2001]] ==>[id:23,label:product,name:[T-Shirt],sku:[P3001]] ==>[id:30,label:customer,customerId:[C101],name:[Amit Sharma]] gremlin> gremlin> gremlin> gremlin> g.E().valueMap(true) ==>[id:64,label:deliveredTo] ==>[id:65,label:deliveredTo] ==>[id:49,label:placed] ==>[id:50,label:placed] ==>[id:51,label:placed] ==>[id:52,label:placed] ==>[id:53,label:placed] ==>[id:54,label:contains] ==>[id:55,label:contains] ==>[id:56,label:contains] ==>[id:57,label:contains] ==>[id:26,label:belongsTo] ==>[id:58,label:contains] ==>[id:27,label:belongsTo] ==>[id:59,label:contains] ==>[id:28,label:belongsTo] ==>[id:60,label:contains] ==>[id:29,label:belongsTo] ==>[id:61,label:deliveredTo] ==>[id:62,label:deliveredTo] ==>[id:63,label:deliveredTo]
Example 2: One-Hop Neighborhood of an Order
Suppose the goal is to inspect everything directly connected to an order, without caring about direction.
g.V(). has('order', 'orderId', 'O9001'). both(). valueMap(true)
gremlin> g.V(). ......1> has('order', 'orderId', 'O9001'). ......2> both(). ......3> valueMap(true) ==>[id:14,label:product,name:[Laptop],sku:[P1001]] ==>[id:20,label:product,name:[Java Programming Book],sku:[P2001]] ==>[id:0,label:city,name:[Bengaluru]] ==>[id:30,label:customer,customerId:[C101],name:[Amit Sharma]]
Example 3: All Vertices Related to a Product
A product participates in two relationships.
Incoming: contains (orders)
Outgoing: belongsTo (category)
g.V(). has('product', 'name', 'Laptop'). both(). valueMap(true)
gremlin> g.V(). ......1> has('product', 'name', 'Laptop'). ......2> both(). ......3> valueMap(true) ==>[id:8,label:category,name:[Electronics]] ==>[id:39,label:order,orderId:[O9001]] ==>[id:45,label:order,orderId:[O9004]]
Example 4: Bidirectional Traversal with Edge Label Filtering
To traverse only through contains edges, regardless of direction.
g.V(). has('product', 'name', 'Laptop'). both('contains'). valueMap(true)
gremlin> g.V(). ......1> has('product', 'name', 'Laptop'). ......2> both('contains'). ......3> valueMap(true) ==>[id:39,label:order,orderId:[O9001]] ==>[id:45,label:order,orderId:[O9004]]
Example 5: Exploring a Customer’s Immediate Graph Context
g.V(). has('customer', 'name', 'Rohit Verma'). both(). valueMap(true)
gremlin> g.V(). ......1> has('customer', 'name', 'Rohit Verma'). ......2> both(). ......3> valueMap(true) ==>[id:45,label:order,orderId:[O9004]] ==>[id:47,label:order,orderId:[O9005]]
The choice between out(), in(), and both() reflects intent:
· Use out() when the meaning of direction matters
· Use in() when asking reverse or impact-style questions
· Use both() when performing structural exploration
both() is particularly valuable for:
· Graph discovery and debugging
· Visualizations and neighborhood inspection
· Algorithms that treat edges as undirected
· Generic tooling where edge direction is unknown or irrelevant
both() Compared to out() and in()
|
Traversal Intent |
Recommended Step |
|
Follow domain flow |
out() |
|
Ask “who/what led here?” |
in() |
|
Explore all neighbors |
both() |
|
Build undirected-style queries |
both() |
While both() can always replace in() + out(), it should be used deliberately to avoid unintentionally broad traversals.
In summary,
both() traverses incoming and outgoing adjacent vertices
· It is direction-agnostic but label-aware
· Ideal for neighborhood exploration and structural queries
· Complements out() and in() rather than replacing them
· Clear graph modeling keeps both() safe and expressive
With out(), in(), and both() together, the foundational navigation toolkit of Gremlin is complete and enabling expressive, precise, and intuitive traversal of property graphs.
Previous Next Home
No comments:
Post a Comment