Showing posts with label gremlin. Show all posts
Showing posts with label gremlin. Show all posts

Saturday, 6 June 2026

Using between() in Gremlin to Simulate startsWith

  

When working with string properties in Gremlin, one common requirement is performing prefix-based searches, similar to a startsWith() operation in traditional programming languages.

 

However, in Apache TinkerPop Gremlin, there is no built-in startsWith() predicate. Unlike SQL, which provides LIKE, or search engines that support regex and text analyzers, Gremlin’s core predicate system is intentionally minimal and optimized around exact matching and range-based comparisons.

 

So how do we perform prefix matching?

The answer lies in understanding how Gremlin compares strings: lexicographically. Because string comparison in Gremlin follows lexicographical (dictionary-style) ordering, we can use the between() predicate to define a range of values that effectively simulates a prefix search.

 

For example:

·      All strings starting with "Ind" fall lexicographically between "Ind" and "Ine".

·      Everything beginning with "B" falls between "B" and "Ba".

 

This works because:

·      String comparisons are character-by-character.

·      Any additional characters after the prefix are naturally included within the defined range.

·      The upper bound acts as a lexical boundary.

·      This technique gives us a lightweight, index-friendly way to perform prefix filtering without regex support.

 

Understanding this pattern is important because:

 

·      Gremlin does not currently support regular expressions natively.

·      There are no built-in fuzzy search or text analysis operators in core TinkerPop.

·      Prefix search is a common requirement in real-world graph applications (user names, product categories, tags, etc.).

·      This approach works efficiently with property indexes in many graph databases.

 

While graph systems like JanusGraph provide additional text search capabilities, mastering this between() pattern ensures your Gremlin queries remain portable across TinkerPop enabled systems.

 

Let’s build a simple Gremlin Graph to understand the between step.

 

Step 1: Get the Graph traversal instance.

graph = TinkerGraph.open()
g = graph.traversal()

   

Step 2: Add Sample Books.

 

g.addV('book').property('title','Data Structures in Java')
g.addV('book').property('title','Database Design Fundamentals')
g.addV('book').property('title','Data Science Handbook')
g.addV('book').property('title','Distributed Systems Concepts')
g.addV('book').property('title','Design Patterns Explained')
g.addV('book').property('title','Deep Learning Basics')
g.addV('book').property('title','Docker in Action')
g.addV('book').property('title','DevOps Handbook')

Step 3: Verify Data

g.V().hasLabel('book').values('title')

gremlin> g.V().
......1>   hasLabel('book').
......2>   values('title')
==>Data Structures in Java
==>Database Design Fundamentals
==>Data Science Handbook
==>Distributed Systems Concepts
==>Design Patterns Explained
==>Deep Learning Basics
==>Docker in Action
==>DevOps Handbook

   

Use between() to Simulate startsWith("Data")

All titles starting with "Data" fall lexicographically between:

 

·      Lower bound: "Data"

·      Upper bound: "Datb"

 

g.V().
  hasLabel('book').
  has('title', between('Data', 'Datb')).
  values('title')

gremlin> g.V().
......1>   hasLabel('book').
......2>   has('title', between('Data', 'Datb')).
......3>   values('title')
==>Data Structures in Java
==>Database Design Fundamentals
==>Data Science Handbook

Expand the Range (Data Dee)

If we want titles starting from "Data" through "Dee".

g.V().
  hasLabel('book').
  has('title', between('Data', 'Dee')).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('book').
......2>   has('title', between('Data', 'Dee')).
......3>   valueMap(true)
==>[id:0,label:book,title:[Data Structures in Java]]
==>[id:2,label:book,title:[Database Design Fundamentals]]
==>[id:4,label:book,title:[Data Science Handbook]]

   

Single Character Example (Titles Starting with "D")

 

g.V().hasLabel('book').
  has('title', between('D','E')).
  values('title').
  order()

gremlin> g.V().hasLabel('book').
......1>   has('title', between('D','E')).
......2>   values('title').
......3>   order()
==>Data Science Handbook
==>Data Structures in Java
==>Database Design Fundamentals
==>Deep Learning Basics
==>Design Patterns Explained
==>DevOps Handbook
==>Distributed Systems Concepts
==>Docker in Action

  

 

Previous                                                    Next                                                    Home

Understanding Gremlin Predicates: eq, neq, gt, lt, and More

  

When working with graph databases using Apache TinkerPop Gremlin, one of the most common tasks is filtering vertices and edges based on specific property values. Whether you are analyzing social networks, transportation networks, or any complex graph structure, the ability to efficiently test values and ranges is crucial.

 

Gremlin provides a rich set of predicates that allow you to compare numbers, strings, or even sets of values. These predicates can be used in steps like has(), where(), or is(), letting you focus your queries on just the nodes and edges that matter.

 

For example, you might want to:

·      Find cities with more than 5 lakes.

·      Identify all routes that avoid certain cities.

·      Query nodes whose IDs fall within a specific range.

·      Filter properties that match a set of allowed or disallowed values.

 

These tasks can be handled efficiently with predicates such as:

 

Predicate

Purpose

eq

Equal to a value

neq

Not equal to a value

gt

Greater than

gte

Greater than or equal                 

lt

Less than

lte

less than or equal                       

inside

Within bounds (exclusive)                            

outside

Outside bounds (exclusive)                           

between

Lower bound inclusive, upper bound exclusive         

within

Matches one or more specified values (list or range) 

without

Excludes one or more specified values (list or range)

 

These predicates provide flexibility in querying graphs, whether you are working with numeric properties (like salaries, age, discounts etc.,) or strings (like city names or airport codes).

 

Why Range and Value Testing Matters?

Efficient range and value testing lets you:

 

·      Filter large graphs quickly: You avoid processing unnecessary nodes.

·      Perform complex queries: Combine within and without to include or exclude specific values.

·      Write cleaner queries: Predicates like inside, outside, or between make your intent explicit.

·      Compare numbers and strings seamlessly: You can handle both numeric and textual data in your graph without switching approaches.

 

1. Student Enrollment Graph

Let’s model a school graph where students can enroll for classes.

 

Step 1: Create Graph traversal instance.

graph = TinkerGraph.open()
g = graph.traversal()

   

Step 2: Create Vertices.

 

// Students
g.addV('student').property('name','Alice').property('age',14).property('grade',9)
g.addV('student').property('name','Bob').property('age',15).property('grade',10)
g.addV('student').property('name','Carol').property('age',13).property('grade',8)
g.addV('student').property('name','David').property('age',14).property('grade',9)
g.addV('student').property('name','Eve').property('age',12).property('grade',7)

// Classes
g.addV('class').property('subject','Math').property('maxStudents',30)
g.addV('class').property('subject','Science').property('maxStudents',25)
g.addV('class').property('subject','History').property('maxStudents',20)
g.addV('class').property('subject','Art').property('maxStudents',15)

Step 3: Connect Students to Classes

// Alice enrolled in Math and Science
g.V().has('student','name','Alice').as('s').
  V().has('class','subject','Math').addE('enrolled_in').from('s')
g.V().has('student','name','Alice').as('s').
  V().has('class','subject','Science').addE('enrolled_in').from('s')

// Bob enrolled in Math and History
g.V().has('student','name','Bob').as('s').
  V().has('class','subject','Math').addE('enrolled_in').from('s')
g.V().has('student','name','Bob').as('s').
  V().has('class','subject','History').addE('enrolled_in').from('s')

// Carol enrolled in Science
g.V().has('student','name','Carol').as('s').
  V().has('class','subject','Science').addE('enrolled_in').from('s')

// David enrolled in Art and History
g.V().has('student','name','David').as('s').
  V().has('class','subject','Art').addE('enrolled_in').from('s')
g.V().has('student','name','David').as('s').
  V().has('class','subject','History').addE('enrolled_in').from('s')

// Eve enrolled in Art only
g.V().has('student','name','Eve').as('s').
  V().has('class','subject','Art').addE('enrolled_in').from('s')

   

Let’s print the Graph and confirm the same.

 

gremlin> g.V().valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:16,label:student,grade:[7],name:[Eve],age:[12]]
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]
==>[id:20,label:class,subject:[Math],maxStudents:[30]]
==>[id:23,label:class,subject:[Science],maxStudents:[25]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]
==>[id:26,label:class,subject:[History],maxStudents:[20]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]
==>[id:29,label:class,subject:[Art],maxStudents:[15]]
gremlin> 
gremlin> g.E().valueMap(true)
==>[id:32,label:enrolled_in]
==>[id:33,label:enrolled_in]
==>[id:34,label:enrolled_in]
==>[id:35,label:enrolled_in]
==>[id:36,label:enrolled_in]
==>[id:37,label:enrolled_in]
==>[id:38,label:enrolled_in]
==>[id:39,label:enrolled_in]

   

2. Introducing Predicates

2.1 eq: Equal To

Matches values that are exactly equal.

 

Example 1: Students who are exactly 14 years old.

 

g.V().
  hasLabel('student').
  has('age', eq(14)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', eq(14)).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

  You can even use "has('age', 14)", eq is optional in simple has() comparisons.

 

g.V().
  hasLabel('student').
  has('age', 14).
  valueMap(true)

 gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', 14).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

Example 2: Get all the students named Alice.

g.V().
  hasLabel('student').
  has('name', eq('Alice')).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('name', eq('Alice')).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]

   

2.2 neq: Not Equal To

Matches values not equal to the given value.

 

Example 1: Students not in grade 9

 

g.V().
  hasLabel('student').
  has('grade', neq(9)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('grade', neq(9)).
......3>   valueMap(true)
==>[id:16,label:student,grade:[7],name:[Eve],age:[12]]
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]

   

Example 2: Students not named Bob

 

g.V().
  hasLabel('student').
  has('name', neq('Bob')).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('name', neq('Bob')).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:16,label:student,grade:[7],name:[Eve],age:[12]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

   

2.3 gt: Greater Than

 

Example: Students older than 14.

 

g.V().
  hasLabel('student').
  has('age', gt(14)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', gt(14)).
......3>   valueMap(true)
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]

   

2.4 gte: Greater Than or Equal

 

Example: Students Greater than or equal to 14

 

g.V().
  hasLabel('student').
  has('age', gte(14)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', gte(14)).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

   

2.5 lt: Less Than

Example: Students age less than 14.

 

g.V().
  hasLabel('student').
  has('age', lt(14)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', lt(14)).
......3>   valueMap(true)
==>[id:16,label:student,grade:[7],name:[Eve],age:[12]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]

   

2.6 lte: Less Than or Equal

Example: Students age less than or equal 14.

 

g.V().
  hasLabel('student').
  has('age', lte(14)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', lte(14)).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:16,label:student,grade:[7],name:[Eve],age:[12]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

   

2.7 inside(lower, upper)

Value is strictly between bounds. Both bounds are excluded (lower < value < upper).

 

Example: Students aged between 12 and 15 (exclusive).

 

g.V().
  hasLabel('student').
  has('age', inside(12, 15)).
  valueMap(true)

 gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', inside(12, 15)).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

   

2.8 outside(lower, upper)

Value is less than lower OR greater than upper, bounds excluded (value < lower OR value > upper).

 

Example: Students younger than 13 or older than 14:

 

g.V().
  hasLabel('student').
  has('age', outside(13, 14)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', outside(13, 14)).
......3>   valueMap(true)
==>[id:16,label:student,grade:[7],name:[Eve],age:[12]]
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]

   

2.9 between(lower, upper)

Lower bound inclusive and upper bound exclusive (lower ≤ value < upper).

 

Example: Students aged 12, 13, or 14:

 

g.V().
  hasLabel('student').
  has('age', between(12, 15)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('age', between(12, 15)).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:16,label:student,grade:[7],name:[Eve],age:[12]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

   

2.10 within(...)

'within' predicate matches any one of the provided values. It can take:

 

·      Explicit values

·      A range

·      A list

 

Example 1: Explicit values

Students in grades 8, 9, or 10.

 

g.V().
  hasLabel('student').
  has('grade', within(8, 9, 10)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('grade', within(8, 9, 10)).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

   

Example 2: Range syntax

 

g.V().
  hasLabel('student').
  has('grade', within(8..10)).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('grade', within(8..10)).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

Example 3: String search

Classes that are Math or Science:

g.V().
  hasLabel('class').
  has('subject',within('Math','Science')).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('class').
......2>   has('subject',within('Math','Science')).
......3>   valueMap(true)
==>[id:20,label:class,subject:[Math],maxStudents:[30]]
==>[id:23,label:class,subject:[Science],maxStudents:[25]]

Example 4: Using list notation

g.V().
  hasLabel('student').
  has('grade', within([8, 9, 10])).
  valueMap(true)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('grade', within([8, 9, 10])).
......3>   valueMap(true)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]
==>[id:8,label:student,grade:[8],name:[Carol],age:[13]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

2.11 without(...)

It is just opposite of within, must NOT match any provided value.

 

Example 1: Students NOT in grades 7 or 8.

g.V().
  hasLabel('student').
  has('grade',without(7,8)).
  valueMap().
  with(WithOptions.tokens)

gremlin> g.V().
......1>   hasLabel('student').
......2>   has('grade',without(7,8)).
......3>   valueMap().
......4>   with(WithOptions.tokens)
==>[id:0,label:student,grade:[9],name:[Alice],age:[14]]
==>[id:4,label:student,grade:[10],name:[Bob],age:[15]]
==>[id:12,label:student,grade:[9],name:[David],age:[14]]

   

Example 2: Classes that are NOT Art.

 

g.V().
  hasLabel('class').
  has('subject',without('Art')).
  valueMap().
  with(WithOptions.tokens)

gremlin> g.V().
......1>   hasLabel('class').
......2>   has('subject',without('Art')).
......3>   valueMap().
......4>   with(WithOptions.tokens)
==>[id:20,label:class,subject:[Math],maxStudents:[30]]
==>[id:23,label:class,subject:[Science],maxStudents:[25]]
==>[id:26,label:class,subject:[History],maxStudents:[20]]

   

In summary, understanding value and range testing predicates is fundamental to writing clean, efficient, and expressive Gremlin traversals.

 

In this post, we explored how predicates like:

 

·      eq, neq: exact matching

·      gt, gte, lt, lte: numeric comparisons

·      inside, outside, between: mathematical range control

·      within, without: multi-value inclusion and exclusion

 

these predicates allow you to precisely filter vertices and edges based on property values.

 


Previous                                                    Next                                                    Home