Documentation Index
Fetch the complete documentation index at: https://docs.helix-db.com/llms.txt
Use this file to discover all available pages before exploring further.
For the complete documentation index optimized for AI agents, see llms.txt.Every traversal in the DSL starts with
g() — a fresh, empty graph cursor — followed
by a source step that produces nodes or edges. This page covers the source steps:
how to anchor on an id, a label, or an indexed property, and the difference between a
SourcePredicate and a full Predicate.
The batch shell
Before any source step, you need a batch to hold the traversal. Reads usereadBatch(); writes use writeBatch(). Inside, every named result is introduced
with varAs("name", traversal), and returning([...]) chooses which names the caller
receives.
nWithLabel("User")is a DSL convenience. On the wire it becomes a label-filtered source:{"NWhere": {"Eq": ["$label", {"String": "User"}]}}. The virtual$labelfield is implicit on every node.count()is a unit step — it carries no payload — so it serializes as the bare string"Count"rather than a{Count: ...}object.
Anchoring on a node id
When you already have a node id (returned by an earlier query, or stored alongside your application data), pass it tog().n(...) to skip any index lookup at all.
NodeRef has several useful constructors:
NodeRef.id(42n)/NodeRef.ids([1n, 2n, 3n])— one or many concrete ids.NodeRef.var("name")— the result of an earliervarAsbinding.NodeRef.param("name")— a value supplied at request time (covered in Parameters & bundles).NodeRef.all()— every node in the graph; rarely what you want.
i64 and can exceed Number.MAX_SAFE_INTEGER. Use BigInt literals
(42n) in TypeScript when authoring queries that touch ids directly.
Anchoring on a label
nWithLabel(label) selects every node of a given label. Without a follow-up filter
this is a full label scan, so reach for it only when you genuinely want every node of
that label.
Anchoring on a unique property
The most common starting shape: look up a node by an indexed property. UsenWhere(SourcePredicate.eq(...)) when there is no label scope, or
nWithLabelWhere(label, SourcePredicate) when you want both at the source step.
SourcePredicate vs Predicate
Source steps (nWhere, eWhere, etc.) accept a SourcePredicate, which is a
deliberately smaller set: eq, neq, gt, gte, lt, lte, between, hasKey,
startsWith, plus and / or. These are the predicates the storage layer can push
down into an index lookup.
For richer post-filtering — collection membership, regex-ish contains/endsWith,
isNull, not, parameter-bound comparisons — use a .where(Predicate.*) step after
the source. That’s covered in Filtering.
If you’ve already built a SourcePredicate and need to lift it into a full
Predicate, every SourcePredicate exposes .toPredicate().
Anchoring on label plus a predicate
When the source step is both label-scoped and predicate-filtered,nWithLabelWhere
collapses the two into one step. It produces the same JSON as nWhere(And[$label, ...])
but reads more clearly.
Anchoring on edges
Edges have a parallel set of source steps:g().e(EdgeRef.id(...))/EdgeRef.ids([...])— by id.g().eWithLabel("FOLLOWS")— by edge label (e.g. everyFOLLOWSedge in the graph).g().eWhere(SourcePredicate.gt("weight", 0.5))— by indexed edge property.g().eWithLabelWhere("FOLLOWS", SourcePredicate.gte("since", "2026-01-01"))— label + predicate.
.outN() / .inN() / .otherN() to walk back to a node — see
Traversals for the full edge story.
Picking the right anchor
Order of preference, narrowest first:- Node or edge id (
g().n(NodeRef.id(...))). No index lookup at all. - Unique-indexed property (
g().nWhere(SourcePredicate.eq("username", ...))). - Equality-indexed property (same shape, non-unique index).
- Label-scoped predicate scan (
g().nWithLabelWhere(...)). - Plain label scan (
g().nWithLabel(...)). Last resort.
Next Steps
Traversals
Walking the graph from your anchor:
out, in, both, edge variants.Filtering, ordering, paging
Predicates,
.where, ordering, and .limit/.skip/.range.