#
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();