Wednesday, 20 May 2026

Controlling valueMap() Output in Apache TinkerPop Using WithOptions

  

In Gremlin, valueMap() is one of the most commonly used steps for projecting element properties into a structured form. It is frequently used when inspecting vertices and edges, debugging traversals, or shaping query results for downstream consumers such as APIs, analytics pipelines, or visualizations.

 

Historically, controlling what valueMap() returned especially when including element identifiers and labels required using constructs like valueMap(true). While functional, this approach offered limited flexibility and made it difficult to precisely tailor the output format as traversal requirements evolved.

 

Starting with Apache TinkerPop 3.4, a more expressive and extensible mechanism was introduced through the with() step and the WithOptions qualifiers. This enhancement allows developers to explicitly control whether tokens such as element IDs and labels are included, select specific tokens independently, and fine-tune how property values are represented in the result set.

 

In addition, TinkerPop 3.4 simplifies a long-standing pain point with valueMap() where, property values being returned as lists even when they are single-valued. By combining valueMap() with step modulators like by(unfold()), traversals can now produce cleaner, more natural result structures without requiring complex post-processing steps.

 

This post explores how to use WithOptions with valueMap() to:

 

·      Include or exclude element tokens such as IDs and labels

·      Select only specific tokens when needed

·      Control which properties are returned

·      Simplify result structures by unwrapping single-value properties

 

By the end, you’ll have a clear understanding of the modern, recommended patterns for shaping valueMap() output in Apache TinkerPop, and how to write traversals that are both more expressive and more maintainable going forward.

 

1. E-Commerce Domain Model Overview

To demonstrate the different ways valueMap() can be controlled using WithOptions, we will use a simple e-commerce domain model built using an in-memory TinkerGraph. This model is intentionally small but expressive enough to showcase real-world traversal patterns.

 

At a high level, the graph represents:

 

·      Customers placing orders

·      Orders containing products

·      Products belonging to categories

·      Orders being delivered to cities

 

This gives us multiple vertex labels, relationships, and property types to work with and ideal for exploring how valueMap() behaves across different scenarios.

 

Vertex Types

·      City: Represents delivery locations

·      Category: Represents product categories

·      Product: Represents items that can be purchased

·      Customer: Represents users of the platform

·      Order: Represents purchase transactions

 

Each vertex label has a small and focused set of properties, which makes it easier to see how valueMap() returns property data and how WithOptions affects the output.

 

Edge Types and Relationships

·      belongsTo: Connects a product to its category

·      placed: Connects a customer to an order

·      contains: Connects an order to one or more product vertices. An order may contain multiple products

·      deliveredTo: Connects an order to a city, represents the delivery destination for the order

 

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

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> 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]

2. Introducting WithOptions

WithOptions is a class provided by Apache TinkerPop to give developers precise control over what tokens (like IDs, labels, keys, etc.) are included when using traversal steps such as valueMap() or other steps that support the .with(...) modulator. Rather than relying on older patterns like valueMap(true), WithOptions makes it explicit and flexible to shape the results of your projections.

 

Under the hood, WithOptions defines a set of static tokens that can be passed into the .with() method of a Gremlin traversal. These tokens affect how maps (e.g., those produced by valueMap()) are constructed and what meta-information they include.

 

Here are the core WithOptions tokens you’ll commonly use with valueMap().

 

Token

What it does?

WithOptions.tokens

Indicates that token values like ID and label should be considered in the value map.

WithOptions.ids

If used, includes the element ids in the map output.  

WithOptions.labels

If used, includes the element label in the map.

WithOptions.keys

For property maps, includes the property keys.

WithOptions.values

For property maps, includes the property values.

WithOptions.all

Includes all possible tokens (IDs, labels, keys, values) in the result. 

WithOptions.none

Includes no tokens at all.

WithOptions.list and WithOptions.map

Control how map entries are indexed (e.g., 2-item lists vs. map representation).

   

WithOptions.tokens

Enables inclusion of element tokens (like id and label) in the valueMap() output.

g.V().
  valueMap().
  with(WithOptions.tokens)

gremlin> g.V().
......1>   valueMap().
......2>   with(WithOptions.tokens)
==>[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]]

  

By default, valueMap() returns only properties, adding WithOptions.tokens tells Gremlin that include element-level metadata (ID + label) in the result map.

 

WithOptions.ids

Includes only the element ID, not the label. Must be used together with WithOptions.tokens.

 

g.V().
  hasLabel('product').
  valueMap('name').
  with(WithOptions.tokens)

Above snippet print id, label and name properties of vertices.

gremlin> g.V().
......1>   hasLabel('product').
......2>   valueMap('name').
......3>   with(WithOptions.tokens, WithOptions.ids)
==>[id:14,name:[Laptop]]
==>[id:17,name:[Smartphone]]
==>[id:20,name:[Java Programming Book]]
==>[id:23,name:[T-Shirt]]

  

Let's add 'WithOptions.ids' to it.

 

g.V().
  hasLabel('product').
  valueMap('name').
  with(WithOptions.tokens, WithOptions.ids)

gremlin> g.V().
......1>   hasLabel('product').
......2>   valueMap('name').
......3>   with(WithOptions.tokens)
==>[id:14,label:product,name:[Laptop]]
==>[id:17,label:product,name:[Smartphone]]
==>[id:20,label:product,name:[Java Programming Book]]
==>[id:23,label:product,name:[T-Shirt]]

WithOptions.labels

Includes only the element label, not the ID, it is used with WithOptions.tokens

g.V().
  hasLabel('product').
  valueMap().
  with(WithOptions.tokens, WithOptions.labels)

gremlin> g.V().
......1>   hasLabel('product').
......2>   valueMap().
......3>   with(WithOptions.tokens, WithOptions.labels)
==>[label:product,name:[Laptop],sku:[P1001]]
==>[label:product,name:[Smartphone],sku:[P1002]]
==>[label:product,name:[Java Programming Book],sku:[P2001]]
==>[label:product,name:[T-Shirt],sku:[P3001]]

WithOptions.all

Includes all possible tokens (IDs, labels, keys, values). It is essentially a shorthand for including everything available.

g.V().
  hasLabel('product').
  valueMap().
  with(WithOptions.tokens, WithOptions.all)

gremlin> g.V().
......1>   hasLabel('product').
......2>   valueMap().
......3>   with(WithOptions.tokens, WithOptions.all)
==>[id:14,label:product,name:[Laptop],sku:[P1001]]
==>[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]]

  

WithOptions.none

 

g.V().
  hasLabel('product').
  valueMap().
  with(WithOptions.tokens, WithOptions.none)

gremlin> g.V().
......1>   hasLabel('product').
......2>   valueMap().
......3>   with(WithOptions.tokens, WithOptions.none)
==>[name:[Laptop],sku:[P1001]]
==>[name:[Smartphone],sku:[P1002]]
==>[name:[Java Programming Book],sku:[P2001]]
==>[name:[T-Shirt],sku:[P3001]]

  

Previous                                                    Next                                                    Home

No comments:

Post a Comment