Friday, 15 May 2026

Bidirectional Edge Traversals (bothE()): Exploring All Incident Edges

  

So far, edge-level traversal has been introduced in two complementary forms:

 

·      outE(): outgoing incident edges

·      inE(): incoming incident edges

 

These steps are ideal when the direction of the relationship matters. However, just as with vertex traversal, there are situations where direction is secondary to connectivity.

 

Typical questions include:

·      What relationships involve this vertex?

·      Which edges touch this entity, regardless of direction?

·      What events or associations is this node part of?

·      These questions are answered using bothE().

 

Both Incoming and Outgoing Incident Edges

An edge is incident to a vertex if it is connected to that vertex—either as the source or the destination.

 

For a vertex v:

·      Outgoing incident edges: edges where v is the out-vertex

·      Incoming incident edges: edges where v is the in-vertex

 

The bothE() step returns all such edges.

 

Graphically:

a  --->  v  --->  b

 

From the perspective of v:

·      The edge from a is incoming

·      The edge to b is outgoing

 

Both edges are incident to v.

 

Edge Label Filtering

The bothE() step supports filtering by edge labels:

·      bothE(): all incident edges

·      bothE('contains'): incident edges labeled contains

·      bothE('placed', 'deliveredTo'): multiple labels

 

Label filtering is particularly important in vertices with diverse relationships.

 

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()

From the established domain model:

 

·      order ──contains── product

·      customer ──placed── order

·      order ──deliveredTo── city

·      product ──belongsTo── category

 

This implies:

·      Products have incoming contains edges

·      Orders have incoming placed edges

·      Cities have incoming deliveredTo edges

·      Categories have incoming belongsTo edges

 

 

Example 1: All Relationships Involving an Order

To retrieve every relationship connected to order O9001.

g.V().
  has('order', 'orderId', 'O9001').
  bothE().
  valueMap(true)

gremlin> g.V().
......1>   has('order', 'orderId', 'O9001').
......2>   bothE().
......3>   valueMap(true)
==>[id:54,label:contains]
==>[id:55,label:contains]
==>[id:61,label:deliveredTo]
==>[id:49,label:placed]

Example 2: Combining bothE() with Vertex Steps

To retrieve all vertices connected to an order via any relationship.

g.V().
  has('order', 'orderId', 'O9001').
  bothE().
  bothV().
  valueMap(true)

gremlin> g.V().
......1>   has('order', 'orderId', 'O9001').
......2>   bothE().
......3>   bothV().
......4>   valueMap(true)
==>[id:39,label:order,orderId:[O9001]]
==>[id:14,label:product,name:[Laptop],sku:[P1001]]
==>[id:39,label:order,orderId:[O9001]]
==>[id:20,label:product,name:[Java Programming Book],sku:[P2001]]
==>[id:39,label:order,orderId:[O9001]]
==>[id:0,label:city,name:[Bengaluru]]
==>[id:30,label:customer,customerId:[C101],name:[Amit Sharma]]
==>[id:39,label:order,orderId:[O9001]]

As seen in the output, the order with orderId = O9001 appears multiple times. This is expected behavior. The traversal bothE().bothV() means:

 

“For each relationship connected to this vertex, return both endpoints of that relationship.”

 

Since the order vertex participates in every incident edge, it is returned once for each such edge, resulting in repeated appearances of the same vertex.

 

You can use dedup step to remove the duplications.

g.V().
  has('order', 'orderId', 'O9001').
  bothE().
  bothV().
  dedup().
  valueMap(true)

gremlin> g.V().
......1>   has('order', 'orderId', 'O9001').
......2>   bothE().
......3>   bothV().
......4>   dedup().
......5>   valueMap(true)
==>[id:39,label:order,orderId:[O9001]]
==>[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]]

Use otherV() step to exclude the starting vertex.

g.V().
  has('order', 'orderId', 'O9001').
  bothE().
  otherV().
  valueMap(true)

 

gremlin> g.V().
......1>   has('order', 'orderId', 'O9001').
......2>   bothE().
......3>   otherV().
......4>   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]]

 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment