Tuesday, 12 May 2026

Incoming Traversals (in()): Navigating Incoming Adjacent Vertices in Gremlin

  

Apache TinkerPop models graphs as directed property graphs, where every edge has a clearly defined direction, from an out-vertex to an in-vertex. While out() follows relationships in their natural, forward direction, many real-world questions require traversing against that direction.

 

This is where the in() step becomes essential.

 

1. What Does "Incoming Adjacent Vertex" Mean?

A vertex U is an incoming adjacent vertex of vertex V if there exists an edge:

 

U --(edge)--> V

 

From the perspective of V:

·      The edge is incoming

·      The vertex U is adjacent via an incoming edge

 

In Gremlin terms, in() moves from a vertex to all vertices that connect into it. In simple terms, in() returns all vertices that have edges pointing to the current vertex.

 

Filtering by Edge Label

Just like out(), the in() step can restrict traversal by edge labels:

 

·      in() follow all incoming edges

·      in('placed') follow only incoming edges with label placed

 

This allows precise, semantically meaningful traversals.

 

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: Orders Placed by a Customer (Reverse Perspective)

Instead of starting from a customer, suppose the traversal starts from an order and asks: “Who placed this order?”

 

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

gremlin> g.V().
......1>   has('order', 'orderId', 'O9001').
......2>   in('placed').
......3>   valueMap(true)
==>[id:30,label:customer,customerId:[C101],name:[Amit Sharma]]

Here, the traversal moves against the placed edge direction to find the customer responsible for the order.

 

Example 3: Orders Containing a Specific Product

Products are destinations of the contains edge. To find all orders that include a given product.

g.V().
  has('product', 'name', 'Laptop').
  in('contains').
  valueMap(true)

This answers a common business question: "Which orders include this product?"

gremlin> g.V().
......1>   has('product', 'name', 'Laptop').
......2>   in('contains').
......3>   valueMap(true)
==>[id:39,label:order,orderId:[O9001]]
==>[id:45,label:order,orderId:[O9004]]

   

Example 4: Customers Who Ordered a Given Product

This traversal combines multiple in() steps.

 

g.V().
  has('product', 'name', 'Java Programming Book').
  in('contains').
  in('placed').
  valueMap(true)

gremlin> g.V().
......1>   has('product', 'name', 'Java Programming Book').
......2>   in('contains').
......3>   in('placed').
......4>   valueMap(true)
==>[id:30,label:customer,customerId:[C101],name:[Amit Sharma]]
==>[id:36,label:customer,customerId:[C103],name:[Rohit Verma]]

   

Example 5: Orders Delivered to a City

Cities are endpoints of the deliveredTo relationship.

 

g.V().
  has('city', 'name', 'Bengaluru').
  in('deliveredTo').
  valueMap(true)

gremlin> g.V().
......1>   has('city', 'name', 'Bengaluru').
......2>   in('deliveredTo').
......3>   valueMap(true)
==>[id:39,label:order,orderId:[O9001]]
==>[id:41,label:order,orderId:[O9002]]

   

The in() step is not a workaround—it is a first-class traversal primitive. It is especially useful when:

 

·      Asking reverse ownership questions

·      Performing impact analysis

·      Answering “who depends on this?” queries

·      Exploring fan-in relationships

 

In many domains, destination nodes (products, cities, categories) act as aggregation points, and in() reveals all upstream contributors.

 

3. in() vs out(): A Mental Model

Question Type

Natural Step

What did this customer do?        

out()

Who did this order belong to?     

in()

What does this order contain?     

out()

Which orders include this product?

in()

 

In summary,

·      Incoming adjacent vertices are connected via edges pointing to the current vertex

·      in() traverses incoming edges only

·      Edge labels can be used to precisely control traversal

·      in() is essential for reverse lookups and impact-style queries

·      Well-directed edges make in() traversals expressive and intuitive

 

Mastering in() completes the foundational traversal toolkit, enabling bidirectional reasoning over graph relationships and setting the stage for more advanced traversal patterns such as both(), where(), and path-based analysis.

  

  

Previous                                                    Next                                                    Home

No comments:

Post a Comment