# Migrating from Gremlin to Curiosity

If you are familiar with Gremlin (the graph traversal language used by Apache TinkerPop and others), this guide will help you translate your knowledge to Curiosity's C# Query fluent interface.

This guide covers the query language as available in Workspace Customizations (Endpoints, Shell, AI Tools, Code Tasks, etc.). Data Connectors have access to a limited subset of this query language, optimized for referencing existing data during ingestion jobs.

Both Gremlin and Curiosity's Query are imperative, fluent-style query builders, so the transition is often quite natural.

# Basic Lookups

# 1. Get all nodes of a label

Gremlin

g.V().hasLabel('Person')

Curiosity

Query().StartAt("Person").Emit()

# 2. Get a node by ID

Gremlin

g.V().hasLabel('Person').hasId('123')

Curiosity

Query().StartAt("Person", "123").Emit()

# 3. Get a node by Key/Property

Gremlin

g.V().hasLabel('Person').has('email', 'alice@example.com')

Curiosity

Query().StartAt("Person", "email", "alice@example.com").Emit()
// OR
Query().StartAt("Person").WhereString("email", "alice@example.com").Emit()

# Filtering

# 4. Filter by string property

Gremlin

g.V().hasLabel('Person').has('status', 'Active')

Curiosity

Query().StartAt("Person").WhereString("status", "Active").Emit()

# 5. Filter by numeric property

Gremlin

g.V().hasLabel('Product').has('price', gt(100))

Curiosity

Query().StartAt("Product").Where(n => n.GetFloat("price") > 100).Emit()

# 6. Filter by boolean

Gremlin

g.V().hasLabel('Task').has('completed', true)

Curiosity

Query().StartAt("Task").Where(n => n.GetBool("completed")).Emit()

# 7. Filter NOT equal

Gremlin

g.V().hasLabel('Person').has('status', neq('Deleted'))

Curiosity

Query().StartAt("Person").WhereNotString("status", "Deleted").Emit()

# 8. Filter IN list

Gremlin

g.V().hasLabel('Person').has('role', within('Admin', 'Manager'))

Curiosity

Query().StartAt("Person").WhereString("role", new[]{"Admin", "Manager"}).Emit()

# 9. Filter by Timestamp

Gremlin

g.V().hasLabel('Log').has('timestamp', gte(new Date())) // Syntax varies by implementation

Curiosity

Query().StartAt("Log").WhereTimestamp(from: new Time(2023, 1, 1), to: Time.MaxValue, insideBoundary: true).Emit()

# Relationships

# 10. Simple Traversal

Gremlin

g.V().hasLabel('Person').out('KNOWS').hasLabel('Person')

Curiosity

Query().StartAt("Person").Out("Person", "KNOWS").Emit()

# 11. Traversal ignoring edge type

Gremlin

g.V().hasLabel('Person').out().hasLabel('Department')

Curiosity

Query().StartAt("Person").Out("Department").Emit()

# 12. Traversal ignoring target label

Gremlin

g.V().hasLabel('Person').out('HAS_FILE')

Curiosity

Query().StartAt("Person").Out(edgeType: "HAS_FILE").Emit()

# 13. Multi-hop Traversal

Gremlin

g.V().hasLabel('Person').repeat(out('KNOWS').hasLabel('Person')).times(2)

Curiosity

Query().StartAt("Person").OutMany(levels: 2, nodeTypes: new[] { "Person" }, edgeTypes: new[] { "KNOWS" }).Emit()

# 14. Check if related to specific node

Gremlin

g.V().hasLabel('Person').filter(out('WORKS_FOR').hasId('C1'))

Curiosity

Query().StartAt("Person").IsRelatedToVia(Node.GetUID("Company", "C1"), "WORKS_FOR").Emit()

# 15. Check if NOT related

Gremlin

g.V().hasLabel('Person').filter(not(out('HAS_Badge')))

Curiosity

Query().StartAt("Person").IsNotRelatedToVia(edgeType: "HAS_Badge").Emit()

# Advanced Logic

# 16. Union

Gremlin

g.V().hasLabel('Manager').union(g.V().hasLabel('Director'))

Curiosity

var q1 = Query().StartAt("Manager");
var q2 = Query().StartAt("Director");
q1.Union(q2).Emit();

# 17. Intersection

Gremlin

g.V().hasLabel('Person').has('age', gt(30)).has('status', 'Active')
// (Implicit chain)

Curiosity

Query().StartAt("Person").Where(n => n.GetInt("age") > 30).WhereString("status", "Active").Emit()

// Or explicit set intersection
var q1 = Query().StartAt("Person").Where(n => n.GetInt("age") > 30);
var q2 = Query().StartAt("Person").WhereString("status", "Active");
q1.Intersect(q2).Emit();

# 18. Sorting and Limiting

Gremlin

g.V().hasLabel('Product').order().by('created', decr).limit(10)

Curiosity

Query().StartAt("Product").SortByTimestamp(oldestFirst: false).Take(10).Emit()

# 19. Counting

Gremlin

g.V().hasLabel('Person').count()

Curiosity

int count = Query().StartAt("Person").Count();

# 20. Distinct

Gremlin

g.V().out('FRIEND').dedup()

Curiosity

Query().StartAt("Person").Out("Person", "FRIEND").Distinct().Emit()

# 21. Path Finding (Shortest Path)

Gremlin

g.V('A').repeat(both().simplePath()).until(hasId('B')).path().limit(1)

Curiosity

Query().PathBetween(Node.GetUID("Person", "A"), Node.GetUID("Person", "B"))

# 22. Text Search

Gremlin

// Vendor specific (e.g., JanusGraph or Neptune text predicates)
g.V().hasLabel('Document').has('content', textContains('project'))

Curiosity

Query().StartSearch("Document", "content", Search.Parse("project")).Emit()

# Complex Scenarios

# 23. Vector / Similarity Search

Gremlin

// Often vendor-specific steps, e.g. Neptune:
g.withSideEffect('Neptune#fts.endpoint', '...')
 .V().hasLabel('Item')
 .has('embedding', neptune.fts.near(vector, 10))

Curiosity

Query().StartAtSimilarText("Machine Learning applications").Emit()

# 24. Grouping and Aggregation (LINQ)

Gremlin

g.V().hasLabel('Person').group().by('department').by(count())

Curiosity

// Curiosity queries stream results, so grouping is done in-memory via LINQ
Query().StartAt("Person")
       .Emit()
       .AsEnumerable()
       .GroupBy(n => n.GetString("department"))
       .Select(g => new { Department = g.Key, Count = g.Count() });

# 25. Custom Logic / User Defined Functions

Gremlin

// Using lambdas (if supported by the engine)
g.V().hasLabel('Person').map { it.get().value('salary') > 100000 ? 'High' : 'Standard' }

Curiosity

// Use standard C# methods or local functions
string CalculateSalaryBand(Node n) {
    return n.GetFloat("salary") > 100000 ? "High" : "Standard";
}

var results = Query().StartAt("Person")
       .Emit()
       .AsEnumerable()
       .Select(n => new {
           Name = n.GetString("name"),
           SalaryBand = CalculateSalaryBand(n)
       });

# 26. Modifying Data

To modify data, you use the ITransactionQuery interface (accessed via .Tx()) or Graph methods directly for atomic operations.

# Create or Update a Node

Gremlin

g.mergeV([(T.label): 'Person', email: 'alice@example.com'])
 .option(Merge.onCreate, [name: 'Alice'])
 .option(Merge.onMatch, [name: 'Alice'])

Curiosity

// GetOrAdd (Atomic Upsert)
var node = await Graph.GetOrAddLockedAsync("Person", "alice@example.com");
node.Set("name", "Alice");
await Graph.CommitAsync(node);

# Update Properties on Query Results

Gremlin

g.V().hasLabel('Person').has('status', 'Old')
 .property('status', 'Inactive')

Curiosity

// Update all matching nodes
await Query().StartAt("Person")
             .WhereString("status", "Old")
             .Tx()
             .Set("status", "Inactive")
             .CommitAsync();

# Create Relationships

Gremlin

g.V().has('Person', 'id', '1').as('p')
 .V().has('Company', 'id', 'C1').as('c')
 .addE('WORKS_FOR').from('p').to('c')

Curiosity

await Query().StartAt("Person", "1")
             .Tx()
             .AddUniqueEdgeTo("WORKS_FOR", "Company", "C1")
             .CommitAsync();

# Delete Nodes

Gremlin

g.V().hasLabel('Data').has('obsolete', true).drop()

Curiosity

await Query().StartAt("Data")
             .Where(n => n.GetBool("obsolete"))
             .Tx()
             .Delete()
             .CommitAsync();