When working with graph data, one of the first things you need is visibility, what properties exist on a vertex or an edge, and what values do they hold? Before writing complex traversals, filters, or aggregations, it is essential to understand the structure and content of the data stored in your graph.
Apache TinkerPop provides the valueMap step precisely for this purpose. It allows you to inspect the properties of vertices and edges in a structured way, returning them as key–value pairs that are easy to reason about and manipulate in a traversal. In many ways, valueMap plays a role similar to inspecting a row in a relational table but adapted to the flexible, schema-optional nature of graphs.
In this post, we will explore how to use valueMap effectively in Gremlin. We will start with the basics retrieving all properties of a vertex and gradually move toward more refined use cases, such as selecting specific properties, including element IDs and labels, improving readability using unfold, and inspecting edge properties.
By the end of this article, you will understand:
· How valueMap represents vertex and edge properties
· Why property values are returned as lists
· How to include IDs and labels in the output
· How to limit the result to only the properties you need
· When and why to use unfold for better readability
· How valueMap can be used as a powerful debugging and exploration tool
Whether you are new to Gremlin or already writing production traversals, mastering valueMap will help you explore your graph faster, write cleaner queries, and avoid unnecessary data retrieval.
1. Simple E-Commerce domain
Vertex Labels
· city → Bengaluru, Hyderabad, Chennai, Pune
· category → Electronics, Books, Clothing
· product → Laptop, Smartphone, Java Book, T-Shirt
· customer → Amit, Priya, Rohit
· order → O9001 … O9005
Edge Labels
· belongsTo → product → category
· placed → customer → order
· contains → order → product
· deliveredTo → order → city
This models a simple e-commerce order flow:
A customer places orders → orders contain products → products belong to categories → orders are delivered to cities.
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()
2. Understanding valueMap in Gremlin
When you work with a graph, every vertex and edge can have properties, small pieces of information stored as key–value pairs. In Gremlin, the valueMap() step is used to retrieve these properties in a structured way.
At a high level, valueMap:
· Extracts properties from vertices or edges
· Returns them as a map of key → value
· Is typically used at the end of a traversal to inspect results
Think of valueMap as asking the graph "Show me all (or some) of the properties of the elements I’ve reached".
2.1 What does valueMap() return?
A simple call like below return the customerId and name of all the customers.
g.V(). hasLabel('customer'). valueMap()
gremlin> g.V(). ......1> hasLabel('customer'). ......2> valueMap() ==>[customerId:[C102],name:[Priya Iyer]] ==>[customerId:[C103],name:[Rohit Verma]] ==>[customerId:[C101],name:[Amit Sharma]]
There are two important things to notice here.
· Properties are returned as lists
· Even though name and customerId have only one value, Gremlin returns them as lists.
This is because:
· Vertices in TinkerPop can have multiple values for the same property key
· valueMap always uses a collection to stay consistent
This behavior often surprises beginners, but it’s intentional.
2.2 IDs and labels are NOT included by default
By default, valueMap() only returns user-defined properties. It does not include:
· the vertex or edge ID
· the element label
To include them, you must explicitly ask for them by passing true as the first parameter to the valueMap step.
g.V(). hasLabel(‘customer’). valueMap(true)
gremlin> g.V(). …...1> hasLabel(‘customer’). …...2> valueMap(true) [id:33,label:customer,customerId:[C102],name:[Priya Iyer]] [id:36,label:customer,customerId:[C103],name:[Rohit Verma]] [id:30,label:customer,customerId:[C101],name:[Amit Sharma]]
This is extremely useful when:
· debugging traversals
· understanding what kind of element you are looking at
· learning a new graph schema
2.3 Selecting specific properties
You can also tell valueMap exactly which properties you want.
g.V(). hasLabel('customer'). valueMap('name')
Above snippet select only the customer names.
gremlin> g.V(). ......1> hasLabel('customer'). ......2> valueMap('name') ==>[name:[Priya Iyer]] ==>[name:[Rohit Verma]] ==>[name:[Amit Sharma]]
2.4 Select id, label along with name property.
Just pass the first argument as true to select id, label along with name property.
g.V(). hasLabel('customer'). valueMap(true, 'name')
gremlin> g.V(). ......1> hasLabel('customer'). ......2> valueMap(true, 'name') ==>[id:33,label:customer,name:[Priya Iyer]] ==>[id:36,label:customer,name:[Rohit Verma]] ==>[id:30,label:customer,name:[Amit Sharma]]
3. Using fold and unfold with valueMap
When valueMap() is used in a traversal, it produces a map per element (per vertex or edge). What fold and unfold do is control how those maps are bundled or unbundled as they flow through the traversal.
Think in terms of packing and unpacking.
gremlin> g.V(). ......1> hasLabel('customer'). ......2> valueMap() ==>[customerId:[C102],name:[Priya Iyer]] ==>[customerId:[C103],name:[Rohit Verma]] ==>[customerId:[C101],name:[Amit Sharma]]
As you see above output, customer details produced as stream of maps.
Using unfold() with valueMap
gremlin> g.V(). ......1> hasLabel('customer'). ......2> valueMap(). ......3> unfold() ==>customerId=[C102] ==>name=[Priya Iyer] ==>customerId=[C103] ==>name=[Rohit Verma] ==>customerId=[C101] ==>name=[Amit Sharma]
unfold() breaks a collection into individual elements, each entry (key–value pair) is emitted separately
Using fold() with valueMap
gremlin> g.V(). ......1> hasLabel('customer'). ......2> valueMap(). ......3> fold() ==>[[customerId:[C102],name:[Priya Iyer]],[customerId:[C103],name:[Rohit Verma]],[customerId:[C101],name:[Amit Sharma]]]
fold() does the opposite of unfold(). It collects all incoming elements and packs them into a single list.
When should you use fold() with valueMap?
Use fold() when:
· You want to return a single result
· You are passing results to an application
· You want to do post-processing
· You want to aggregate results
4. Using select() with valueMap
The valueMap() step gives you all (or many) properties of an element. But often, you don’t want everything, you only want a few specific fields from the result. That’s where select() comes in.
At a high level, select() lets you pick specific keys from a map and discard the rest.
Example:
g.V(). hasLabel('customer'). valueMap(). select('customerId','name')
Above snippet select customerId and name properties.
gremlin> g.V(). ......1> hasLabel('customer'). ......2> valueMap(). ......3> select('customerId','name') ==>[customerId:[C102],name:[Priya Iyer]] ==>[customerId:[C103],name:[Rohit Verma]] ==>[customerId:[C101],name:[Amit Sharma]]
valueMap('key') vs valueMap().select('key')
· valueMap('key') limits properties at the source, more efficient and is preferred when you know what you want upfront.
· valueMap().select('key') filters properties after retrieval
In summary, the valueMap step is one of the most practical tools in Gremlin for understanding and exploring graph data. Whether you are learning a new graph model, debugging a traversal, or inspecting results interactively, valueMap gives you clear visibility into the properties stored on vertices and edges. By combining valueMap with steps like unfold, fold, and select, you gain more control over the structure and readability of query results. These steps allow you to move smoothly between exploratory console-friendly output and application-ready result formats.
Previous Next Home
No comments:
Post a Comment