helix-query-from-cypher
Cypher To Helix Queries
Translate Cypher into Helix Rust DSL by mapping patterns into explicit anchors, traversals, predicates, and return shaping.
When To Use
Use this skill when the task is to:
- translate a Cypher query into Helix Rust DSL
- port a Neo4j query into a stored Helix route
- replace
MATCH,OPTIONAL MATCH,WHERE,RETURN,DISTINCT,ORDER BY,LIMIT,MERGE,CASE,UNWIND,FOREACH, orDETACH DELETEwith Helix DSL equivalents - explain how a Cypher graph pattern should be expressed in Helix Rust
Do not use this skill as the main guide for Gremlin, SQL, or dynamic inline-query JSON.
First Steps
Before translating:
- Inspect the local repo for real labels, edge labels, property names, and route style.
- Parse the Cypher into anchors, edge directions, hop depth, filters, return shape, ordering, and pagination.
- Decide whether the target route is read or write.
- Identify optional branches, per-element writes, null or existence checks, and timestamp usage.
- Identify any Cypher constructs that need semantic rather than literal translation.
If the local repo does not already contain an obvious Helix pattern, use:
docs/cypher-rosetta.mddocs/dsl-cheatsheet.mdexamples/authoring-patterns.mdexamples/search-patterns.md
Translation Workflow
1. Choose The First Anchor
Translate the first practical Cypher node pattern into the narrowest Helix anchor you can justify.
Prefer:
- node ID or edge ID
- unique property lookup
- equality-indexed property lookup
- scoped label scan
- broad label scan
2. Translate Edge Direction Explicitly
Cypher pattern direction should map directly:
()-[:REL]->()toout(Some("REL"))()<-[:REL]-()toin_(Some("REL"))()-[e:REL]->()toout_e(Some("REL"))- undirected or symmetric traversal usually to
both(Some("REL"))orboth_e(Some("REL"))when the schema and task justify it
3. Translate WHERE Into Predicates
Typical mappings:
- equality to
Predicate::eq_param - numeric comparisons to
Predicate::gt_param,gte_param,lt_param,lte_param - membership to
Predicate::is_in_param - null checks to
Predicate::is_null - property-existence checks to
Predicate::has_key - compound logic to
Predicate::and(vec![...])andPredicate::or(vec![...])
4. Translate RETURN Into Explicit Output Shaping
Use:
project(...)for intentional fieldsvalue_map(...)when a looser property map is acceptablecount()for countsdedup()forDISTINCTorder_by,skip,limit, andrangefor result ordering and pagination
5. Handle Non-1:1 Cypher Features Carefully
Do not force literal translations for:
MERGE- path-returning queries
RETURN *
Translate them semantically instead.
Key Cypher Rules
MATCH
MATCH usually becomes one or more var_as(...) bindings plus explicit traversal steps.
WHERE
WHERE becomes explicit predicate calls. Keep parameter names aligned with the user's query or local route conventions.
RETURN
RETURN should become a deliberate Helix result shape, not an implicit full-object dump unless the route truly wants that.
DISTINCT
Use dedup() before shaping or returning results.
MERGE
There is no single drop-in MERGE translation pattern in this skill. Use explicit read-first branching with var_as_if.
OPTIONAL MATCH
Use .optional(sub(...)) when a related traversal should not eliminate the root path just because the optional branch has no match.
CASE WHEN
Use .choose(...) for traversal-level if/then/else logic.
UNWIND And FOREACH
Use for_each_param(...) when a write route needs to iterate an array parameter and perform graph work per element.
Multi-Hop Patterns
Use bounded repeat(RepeatConfig::new(...).times(N).emit_after()) for Cypher patterns like [:REL*1..N].
IS NULL And Property Existence
Use Predicate::is_null for null-style checks and Predicate::has_key when the query is really testing whether the property exists.
Server-Side Timestamps
Use the server-side timestamp helper from your current Helix build when translating Cypher timestamp() usage.
Canonical Example
Cypher:
MATCH (u:User {userId: $userId})-[:FOLLOWS]->(v:User)
WHERE v.status = $status
RETURN v
ORDER BY v.createdAt DESC
LIMIT $limit
Helix Rust DSL:
read_batch()
.var_as(
"user",
g().n_with_label("User")
.where_(Predicate::eq_param("userId", "userId")),
)
.var_as(
"results",
g().n(NodeRef::var("user"))
.out(Some("FOLLOWS"))
.where_(Predicate::eq_param("status", "status"))
.order_by("createdAt", Order::Desc)
.limit(Expr::param("limit"))
.project(vec![
PropertyProjection::new("$id"),
PropertyProjection::new("userId"),
PropertyProjection::new("name"),
PropertyProjection::new("status"),
PropertyProjection::new("createdAt"),
]),
)
.returning(["results"])
Anti-Patterns
Do not:
- translate Cypher by string substitution alone
- ignore edge direction
- preserve Cypher variable names if they conflict with the local Helix route style and make the translation worse
- assume every Cypher clause is a single-token replacement in Helix
- return every property by default just because the Cypher query returned a node variable
- invent labels, properties, or edge names instead of reading the target schema
Validation Checklist
Before finishing:
- verify the first anchor is correct and narrow enough
- verify edge directions are translated correctly
- verify
WHEREclauses became explicitPredicatelogic - verify optional traversals use
optional(sub(...))when required - verify multi-hop traversal uses bounded
repeat(...)with explicit emission behavior - verify
RETURNbecame an intentional Helix output shape - verify
DISTINCT,ORDER BY,SKIP, andLIMITwere mapped deliberately - verify
CASE,UNWIND,FOREACH, delete, and timestamp logic were translated to Helix-native constructs when present - verify
MERGEwas translated semantically, not literally - verify labels, edge labels, and properties match the local repo exactly
Repo References
For shared references in this repo, see:
docs/cypher-rosetta.mddocs/dsl-cheatsheet.mdexamples/authoring-patterns.mdexamples/search-patterns.md
More from helixdb/skills
helix-query-json-dynamic
Build and validate HelixDB dynamic inline-query requests for POST /v1/query. Use when the task involves dynamic queries, inline query JSON, the inline AST (steps, predicates, expressions, projections), parameter_types, DateTime coercion, query warming, or debugging a request body sent directly to the Helix gateway. See REFERENCE.md for every AST variant and EXAMPLES.md for copy-pasteable payloads.
10helix-query-authoring
Write and revise HelixDB Rust DSL stored queries from scratch. Use when the task is to add, update, or review a Helix query built with read_batch, write_batch, traversal builders, projections, indexes, BM25 text search, or vector search. Inspect local labels, edges, properties, and existing query patterns before inventing new code. See REFERENCE.md for the full builder catalog and EXAMPLES.md for end-to-end patterns.
10helix-query-optimize
Review and improve HelixDB query performance and query shape. Use when the task is to optimize a slow Helix query, improve anchor choice, tighten index usage, reduce traversal breadth, slim projections, fix BM25 or vector search scope, or decide between stored and dynamic routes.
8helix-query-from-gremlin
Translate Gremlin and TinkerPop-style traversals into HelixDB Rust DSL stored queries. Use when the input contains Gremlin, TinkerPop, g.V, g.E, hasLabel, has, out, in, both, outE, inE, repeat, emit, dedup, valueMap, count, range, or limit and the goal is to produce an equivalent Helix Rust query.
7