Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

What is HelixQL?

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

HelixQL is a strongly typed, compiled query language for HelixDB that combines the best features of Gremlin, Cypher, and Rust to provide type-safe graph and vector queries.

Why use HelixQL instead of other query languages?

HelixQL addresses key limitations found in existing graph query languages:

  • Type safety: Queries are validated at compile time, catching errors before runtime
  • Readability: Clean, concise syntax that remains readable even for complex queries
  • Performance: Compiled queries execute faster than dynamically parsed alternatives
  • Developer experience: IDE support with autocomplete and type checking

Unlike Gremlin’s verbose syntax or Cypher’s runtime parsing, HelixQL provides a modern, type-safe approach to graph querying.

How do I write a HelixQL query?

HelixQL queries follow a simple, declarative syntax:

QUERY QueryName(param1: Type, param2: Type) =>
    result <- traversal_expression
    RETURN result

Query components

  • QUERY: Keyword to start a query definition
  • QueryName: Identifier for the query
  • parameters: Input parameters in parentheses
  • Type: Type of the parameter (e.g. String, I32, F64, Boolean, [Type] or schema Node/Edge)
  • =>: Separates query header from body
  • <-: Assignment operator
  • RETURN: Specifies output values
  • //: Single line comments

Next Steps

- **[Deploy your first query](https://docs.helix-db.com/cli/getting-started)** — Learn how to deploy your first query in HelixQL.

- **[Schemas](schema/schema-definition.md)** — Learn about how to build your schema in HelixQL.

- **[Traversals](select/selectN.md)** — Learn about how to traverse the graph in HelixQL.

- **[Creating data](create/addN.md)** — Learn about how to create data in HelixQL.

- **[Vector operations](vectors/searching.md)** — Learn about how to perform vector operations in HelixQL.

- **[Keyword search](keyword_search.md)** — Learn about how to perform keyword search in HelixQL.

- **[Properties](properties/property-access.md)** — Learn about how to access properties in HelixQL.

- **[Conditionals](conditionals/conditions.md)** — Learn about how to use conditionals in HelixQL.

- **[Result operations](result_ops.md)** — Learn about how to perform result operations in HelixQL.

- **[Output values](output_values.md)** — Learn about how to output values in HelixQL.

- **[Types](types.md)** — Learn about the types of HelixQL.

- **[Errors](errors/E101.md)** — Troubleshoot errors in HelixQL.

Helix Types

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Types

HelixQL supports the following types:

TypeDescriptionTypeDescription
IDUUID for nodes, edges and vectors        [T]Array of any type
DateTimestamp or RFC3339 stringF3232-bit floating point
StringText dataF6464-bit floating point
BooleanTrue/false valueU88-bit unsigned integer
I88-bit signed integerU1616-bit unsigned integer
I1616-bit signed integerU3232-bit unsigned integer
I3232-bit signed integerU6464-bit unsigned integer
I6464-bit signed integerU128128-bit unsigned integer

HelixQL Language Reference

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Every HelixQL feature on one page. Each entry shows what it does, its syntax variants, and links to the full documentation.

Query Structure


QUERY

Define a named query with typed parameters.

QUERY GetUser(user_id: ID) =>
QUERY CreateUser(name: String, age: U8) =>
QUERY GetAllUsers() =>
  1. A query with one typed parameter.
  2. A query with multiple typed parameters.
  3. A query with no parameters.

See What is HelixQL? for more details.


RETURN

Specify query output values.

RETURN users
RETURN user, posts
RETURN users::{name, age}
RETURN users::ID
RETURN "ok"
RETURN NONE
  1. Return a single binding.
  2. Return multiple bindings.
  3. Return projected fields.
  4. Return only IDs.
  5. Return a string literal.
  6. Return no payload.

See Output Values for more details.


<- (Assignment)

Bind the result of an expression to a variable.

user <- N<User>(user_id)
users <- N<User>::WHERE(_::{age}::GT(18))
count <- N<User>::COUNT
  1. Bind a node lookup to user.
  2. Bind a filtered traversal to users.
  3. Bind a count result to count.

See What is HelixQL? for more details.


Comments (//)

Single-line comments.

// This is a comment
user <- N<User>(user_id) // inline comment
  1. A standalone comment line.
  2. A comment at the end of a statement.

See What is HelixQL? for more details.


Types


Scalar Types

Built-in primitive types for schema fields and query parameters.

name: String
is_active: Boolean
age: U8
count: I64
score: F64
  1. Text data.
  2. True/false value.
  3. 8-bit unsigned integer. Also available: U16, U32, U64, U128.
  4. 64-bit signed integer. Also available: I8, I16, I32.
  5. 64-bit floating point. Also available: F32.

See Helix Types for more details.


ID

UUID identifier type for nodes, edges, and vectors.

QUERY GetUser(user_id: ID) =>
user <- N<User>(user_id)
RETURN user::{userID: ID}
  1. Accept an ID as a query parameter.
  2. Access an element’s unique identifier in a property selection.

See Helix Types for more details.


Date

Timestamp / RFC3339 date type.

created_at: Date
created_at: Date DEFAULT NOW
  1. A date field in a schema.
  2. A date field with automatic timestamp default.

See Helix Types for more details.


[T] (Arrays)

Array of any type.

QUERY Search(vector: [F64]) =>
QUERY CreateUsers(user_data: [{name: String, age: U8}]) =>
tags: [String]
  1. An array of 64-bit floats as a parameter.
  2. An array of objects as a parameter.
  3. An array of strings as a schema field.

See Helix Types for more details.


Schema Definition


N:: (Node Schema)

Define node types with properties.

N::User {
    name: String,
    age: U8,
    email: String,
}
  1. Define a User node type with three properties.

See Schema Definition for more details.


E:: (Edge Schema)

Define edge types with source, target, and optional properties.

E::Follows {
    From: User,
    To: User,
}
E::Friends {
    From: User,
    To: User,
    Properties: {
        since: Date,
        strength: F64
    }
}
  1. An edge with no properties connecting User to User.
  2. An edge with properties connecting User to User.

See Schema Definition for more details.


V:: (Vector Schema)

Define vector embedding types with metadata properties.

V::Document {
    content: String,
    created_at: Date
}
V::Embedding {}
  1. A vector type with metadata properties.
  2. A vector type with no extra properties.

See Schema Definition for more details.


INDEX

Create indexed fields for fast property-based lookup.

N::User {
    INDEX email: String,
    name: String,
}
  1. Index the email field on User nodes for fast lookups via N<User>({email: email}).

See Secondary Indexing for more details.


UNIQUE INDEX

Create unique constraints on fields, preventing duplicates.

N::User {
    UNIQUE INDEX email: String,
    name: String,
}
E::BestFriend UNIQUE {
    From: User,
    To: User,
}
  1. Enforce uniqueness on the email field.
  2. Enforce that only one BestFriend edge can exist from a given user.

See Unique Indexes for more details.


DEFAULT

Set default property values used when a value is not provided on insert.

N::Post {
    title: String,
    view_count: I32 DEFAULT 0,
    status: String DEFAULT "draft",
    created_at: Date DEFAULT NOW,
    is_published: Boolean DEFAULT false,
    archived_at: Date DEFAULT NONE,
}
  1. A schema with defaults for numbers, strings, timestamps, booleans, and NONE.

See Default Values for more details.


Node Operations


AddN

Create new nodes with optional properties.

AddN<User>
AddN<User>({name: "Alice", age: 25, email: "alice@example.com"})
  1. Create a User node with no properties.
  2. Create a User node with a properties object.

See Create Nodes for more details.


N

Select nodes by type, by ID, or by indexed property.

N<User>
N<User>(user_id)
N<User>({email: email})
  1. Select all User nodes.
  2. Select a User by ID.
  3. Select a User by an indexed property value.

See Select Nodes for more details.


UpsertN

Insert-or-update a node. Updates existing nodes from the traversal context, or creates a new one if none are found.

existing <- N<Person>::WHERE(_::{name}::EQ(name))
person <- existing::UpsertN({name: "Alice", age: 30})
  1. Find existing nodes matching a condition.
  2. Upsert with the given properties — updates if found, creates if not.

See Upsert Nodes for more details.


Edge Operations


AddE

Create edges between nodes with optional properties.

AddE<Follows>::From(user1_id)::To(user2_id)
AddE<Friends>({since: "2024-01-15", strength: 0.85})::From(user1_id)::To(user2_id)
  1. Create a Follows edge between two nodes.
  2. Create a Friends edge with properties.

See Create Edges for more details.


E

Select edges by type or by ID.

E<Follows>
E<Follows>(edge_id)
  1. Select all Follows edges.
  2. Select a specific Follows edge by ID.

See Select Edges for more details.


UpsertE

Insert-or-update an edge. Operates on an existing edge traversal context — updates if an edge exists between the specified nodes, creates a new one otherwise.

existing <- E<Knows>
edge <- existing::UpsertE({since: "2024-06-01"})::From(user1_id)::To(user2_id)
  1. Select the edge type to operate on.
  2. Upsert with properties between two nodes — updates if found, creates if not.

See Upsert Edges for more details.


Vector Operations


AddV

Create vector embeddings with optional properties.

AddV<Document>(vector)
AddV<Document>(vector, {content: "Hello world", created_at: created_at})
AddV<Document>(Embed(content), {content: content})
  1. Create a vector with no metadata.
  2. Create a vector with metadata properties.
  3. Create a vector using the built-in Embed function.

See Create Vectors for more details.


V

Select vectors by type or by ID.

V<Document>
V<Document>(vector_id)
  1. Select all Document vectors.
  2. Select a specific vector by ID.

See Select Vectors for more details.


UpsertV

Insert-or-update a vector. Updates existing vectors from the traversal context, or creates a new one if none are found.

existing <- V<Document>::WHERE(_::{content}::EQ(content))
doc <- existing::UpsertV(Embed(content), {content: content})
  1. Find existing vectors matching a condition.
  2. Upsert with new vector data and properties.

See Upsert Vectors for more details.


Embed

Generate vector embeddings from text using your configured embedding model.

AddV<Document>(Embed(content), {content: content})
SearchV<Document>(Embed(query), 10)
  1. Embed text and store it as a vector.
  2. Embed a search query for vector similarity search.

See Embedding Vectors for more details.


Traversal Steps (from Nodes)


::Out

Follow outgoing edges and return the target nodes.

N<User>(user_id)::Out<Follows>
N<User>(user_id)::Out<Follows>::Out<Follows>
  1. Get all users that user_id follows.
  2. Chain traversals to get followers-of-followers.

See Traversals From Nodes for more details.


::In

Follow incoming edges and return the source nodes.

N<User>(user_id)::In<Follows>
  1. Get all users that follow user_id.

See Traversals From Nodes for more details.


::OutE

Return the outgoing edges themselves (with their properties).

N<User>(user_id)::OutE<Follows>
  1. Get the outgoing Follows edge objects from a user.

See Traversals From Nodes for more details.


::InE

Return the incoming edges themselves (with their properties).

N<User>(user_id)::InE<Follows>
  1. Get the incoming Follows edge objects pointing at a user.

See Traversals From Nodes for more details.


Traversal Steps (from Edges)


::FromN

Get the source node of an edge.

E<Creates>(edge_id)::FromN
  1. Get the node that the Creates edge originates from.

See Traversals From Edges for more details.


::ToN

Get the target node of an edge.

E<Creates>(edge_id)::ToN
  1. Get the node that the Creates edge points to.

See Traversals From Edges for more details.


::FromV

Get the source vector of an edge.

E<HasEmbedding>(edge_id)::FromV
  1. Get the vector that the edge originates from.

See Traversals From Edges for more details.


::ToV

Get the target vector of an edge.

E<HasEmbedding>(edge_id)::ToV
  1. Get the vector that the edge points to.

See Traversals From Edges for more details.


Filtering & Conditions


::WHERE

Filter elements by a condition.

N<User>::WHERE(_::{age}::GT(18))
N<User>::WHERE(EXISTS(_::In<Follows>))
N<User>::WHERE(AND(_::{age}::GT(18), _::{age}::LT(30)))
  1. Filter users where age is greater than 18.
  2. Filter users that have at least one follower.
  3. Combine multiple conditions with AND.

See Conditional Steps for more details.


::INTERSECT

Keep only elements that appear in a sub-traversal result for every upstream element.

N<Tag>::WHERE(_::{name}::IS_IN(tag_names))::INTERSECT(_::In<HasTag>)
N<Tag>::WHERE(_::{name}::IS_IN(tag_names))::INTERSECT(_::In<HasTag>)::WHERE(_::{title}::EQ(title))
  1. INTERSECT takes an anonymous traversal expression, such as _::In<HasTag>.
  2. Helix runs that sub-traversal for each upstream element, then keeps only IDs shared across all sub-results.
  3. If the upstream set is empty (or there is no overlap), the final result is empty.

See Traversals From Nodes for more details.


::EQ

Equals comparison. Works with strings, booleans, and numbers.

::WHERE(_::{status}::EQ("active"))
::WHERE(_::{name}::EQ(name))
  1. Filter where status equals the string "active".
  2. Filter where name equals a query parameter.

See Conditional Steps for more details.


::NEQ

Not equals comparison. Works with strings, booleans, and numbers.

::WHERE(_::{role}::NEQ("admin"))
  1. Filter where role is not "admin".

See Conditional Steps for more details.


::GT

Greater than comparison. Works with numbers.

::WHERE(_::{age}::GT(18))
  1. Filter where age is greater than 18.

See Conditional Steps for more details.


::GTE

Greater than or equal comparison. Works with numbers.

::WHERE(_::{rating}::GTE(4.5))
  1. Filter where rating is at least 4.5.

See Conditional Steps for more details.


::LT

Less than comparison. Works with numbers.

::WHERE(_::{age}::LT(30))
  1. Filter where age is less than 30.

See Conditional Steps for more details.


::LTE

Less than or equal comparison. Works with numbers.

::WHERE(_::{priority}::LTE(2))
  1. Filter where priority is at most 2.

See Conditional Steps for more details.


::CONTAINS

Check if a string contains a substring or an array contains an element.

::WHERE(_::{name}::CONTAINS("john"))
::WHERE(_::Out<Follows>::CONTAINS(user))
  1. Filter where name contains the substring "john".
  2. Filter where the outgoing Follows set contains user.

See Conditional Steps for more details.


::IS_IN

Check if a value exists in an array.

::WHERE(_::{status}::IS_IN(["active", "pending"]))
  1. Filter where status is one of "active" or "pending".

See Conditional Steps for more details.


EXISTS

Check if a traversal returns any results.

::WHERE(EXISTS(_::In<Follows>))
::WHERE(!EXISTS(_::In<Follows>))
  1. Filter for elements that have incoming Follows edges.
  2. Filter for elements that have no incoming Follows edges (negated with !).

See Conditional Steps for more details.


Multiple Conditions (AND / OR / !)

Combine conditions with boolean logic.

::WHERE(AND(_::{age}::GT(18), _::{age}::LT(30)))
::WHERE(OR(_::{role}::EQ("admin"), _::{role}::EQ("moderator")))
::WHERE(!AND(_::{is_active}::EQ(true), _::{age}::GT(18)))
  1. Match when all conditions are true.
  2. Match when any condition is true.
  3. Negate an AND/OR block by prefixing !.

See Multiple Conditional Steps for more details.


Result Operations


::FIRST

Get the first element from a traversal result.

N<User>::FIRST
N<User>(user_id)::In<Follows>::FIRST
  1. Get the first User node.
  2. Get the first follower of a user.

See Result Operations for more details.


::COUNT

Count the number of elements in a traversal result.

N<User>::COUNT
N<User>(user_id)::In<Follows>::COUNT
  1. Count all User nodes.
  2. Count how many users follow user_id.

See Result Operations for more details.


::RANGE

Get a range of elements (inclusive start, exclusive end).

N<User>::RANGE(0, 10)
N<User>::RANGE(start, end)
  1. Get the first 10 users (elements 0 through 9).
  2. Get a dynamic page of users using parameters.

See Result Operations for more details.


::ORDER

Sort elements ascending or descending by a property.

N<User>::ORDER<Desc>(_::{age})
N<User>::ORDER<Asc>(_::{age})
  1. Sort users by age descending (oldest first).
  2. Sort users by age ascending (youngest first).

See Result Operations for more details.


Aggregation


::GROUP_BY

Group elements by one or more properties, returning count summaries.

N<User>::GROUP_BY(age)
N<User>::GROUP_BY(department, role)
  1. Group users by age, returning [{age: 25, count: 3}, ...].
  2. Group users by multiple properties.

See Group By for more details.


::AGGREGATE_BY

Group elements by properties, returning counts and full data objects.

N<User>::AGGREGATE_BY(department)
N<User>::AGGREGATE_BY(department, role)
  1. Aggregate users by department, returning counts and full user objects per group.
  2. Aggregate by multiple properties.

See Aggregations for more details.


Property Operations


::{} (Property Selection)

Select specific properties from elements.

users::{name, age}
users::{name, age, email}
  1. Return only name and age from each user.
  2. Return three specific fields.

See Property Access for more details.


::ID

Access an element’s unique identifier.

users::ID
users::{userID: ID, name}
  1. Return only the IDs of users.
  2. Map the ID to a custom field name alongside other properties.

See Property Access for more details.


::!{} (Property Exclusion)

Exclude specific properties, returning all others.

users::!{email, location}
  1. Return all user properties except email and location.

See Property Exclusion for more details.


Property Remapping

Rename properties in output using alias syntax.

users::{displayName: name, userAge: age}
users::{userID: ID, displayName: name}
  1. Rename name to displayName and age to userAge.
  2. Map the ID to userID and name to displayName.

See Property Remappings for more details.


Spread Operator (..)

Include all properties while optionally adding or remapping specific fields.

users::{
    userID: ID,
    ..
}
  1. Include all schema properties plus a computed userID field.

See Property Access for more details.


Nested Mappings (::|name|{})

Closure-style scoped property access for complex output structures.

user::|usr|{
    posts: posts::{
        postID: ID,
        creatorID: usr::ID,
        creatorName: usr::name,
        ..
    }
}
  1. Scope usr as a reference to user, then use it inside nested property selections.

See Property Remappings for more details.


Computed Properties

Add derived values to output using traversals or expressions.

users::{
    userID: ID,
    followerCount: _::In<Follows>::COUNT
}
  1. Add a followerCount computed from the incoming Follows edge count.

See Property Additions for more details.


Modification Operations


::UPDATE

Update properties on existing elements.

N<Person>(user_id)::UPDATE({name: "Alice Johnson", age: 26})
  1. Update the name and age of a person. Omitted properties stay unchanged.

See Updating Items for more details.


DROP

Delete elements and their related edges.

DROP N<User>(user_id)
DROP N<User>(user_id)::Out<Follows>
DROP N<User>(user_id)::OutE<Follows>
  1. Delete a user node and all its connected edges.
  2. Delete all nodes that the user follows (and connecting edges).
  3. Delete only the outgoing Follows edges without touching neighbor nodes.

See Delete Operation for more details.



SearchV

Vector similarity search using cosine similarity.

SearchV<Document>(vector, 10)
SearchV<Document>(Embed(query), limit)
  1. Search for the 10 most similar Document vectors to a raw vector.
  2. Search using an embedded text query with a dynamic limit.

See Vector Search for more details.


SearchBM25

Keyword search using the BM25 ranking algorithm.

SearchBM25<Document>(keywords, 10)
SearchBM25<Document>(query, limit)
  1. Search Document nodes for keywords, returning up to 10 results.
  2. Search with dynamic query text and limit.

See Keyword Search for more details.


Reranking


::RerankRRF

Reciprocal Rank Fusion — combine multiple ranked lists without score calibration.

::RerankRRF
::RerankRRF(k: 60.0)
  1. Apply RRF with the default k=60.
  2. Apply RRF with a custom k parameter.

See RerankRRF for more details.


::RerankMMR

Maximal Marginal Relevance — diversify results by balancing relevance with diversity.

::RerankMMR(lambda: 0.7)
::RerankMMR(lambda: 0.5, distance: "euclidean")
::RerankMMR(lambda: 0.6, distance: "dotproduct")
  1. Apply MMR with cosine distance (default).
  2. Apply MMR with euclidean distance.
  3. Apply MMR with dot product distance.

See RerankMMR for more details.


Shortest Path Algorithms


::ShortestPath

Default shortest path (BFS). Minimizes hop count when no weights are provided.

N<City>(start_id)::ShortestPath<Road>::To(end_id)
  1. Find the shortest unweighted path via Road edges.

See Shortest Path Algorithms for more details.


::ShortestPathBFS

Unweighted shortest path using breadth-first search. Minimizes the number of hops.

N<City>(start_id)::ShortestPathBFS<Road>::To(end_id)
  1. Find the shortest unweighted path from one city to another via Road edges.

See ShortestPathBFS for more details.


::ShortestPathDijkstras

Weighted shortest path using Dijkstra’s algorithm.

N<City>(start_id)::ShortestPathDijkstras<Road>(_::{distance})::To(end_id)
  1. Find the shortest weighted path using the distance edge property as the weight.

See ShortestPathDijkstras for more details.


::ShortestPathAStar

Heuristic shortest path using the A* algorithm.

N<City>(start_id)::ShortestPathAStar<Road>(_::{distance}, "heuristic")::To(end_id)
  1. Find the shortest path using edge distance as weight and a node heuristic property for guidance.

See ShortestPathAStar for more details.


Custom Weight Expressions

Define edge weights for path algorithms using property contexts.

_::{distance}
_::FromN::{population}
_::ToN::{capacity}
ADD(_::{distance}, _::ToN::{traffic})
  1. Use an edge property as weight.
  2. Reference the source node’s property.
  3. Reference the target node’s property.
  4. Combine edge and node properties with arithmetic.

See Custom Weight Calculations for more details.


Control Flow


FOR ... IN

Iterate over collections and perform operations on each element.

FOR user_name IN user_names {
    AddN<User>({name: user_name})
}
  1. Loop through a collection of names and create a node for each element.

See FOR Loops for more details.


FOR with Destructuring

Unpack properties directly in the loop variable declaration.

FOR {name, age, email} IN user_data {
    AddN<User>({name: name, age: age, email: email})
}
  1. Destructure name, age, and email from each element and use them directly.

See FOR Loop Destructuring for more details.


Math Functions


Arithmetic

Basic arithmetic operations.

ADD(price, tax)
SUB(total, discount)
MUL(quantity, unit_price)
DIV(total, count)
POW(base, exponent)
MOD(value, divisor)
  1. Addition: price + tax.
  2. Subtraction: total - discount.
  3. Multiplication: quantity * unit_price.
  4. Division: total / count.
  5. Power: base ^ exponent.
  6. Modulo: value % divisor.

See Arithmetic Functions for more details.


Unary Math

Single-argument mathematical functions.

ABS(value)
SQRT(value)
LN(value)
LOG10(value)
LOG(value, base)
EXP(value)
CEIL(value)
FLOOR(value)
ROUND(value)
  1. Absolute value.
  2. Square root.
  3. Natural logarithm.
  4. Base-10 logarithm.
  5. Logarithm with custom base.
  6. Exponential (e^value).
  7. Round up to nearest integer.
  8. Round down to nearest integer.
  9. Round to nearest integer.

See Unary Math Functions for more details.


Trigonometry

Trigonometric and inverse trigonometric functions (angles in radians).

SIN(angle)
COS(angle)
TAN(angle)
ASIN(value)
ACOS(value)
ATAN(value)
ATAN2(y, x)
  1. Sine of an angle.
  2. Cosine of an angle.
  3. Tangent of an angle.
  4. Inverse sine (arc sine).
  5. Inverse cosine (arc cosine).
  6. Inverse tangent (arc tangent).
  7. Two-argument inverse tangent.

See Trigonometric Functions for more details.


Constants

Mathematical constants.

PI()
E()
  1. Returns the value of pi (~3.14159).
  2. Returns the value of Euler’s number (~2.71828).

See Mathematical Constants for more details.


Aggregate Functions

Aggregate operations on collections.

MIN(ages)
MAX(ages)
SUM(prices)
AVG(scores)
COUNT(users)
  1. Minimum value in a collection.
  2. Maximum value in a collection.
  3. Sum of all values.
  4. Average of all values.
  5. Number of elements.

See Aggregate Functions for more details.


Macros


#[model]

Specify which embedding model to use for Embed() calls within a query.

#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY StoreDocument(content: String) =>
    doc <- AddV<Document>(Embed(content), {content: content})
    RETURN doc
  1. Override the default embedding model for this query using the #[model] macro.

See Model Macro for more details.


#[mcp]

Expose a query as an MCP (Model Context Protocol) endpoint for AI agents.

#[mcp]
QUERY GetDocument(doc_id: ID) =>
    doc <- N<Document>(doc_id)
    RETURN doc
  1. Mark a query as an MCP tool, making it accessible to LLM applications.

See MCP Macro for more details.


Output


Output Values

Return literals, computed values, and structured objects from queries.

RETURN "Success"
RETURN users::{name, age}
RETURN user, posts
RETURN users::!{email}
RETURN NONE
  1. Return a string literal.
  2. Return projected properties.
  3. Return multiple bindings.
  4. Return all properties except email.
  5. Return no payload.

See Output Values for more details.

Schema definition

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

How do I define schemas in HelixDB?

Define the structure of your graph using HelixQL schema definitions. Every node, edge, and vector automatically includes an implicit ID field.

Node schemas

Define node types and their properties:

N::NodeType {
  field1: String,
  field2: U32
}

Edge schemas

Define relationships between nodes with properties:

E::EdgeType {
  From: NodeType,
  To: NodeType,
  Properties: {
    field1: String,
    field2: U32
  }
}

Vector schemas

Define vector types with metadata properties:

V::VectorType {
  field1: String
}

Secondary Indexing

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

You can create secondary indexes on node fields using the INDEX keyword. For enforcing uniqueness constraints, see Unique Indexes.

To create a secondary index, use the INDEX keyword:

N::NodeType {
  INDEX field1: String,
  field2: U32
}

This internally creates a table for the indexed field. To query a node by the indexed field, you can use object syntax:

QUERY getByIndex(index_field: String) =>
    node <- N<User>({field1: index_field})
    RETURN node

Note: You can currently only use indexing with nodes. Indexing for vectors is not supported yet.

Unique Indexes

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Unique indexes allow you to enforce uniqueness constraints on your data, ensuring that no two records share the same value for a given field or edge relationship.

Unique Index on Node Fields

To enforce uniqueness on a node field, use the UNIQUE INDEX keywords before the field name:

N::User {
    UNIQUE INDEX email: String,
    name: String,
    age: U8
}

This creates a unique secondary index on the email field, preventing duplicate email addresses across all User nodes.

Syntax

N::NodeType {
    UNIQUE INDEX field_name: Type,
    UNIQUE INDEX field_name: Type DEFAULT default_value,
}

Combining with Default Values

You can combine unique indexes with default values:

N::Document {
    UNIQUE INDEX slug: String,
    INDEX view_count: I32 DEFAULT 0,
    content: String,
}

Querying by Unique Index

Nodes with unique indexes can be queried using object syntax, just like regular indexes:

QUERY getUserByEmail(email: String) =>
    user <- N<User>({email: email})
    RETURN user

Unique Edges

Unique edges ensure that only one edge of a given type can exist between two specific nodes. This is useful for relationships that should be singular, such as “best friend” or “primary contact”.

Syntax

E::EdgeType UNIQUE {
    From: NodeType,
    To: NodeType,
}

The UNIQUE modifier is placed between the edge name and the edge body.

Example

N::User {
    UNIQUE INDEX username: String,
    name: String,
}

E::BestFriend UNIQUE {
    From: User,
    To: User,
}

With this schema, each user can only have one BestFriend edge pointing to another user. Attempting to create a second BestFriend edge from the same user will enforce the uniqueness constraint.

Unique Edges with Properties

Unique edges can also have properties:

E::PrimaryContact UNIQUE {
    From: Company,
    To: Person,
    Properties: {
        assigned_date: String,
        priority: I32
    }
}

Complete Example

N::Employee {
    UNIQUE INDEX employee_id: String,
    UNIQUE INDEX email: String,
    name: String,
    department: String,
}

N::Project {
    UNIQUE INDEX project_code: String,
    name: String,
}

E::LeadOf UNIQUE {
    From: Employee,
    To: Project,
}

E::WorksOn {
    From: Employee,
    To: Project,
}
QUERY createEmployee(employee_id: String, email: String, name: String, department: String) =>
    emp <- AddN<Employee>({
        employee_id: employee_id,
        email: email,
        name: name,
        department: department
    })
    RETURN emp

QUERY getEmployeeByEmail(email: String) =>
    emp <- N<Employee>({email: email})
    RETURN emp

QUERY assignProjectLead(employee_id: ID, project_id: ID) =>
    lead <- AddE<LeadOf>::From(employee_id)::To(project_id)
    RETURN lead

In this example:

  • Each employee has a unique employee_id and email
  • Each project has a unique project_code
  • Each project can only have one lead (via the UNIQUE edge constraint)
  • Employees can work on multiple projects (non-unique WorksOn edge)

Constraint Violations

When inserting data that violates a unique constraint, HelixDB will return an error. This applies to both unique node field indexes and unique edges.

Node Field Violations

If you attempt to insert a node with a value that already exists for a unique indexed field, the operation will fail:

// First insert succeeds
emp1 <- AddN<Employee>({
    employee_id: "EMP001",
    email: "alice@company.com",
    name: "Alice"
})

// Second insert with same email fails with an error
emp2 <- AddN<Employee>({
    employee_id: "EMP002",
    email: "alice@company.com",  // Duplicate - constraint violation
    name: "Bob"
})

The second operation will return an error indicating that a node with that email already exists.

Edge Violations

Similarly, attempting to create a duplicate unique edge will fail:

// First edge succeeds
lead1 <- AddE<LeadOf>::From(alice_id)::To(project_id)

// Second edge from the same node fails
lead2 <- AddE<LeadOf>::From(alice_id)::To(other_project_id)  // Constraint violation

💡 Tip

Use unique constraints to enforce data integrity at the database level. Handle the returned errors in your application to provide appropriate feedback to users, such as “This email is already registered.”

Default Values

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Sometimes, you may want to set default values for item fields

N::NodeType {
  field1: String DEFAULT "default",
  field2: U32 DEFAULT 0,
  created_at: Date DEFAULT NOW
}

Helix will use the default value if a value is not provided when inserting the item.

Default value types

TypeExampleDescription
StringDEFAULT "value"Default string literal
NumberDEFAULT 0Default numeric value
TimestampDEFAULT NOWAutomatically set to current timestamp on creation

Example

In the example below, the age field has not been provided when inserting the item so the default value of 0 will be used.

QUERY createUser(name: String) =>
    user <- AddN<User>({name}) 
    RETURN user

Create nodes

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

How do I create nodes in HelixDB?

Use AddN to create new nodes in your graph with optional properties.

AddN<Type>
AddN<Type>({properties})

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Adding an empty user node

QUERY CreateUsers () =>
    empty_user <- AddN<User>
    RETURN empty_user
N::User {
    name: String,
    age: U8,
    email: String,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client
client = Client(local=True, port=6969)
print(client.query("CreateUsers"))
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let result: serde_json::Value = client.query("CreateUsers", &json!({})).await?;
    println!("Created empty user: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var result map[string]any
    err := client.Query("CreateUsers").Scan(&result)
    if err != nil {
        log.Fatalf("CreateUsers query failed: %s", err)
    }

    fmt.Printf("Created empty user: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("CreateUsers", {});
    console.log("Created empty user:", result);
}

main().catch((err) => {
    console.error("CreateUsers query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUsers \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Adding a user with parameters

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })

    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client
client = Client(local=True, port=6969)

params = {
    "name": "Alice",
    "age": 25,
    "email": "alice@example.com",
}

print(client.query("CreateUser", params))
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let payload = json!({
        "name": "Alice",
        "age": 25,
        "email": "alice@example.com",
    });

    let result: serde_json::Value = client.query("CreateUser", &payload).await?;
    println!("Created user: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    payload := map[string]any{
        "name":  "Alice",
        "age":   uint8(25),
        "email": "alice@example.com",
    }

    var result map[string]any
    err := client.
        Query("CreateUser", helix.WithData(payload)).
        Scan(&result)
    if err != nil {
        log.Fatalf("CreateUser query failed: %s", err)
    }

    fmt.Printf("Created user: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("CreateUser", {
        name: "Alice",
        age: 25,
        email: "alice@example.com",
    });

    console.log("Created user:", result);
}

main().catch((err) => {
    console.error("CreateUser query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

Example 3: Adding a user with predefined properties

QUERY CreateUser () =>
    predefined_user <- AddN<User>({
        name: "Alice Johnson",
        age: 30,
        email: "alice@example.com"
    })

    RETURN predefined_user
N::User {
    name: String,
    age: U8,
    email: String,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client
client = Client(local=True, port=6969)
print(client.query("CreateUser"))
use helix_rs::{HelixDB, HelixDBClient};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let result: serde_json::Value = client.query("CreateUser", &()).await?;
    println!("Created predefined user: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var result map[string]any
    err := client.Query("CreateUser").Scan(&result)
    if err != nil {
        log.Fatalf("CreateUser query failed: %s", err)
    }

    fmt.Printf("Created predefined user: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("CreateUser", {});
    console.log("Created predefined user:", result);
}

main().catch((err) => {
    console.error("CreateUser query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{}'

Edges

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Create Edges using AddE  

Create connections between nodes in your graph.

AddE<Type>::From(v1)::To(v2)
AddE<Type>({properties})::From(v1)::To(v2)

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Creating a simple follows relationship

QUERY CreateRelationships (user1_id: ID, user2_id: ID) =>
    follows <- AddE<Follows>::From(user1_id)::To(user2_id)
    RETURN follows

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
}

E::Follows {
    From: User,
    To: User,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

alice_id = client.query("CreateUser", {
    "name": "Alice",
    "age": 25,
    "email": "alice@example.com",
})[0]["user"]["id"]

bob_id = client.query("CreateUser", {
    "name": "Bob",
    "age": 28,
    "email": "bob@example.com",
})[0]["user"]["id"]

print(client.query("CreateRelationships", {
    "user1_id": alice_id,
    "user2_id": bob_id,
}))
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "age": 25,
        "email": "alice@example.com",
    })).await?;
    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();

    let bob: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Bob",
        "age": 28,
        "email": "bob@example.com",
    })).await?;
    let bob_id = bob["user"]["id"].as_str().unwrap().to_string();

    let follows: serde_json::Value = client.query("CreateRelationships", &json!({
        "user1_id": alice_id,
        "user2_id": bob_id,
    })).await?;

    println!("Created follows edge: {follows:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    alicePayload := map[string]any{
        "name":  "Alice",
        "age":   uint8(25),
        "email": "alice@example.com",
    }

    var alice map[string]any
    if err := client.Query("CreateUser", helix.WithData(alicePayload)).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }
    aliceUser := alice["user"].(map[string]any)
    aliceID := aliceUser["id"].(string)

    bobPayload := map[string]any{
        "name":  "Bob",
        "age":   uint8(28),
        "email": "bob@example.com",
    }

    var bob map[string]any
    if err := client.Query("CreateUser", helix.WithData(bobPayload)).Scan(&bob); err != nil {
        log.Fatalf("CreateUser (Bob) failed: %s", err)
    }
    bobUser := bob["user"].(map[string]any)
    bobID := bobUser["id"].(string)

    followsPayload := map[string]any{
        "user1_id": aliceID,
        "user2_id": bobID,
    }

    var follows map[string]any
    if err := client.Query("CreateRelationships", helix.WithData(followsPayload)).Scan(&follows); err != nil {
        log.Fatalf("CreateRelationships failed: %s", err)
    }

    fmt.Printf("Created follows edge: %#v\n", follows)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const alice = await client.query("CreateUser", {
        name: "Alice",
        age: 25,
        email: "alice@example.com",
    });
    const aliceId: string = alice.user.id;

    const bob = await client.query("CreateUser", {
        name: "Bob",
        age: 28,
        email: "bob@example.com",
    });
    const bobId: string = bob.user.id;

    const follows = await client.query("CreateRelationships", {
        user1_id: aliceId,
        user2_id: bobId,
    });

    console.log("Created follows edge:", follows);
}

main().catch((err) => {
    console.error("CreateRelationships query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":28,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateRelationships \
  -H 'Content-Type: application/json' \
  -d '{"user1_id":"<alice_id>","user2_id":"<bob_id>"}'

Example 2: Creating a detailed friendship with properties

QUERY CreateFriendship (user1_id: ID, user2_id: ID) =>
    friendship <- AddE<Friends>({
        since: "2024-01-15",
        strength: 0.85
    })::From(user1_id)::To(user2_id)
    RETURN friendship

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
}

E::Friends {
    From: User,
    To: User,
    Properties: {
        since: Date,
        strength: F64
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

user1_id = client.query("CreateUser", {
    "name": "Charlie",
    "age": 31,
    "email": "charlie@example.com",
})[0]["user"]["id"]

user2_id = client.query("CreateUser", {
    "name": "Dana",
    "age": 29,
    "email": "dana@example.com",
})[0]["user"]["id"]

print(client.query("CreateFriendship", {
    "user1_id": user1_id,
    "user2_id": user2_id,
}))
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let charlie: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Charlie",
        "age": 31,
        "email": "charlie@example.com",
    })).await?;
    let charlie_id = charlie["user"]["id"].as_str().unwrap().to_string();

    let dana: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Dana",
        "age": 29,
        "email": "dana@example.com",
    })).await?;
    let dana_id = dana["user"]["id"].as_str().unwrap().to_string();

    let friendship: serde_json::Value = client.query("CreateFriendship", &json!({
        "user1_id": charlie_id,
        "user2_id": dana_id,
    })).await?;

    println!("Created friendship edge: {friendship:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    charliePayload := map[string]any{
        "name":  "Charlie",
        "age":   uint8(31),
        "email": "charlie@example.com",
    }

    var charlie map[string]any
    if err := client.Query("CreateUser", helix.WithData(charliePayload)).Scan(&charlie); err != nil {
        log.Fatalf("CreateUser (Charlie) failed: %s", err)
    }
    charlieID := charlie["user"].(map[string]any)["id"].(string)

    danaPayload := map[string]any{
        "name":  "Dana",
        "age":   uint8(29),
        "email": "dana@example.com",
    }

    var dana map[string]any
    if err := client.Query("CreateUser", helix.WithData(danaPayload)).Scan(&dana); err != nil {
        log.Fatalf("CreateUser (Dana) failed: %s", err)
    }
    danaID := dana["user"].(map[string]any)["id"].(string)

    friendshipPayload := map[string]any{
        "user1_id": charlieID,
        "user2_id": danaID,
    }

    var friendship map[string]any
    if err := client.Query("CreateFriendship", helix.WithData(friendshipPayload)).Scan(&friendship); err != nil {
        log.Fatalf("CreateFriendship failed: %s", err)
    }

    fmt.Printf("Created friendship edge: %#v\n", friendship)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const charlie = await client.query("CreateUser", {
        name: "Charlie",
        age: 31,
        email: "charlie@example.com",
    });
    const charlieId: string = charlie.user.id;

    const dana = await client.query("CreateUser", {
        name: "Dana",
        age: 29,
        email: "dana@example.com",
    });
    const danaId: string = dana.user.id;

    const friendship = await client.query("CreateFriendship", {
        user1_id: charlieId,
        user2_id: danaId,
    });

    console.log("Created friendship edge:", friendship);
}

main().catch((err) => {
    console.error("CreateFriendship query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":31,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Dana","age":29,"email":"dana@example.com"}'

curl -X POST \
  http://localhost:6969/CreateFriendship \
  -H 'Content-Type: application/json' \
  -d '{"user1_id":"<charlie_id>","user2_id":"<dana_id>"}'

Example 3: Traversal Example

QUERY CreateRelationships (user1_id: ID, user2_name: String) =>
    user2 <- N<User>::WHERE(_::{name}::EQ(user2_name))
    follows <- AddE<Follows>::From(user1_id)::To(user2)
    RETURN follows

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
}

E::Follows {
    From: User,
    To: User,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

user1 = client.query("CreateUser", {
    "name": "Eve",
    "age": 33,
    "email": "eve@example.com",
})
user1_id = user1[0]["user"]["id"]

client.query("CreateUser", {
    "name": "Frank",
    "age": 35,
    "email": "frank@example.com",
})

print(client.query("CreateRelationships", {
    "user1_id": user1_id,
    "user2_name": "Frank",
}))
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let eve: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Eve",
        "age": 33,
        "email": "eve@example.com",
    })).await?;
    let eve_id = eve["user"]["id"].as_str().unwrap().to_string();

    client.query::<_, serde_json::Value>("CreateUser", &json!({
        "name": "Frank",
        "age": 35,
        "email": "frank@example.com",
    })).await?;

    let follows: serde_json::Value = client.query("CreateRelationships", &json!({
        "user1_id": eve_id,
        "user2_name": "Frank",
    })).await?;

    println!("Created follows edge via traversal: {follows:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    evePayload := map[string]any{
        "name":  "Eve",
        "age":   uint8(33),
        "email": "eve@example.com",
    }

    var eve map[string]any
    if err := client.Query("CreateUser", helix.WithData(evePayload)).Scan(&eve); err != nil {
        log.Fatalf("CreateUser (Eve) failed: %s", err)
    }
    eveID := eve["user"].(map[string]any)["id"].(string)

    frankPayload := map[string]any{
        "name":  "Frank",
        "age":   uint8(35),
        "email": "frank@example.com",
    }

    if err := client.Query("CreateUser", helix.WithData(frankPayload)).Scan(&map[string]any{}); err != nil {
        log.Fatalf("CreateUser (Frank) failed: %s", err)
    }

    followsPayload := map[string]any{
        "user1_id":  eveID,
        "user2_name": "Frank",
    }

    var follows map[string]any
    if err := client.Query("CreateRelationships", helix.WithData(followsPayload)).Scan(&follows); err != nil {
        log.Fatalf("CreateRelationships failed: %s", err)
    }

    fmt.Printf("Created follows edge via traversal: %#v\n", follows)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const eve = await client.query("CreateUser", {
        name: "Eve",
        age: 33,
        email: "eve@example.com",
    });
    const eveId: string = eve.user.id;

    await client.query("CreateUser", {
        name: "Frank",
        age: 35,
        email: "frank@example.com",
    });

    const follows = await client.query("CreateRelationships", {
        user1_id: eveId,
        user2_name: "Frank",
    });

    console.log("Created follows edge via traversal:", follows);
}

main().catch((err) => {
    console.error("CreateRelationships traversal failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","age":33,"email":"eve@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Frank","age":35,"email":"frank@example.com"}'

curl -X POST \
  http://localhost:6969/CreateRelationships \
  -H 'Content-Type: application/json' \
  -d '{"user1_id":"<eve_id>","user2_name":"Frank"}'

Vectors

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Create Vectors using AddV  

Create new vector embeddings in your graph.

AddV<Type>
AddV<Type>(vector, {properties})

ℹ️ Note

Currently, Helix only supports using an array of F64 values to represent the vector. We will be adding support for different types such as F32, binary vectors and more in the very near future. Please reach out to us if you need a different vector type.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Creating a vector with no properties

QUERY InsertVector (vector: [F64]) =>
    vector_node <- AddV<Document>(vector)
    RETURN vector_node
// You don't need to define the properties in the schema,
//  it uses [F64] by default
V::Document {}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

print(client.query("InsertVector", {
    "vector": [0.1, 0.2, 0.3, 0.4],
}))
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let payload = json!({
        "vector": [0.1, 0.2, 0.3, 0.4],
    });

    let result: serde_json::Value = client.query("InsertVector", &payload).await?;
    println!("Created vector node: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    payload := map[string]any{
        "vector": []float64{0.1, 0.2, 0.3, 0.4},
    }

    var result map[string]any
    if err := client.Query("InsertVector", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("InsertVector failed: %s", err)
    }

    fmt.Printf("Created vector node: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("InsertVector", {
        vector: [0.1, 0.2, 0.3, 0.4],
    });

    console.log("Created vector node:", result);
}

main().catch((err) => {
    console.error("InsertVector query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertVector \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4]}'

Example 2: Creating a vector with properties

QUERY InsertVector (vector: [F64], content: String, created_at: Date) =>
    vector_node <- AddV<Document>(vector, { content: content, created_at: created_at })
    RETURN vector_node
V::Document {
    content: String,
    created_at: Date
}

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

payload = {
    "vector": [0.12, 0.34, 0.56, 0.78],
    "content": "Quick brown fox",
    "created_at": datetime.now(timezone.utc).isoformat(),
}

print(client.query("InsertVector", payload))
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let payload = json!({
        "vector": [0.12, 0.34, 0.56, 0.78],
        "content": "Quick brown fox",
        "created_at": Utc::now().to_rfc3339(),
    });

    let result: serde_json::Value = client.query("InsertVector", &payload).await?;
    println!("Created vector node: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
    "time"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    payload := map[string]any{
        "vector":     []float64{0.12, 0.34, 0.56, 0.78},
        "content":    "Quick brown fox",
        "created_at": time.Now().UTC().Format(time.RFC3339),
    }

    var result map[string]any
    if err := client.Query("InsertVector", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("InsertVector failed: %s", err)
    }

    fmt.Printf("Created vector node: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("InsertVector", {
        vector: [0.12, 0.34, 0.56, 0.78],
        content: "Quick brown fox",
        created_at: new Date().toISOString(),
    });

    console.log("Created vector node:", result);
}

main().catch((err) => {
    console.error("InsertVector query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertVector \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.12,0.34,0.56,0.78],"content":"Quick brown fox","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

Example 3: Creating a vector and connecting it to a node

QUERY InsertVector (user_id: ID, vector: [F64], content: String, created_at: Date) =>
    vector_node <- AddV<Document>(vector, { content: content, created_at: created_at })
    edge <- AddE<User_to_Document_Embedding>::From(user_id)::To(vector_node)
    RETURN "Success"

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
}

V::Document {
    content: String,
    created_at: Date
}

E::User_to_Document_Embedding {
    From: User,
    To: Document,
}

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

user = client.query("CreateUser", {
    "name": "Alice",
    "age": 25,
    "email": "alice@example.com",
})
user_id = user[0]["user"]["id"]

payload = {
    "user_id": user_id,
    "vector": [0.05, 0.25, 0.5, 0.75],
    "content": "Favorite quotes",
    "created_at": datetime.now(timezone.utc).isoformat(),
}

print(client.query("InsertVector", payload))
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "age": 25,
        "email": "alice@example.com",
    })).await?;
    let user_id = alice["user"]["id"].as_str().unwrap().to_string();

    let payload = json!({
        "user_id": user_id,
        "vector": [0.05, 0.25, 0.5, 0.75],
        "content": "Favorite quotes",
        "created_at": Utc::now().to_rfc3339(),
    });

    let result: serde_json::Value = client.query("InsertVector", &payload).await?;
    println!("InsertVector result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    userPayload := map[string]any{
        "name":  "Alice",
        "age":   uint8(25),
        "email": "alice@example.com",
    }

    var user map[string]any
    if err := client.Query("CreateUser", helix.WithData(userPayload)).Scan(&user); err != nil {
        log.Fatalf("CreateUser failed: %s", err)
    }

    userID := user["user"].(map[string]any)["id"].(string)

    payload := map[string]any{
        "user_id":   userID,
        "vector":    []float64{0.05, 0.25, 0.5, 0.75},
        "content":   "Favorite quotes",
        "created_at": time.Now().UTC().Format(time.RFC3339),
    }

    var result map[string]any
    if err := client.Query("InsertVector", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("InsertVector failed: %s", err)
    }

    fmt.Printf("InsertVector result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const user = await client.query("CreateUser", {
        name: "Alice",
        age: 25,
        email: "alice@example.com",
    });
    const userId: string = user.user.id;

    const result = await client.query("InsertVector", {
        user_id: userId,
        vector: [0.05, 0.25, 0.5, 0.75],
        content: "Favorite quotes",
        created_at: new Date().toISOString(),
    });

    console.log("InsertVector result:", result);
}

main().catch((err) => {
    console.error("InsertVector query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/InsertVector \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<user_id>","vector":[0.05,0.25,0.5,0.75],"content":"Favorite quotes","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

Example 4: Using the built in Embed function

You can also use the built in Embed function to embed the text without sending in the array of floats. It uses the embedding model defined in your config.hx.json file.

⚠️ Warning

All vectors in a vector type must have the same dimensions. If you change your embedding model (e.g., switching from text-embedding-ada-002 to a different model), the new vectors will have different dimensions and will cause an error. Ensure you use the same embedding model consistently for all vectors.

QUERY InsertVector (content: String, created_at: Date) =>
    vector_node <- AddV<Document>(Embed(content), { content: content, created_at: created_at })
    RETURN vector_node
V::Document {
    content: String,
    created_at: Date
}
OPENAI_API_KEY=your_api_key

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

payload = {
    "content": "Quick summary of a meeting",
    "created_at": datetime.now(timezone.utc).isoformat(),
}

print(client.query("InsertVector", payload))
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let payload = json!({
        "content": "Quick summary of a meeting",
        "created_at": Utc::now().to_rfc3339(),
    });

    let result: serde_json::Value = client.query("InsertVector", &payload).await?;
    println!("InsertVector result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    payload := map[string]any{
        "content":   "Quick summary of a meeting",
        "created_at": time.Now().UTC().Format(time.RFC3339),
    }

    var result map[string]any
    if err := client.Query("InsertVector", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("InsertVector failed: %s", err)
    }

    fmt.Printf("InsertVector result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("InsertVector", {
        content: "Quick summary of a meeting",
        created_at: new Date().toISOString(),
    });

    console.log("InsertVector result:", result);
}

main().catch((err) => {
    console.error("InsertVector query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Quick summary of a meeting","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

Upsert Nodes

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Upsert Nodes using UpsertN  

Create new nodes or update existing ones with insert-or-update semantics.

::UpsertN({properties})

ℹ️ Info

UpsertN is a traversal step that operates on an existing traversal context. The type comes from the preceding traversal (N<Type>), not from the upsert call itself. If the traversal returns existing nodes, they are updated; if no nodes are found, a new node is created.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Basic node upsert with properties

QUERY UpsertPerson(name: String, age: U32) =>
    existing <- N<Person>::WHERE(_::{name}::EQ(name))
    person <- existing::UpsertN({name: name, age: age})
    RETURN person
N::Person {
    INDEX name: String,
    age: U32,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# First call creates a new person
person = client.query("UpsertPerson", {
    "name": "Alice",
    "age": 25,
})

print("Created person:", person)

# Subsequent calls with same data update the existing person
updated = client.query("UpsertPerson", {
    "name": "Alice",
    "age": 26,
})

print("Updated person:", updated)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // First call creates a new person
    let person: serde_json::Value = client.query("UpsertPerson", &json!({
        "name": "Alice",
        "age": 25,
    })).await?;

    println!("Created person: {person:#?}");

    // Subsequent calls update the existing person
    let updated: serde_json::Value = client.query("UpsertPerson", &json!({
        "name": "Alice",
        "age": 26,
    })).await?;

    println!("Updated person: {updated:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // First call creates a new person
    payload := map[string]any{
        "name": "Alice",
        "age":  uint32(25),
    }

    var person map[string]any
    if err := client.Query("UpsertPerson", helix.WithData(payload)).Scan(&person); err != nil {
        log.Fatalf("UpsertPerson failed: %s", err)
    }

    fmt.Printf("Created person: %#v\n", person)

    // Subsequent calls update the existing person
    payload["age"] = uint32(26)

    var updated map[string]any
    if err := client.Query("UpsertPerson", helix.WithData(payload)).Scan(&updated); err != nil {
        log.Fatalf("UpsertPerson failed: %s", err)
    }

    fmt.Printf("Updated person: %#v\n", updated)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // First call creates a new person
    const person = await client.query("UpsertPerson", {
        name: "Alice",
        age: 25,
    });

    console.log("Created person:", person);

    // Subsequent calls update the existing person
    const updated = await client.query("UpsertPerson", {
        name: "Alice",
        age: 26,
    });

    console.log("Updated person:", updated);
}

main().catch((err) => {
    console.error("UpsertPerson query failed:", err);
});
# First call creates a new person
curl -X POST \
  http://localhost:6969/UpsertPerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25}'

# Subsequent calls update the existing person
curl -X POST \
  http://localhost:6969/UpsertPerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":26}'

Example 2: Upsert from a pre-fetched node by ID

When you already have a node ID, you can fetch it directly and apply the upsert.

QUERY UpsertPersonById(id: ID, new_age: U32) =>
    existing <- N<Person>(id)
    person <- existing::UpsertN({age: new_age})
    RETURN person
N::Person {
    INDEX name: String,
    age: U32,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)
result = client.query("UpsertPersonById", {
    "id": "<person_id>",
    "new_age": 30,
})
print("Upserted person:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let result: serde_json::Value = client.query("UpsertPersonById", &json!({
        "id": "<person_id>",
        "new_age": 30,
    })).await?;
    println!("Upserted person: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    payload := map[string]any{
        "id":      "<person_id>",
        "new_age": uint32(30),
    }

    var result map[string]any
    if err := client.Query("UpsertPersonById", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("UpsertPersonById failed: %s", err)
    }

    fmt.Printf("Upserted person: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("UpsertPersonById", {
        id: "<person_id>",
        new_age: 30,
    });
    console.log("Upserted person:", result);
}

main().catch((err) => {
    console.error("UpsertPersonById query failed:", err);
});
curl -X POST \
  http://localhost:6969/UpsertPersonById \
  -H 'Content-Type: application/json' \
  -d '{"id":"<person_id>","new_age":30}'

Example 3: Upsert with WHERE filter

Find existing nodes with a WHERE clause and update or create if not found.

QUERY UpdateOrCreatePerson(name: String, new_age: U32) =>
    existing <- N<Person>::WHERE(_::{name}::EQ(name))
    person <- existing::UpsertN({name: name, age: new_age})
    RETURN person
N::Person {
    INDEX name: String,
    age: U32,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# If "Alice" exists, updates her age; otherwise creates a new person
result = client.query("UpdateOrCreatePerson", {
    "name": "Alice",
    "new_age": 30,
})

print("Upserted person:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // If "Alice" exists, updates her age; otherwise creates a new person
    let result: serde_json::Value = client.query("UpdateOrCreatePerson", &json!({
        "name": "Alice",
        "new_age": 30,
    })).await?;

    println!("Upserted person: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // If "Alice" exists, updates her age; otherwise creates a new person
    payload := map[string]any{
        "name":    "Alice",
        "new_age": uint32(30),
    }

    var result map[string]any
    if err := client.Query("UpdateOrCreatePerson", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("UpdateOrCreatePerson failed: %s", err)
    }

    fmt.Printf("Upserted person: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // If "Alice" exists, updates her age; otherwise creates a new person
    const result = await client.query("UpdateOrCreatePerson", {
        name: "Alice",
        new_age: 30,
    });

    console.log("Upserted person:", result);
}

main().catch((err) => {
    console.error("UpdateOrCreatePerson query failed:", err);
});
curl -X POST \
  http://localhost:6969/UpdateOrCreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","new_age":30}'

How Upsert differs from Add and Update

OperationBehavior
AddNAlways creates a new node
UPDATEOnly modifies existing nodes (fails if node doesn’t exist)
UpsertNOperates on traversal context: updates if nodes found, creates if empty

💡 Tip

When updating, UpsertN merges properties: it updates specified properties while preserving any existing properties that aren’t included in the upsert.

Upsert Edges

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Upsert Edges using UpsertE  

Create new edges or update existing ones with insert-or-update semantics.

UpsertE({properties})::From(node1)::To(node2)

ℹ️ Info

UpsertE is a traversal step that operates on an existing traversal context. The type comes from the preceding traversal (E<Type>), not from the upsert call itself. If an edge already exists between the specified nodes, it is updated; if no edge is found, a new edge is created.

⚠️ Warning

Edge connections (::From() and ::To()) are required for UpsertE. The order of From and To is flexible. Properties are also required.

Example 1: Basic edge upsert with properties

QUERY UpsertFriendship(id1: ID, id2: ID, since: String) =>
    person1 <- N<Person>(id1)
    person2 <- N<Person>(id2)
    existing <- E<Knows>
    edge <- existing::UpsertE({since: since})::From(person1)::To(person2)
    RETURN edge

QUERY CreatePerson(name: String, age: U32) =>
    person <- AddN<Person>({name: name, age: age})
    RETURN person
N::Person {
    INDEX name: String,
    age: U32,
}

E::Knows {
    From: Person,
    To: Person,
    Properties: {
        since: String,
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create two people
alice = client.query("CreatePerson", {
    "name": "Alice",
    "age": 25,
})[0]["person"]

bob = client.query("CreatePerson", {
    "name": "Bob",
    "age": 28,
})[0]["person"]

# First call creates the edge
result = client.query("UpsertFriendship", {
    "id1": alice["id"],
    "id2": bob["id"],
    "since": "2024-01-15",
})

print("Created edge:", result)

# Subsequent calls update the existing edge (no duplicate created)
result = client.query("UpsertFriendship", {
    "id1": alice["id"],
    "id2": bob["id"],
    "since": "2024-06-20",
})

print("Upserted edge:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create two people
    let alice: serde_json::Value = client.query("CreatePerson", &json!({
        "name": "Alice",
        "age": 25,
    })).await?;
    let alice_id = alice["person"]["id"].as_str().unwrap().to_string();

    let bob: serde_json::Value = client.query("CreatePerson", &json!({
        "name": "Bob",
        "age": 28,
    })).await?;
    let bob_id = bob["person"]["id"].as_str().unwrap().to_string();

    // First call creates the edge
    let result: serde_json::Value = client.query("UpsertFriendship", &json!({
        "id1": alice_id,
        "id2": bob_id,
        "since": "2024-01-15",
    })).await?;

    println!("Created edge: {result:#?}");

    // Subsequent calls update the existing edge
    let result: serde_json::Value = client.query("UpsertFriendship", &json!({
        "id1": alice_id,
        "id2": bob_id,
        "since": "2024-06-20",
    })).await?;

    println!("Upserted edge: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create two people
    var alice map[string]any
    if err := client.Query("CreatePerson", helix.WithData(map[string]any{
        "name": "Alice",
        "age":  uint32(25),
    })).Scan(&alice); err != nil {
        log.Fatalf("CreatePerson (Alice) failed: %s", err)
    }
    aliceID := alice["person"].(map[string]any)["id"].(string)

    var bob map[string]any
    if err := client.Query("CreatePerson", helix.WithData(map[string]any{
        "name": "Bob",
        "age":  uint32(28),
    })).Scan(&bob); err != nil {
        log.Fatalf("CreatePerson (Bob) failed: %s", err)
    }
    bobID := bob["person"].(map[string]any)["id"].(string)

    // First call creates the edge
    var result map[string]any
    if err := client.Query("UpsertFriendship", helix.WithData(map[string]any{
        "id1":   aliceID,
        "id2":   bobID,
        "since": "2024-01-15",
    })).Scan(&result); err != nil {
        log.Fatalf("UpsertFriendship failed: %s", err)
    }

    fmt.Printf("Created edge: %#v\n", result)

    // Subsequent calls update the existing edge
    if err := client.Query("UpsertFriendship", helix.WithData(map[string]any{
        "id1":   aliceID,
        "id2":   bobID,
        "since": "2024-06-20",
    })).Scan(&result); err != nil {
        log.Fatalf("UpsertFriendship failed: %s", err)
    }

    fmt.Printf("Upserted edge: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create two people
    const alice = await client.query("CreatePerson", {
        name: "Alice",
        age: 25,
    });
    const aliceId: string = alice.person.id;

    const bob = await client.query("CreatePerson", {
        name: "Bob",
        age: 28,
    });
    const bobId: string = bob.person.id;

    // First call creates the edge
    let result = await client.query("UpsertFriendship", {
        id1: aliceId,
        id2: bobId,
        since: "2024-01-15",
    });

    console.log("Created edge:", result);

    // Subsequent calls update the existing edge
    result = await client.query("UpsertFriendship", {
        id1: aliceId,
        id2: bobId,
        since: "2024-06-20",
    });

    console.log("Upserted edge:", result);
}

main().catch((err) => {
    console.error("UpsertFriendship query failed:", err);
});
# Create two people first
curl -X POST \
  http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25}'

curl -X POST \
  http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":28}'

# Create or update the edge
curl -X POST \
  http://localhost:6969/UpsertFriendship \
  -H 'Content-Type: application/json' \
  -d '{"id1":"<alice_id>","id2":"<bob_id>","since":"2024-01-15"}'

Example 2: Upsert edge with multiple properties

QUERY UpsertFriendshipWithProps(id1: ID, id2: ID, since: String, strength: F32) =>
    person1 <- N<Person>(id1)
    person2 <- N<Person>(id2)
    existing <- E<Friendship>
    edge <- existing::UpsertE({since: since, strength: strength})::From(person1)::To(person2)
    RETURN edge

QUERY CreatePerson(name: String, age: U32) =>
    person <- AddN<Person>({name: name, age: age})
    RETURN person
N::Person {
    INDEX name: String,
    age: U32,
}

E::Friendship {
    From: Person,
    To: Person,
    Properties: {
        since: String,
        strength: F32,
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

alice = client.query("CreatePerson", {
    "name": "Alice",
    "age": 25,
})[0]["person"]

bob = client.query("CreatePerson", {
    "name": "Bob",
    "age": 28,
})[0]["person"]

# Create friendship with properties
result = client.query("UpsertFriendshipWithProps", {
    "id1": alice["id"],
    "id2": bob["id"],
    "since": "2024-01-15",
    "strength": 0.85,
})

print("Created friendship:", result)

# Update the friendship strength
result = client.query("UpsertFriendshipWithProps", {
    "id1": alice["id"],
    "id2": bob["id"],
    "since": "2024-01-15",
    "strength": 0.95,
})

print("Updated friendship:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let alice: serde_json::Value = client.query("CreatePerson", &json!({
        "name": "Alice",
        "age": 25,
    })).await?;
    let alice_id = alice["person"]["id"].as_str().unwrap().to_string();

    let bob: serde_json::Value = client.query("CreatePerson", &json!({
        "name": "Bob",
        "age": 28,
    })).await?;
    let bob_id = bob["person"]["id"].as_str().unwrap().to_string();

    // Create friendship with properties
    let result: serde_json::Value = client.query("UpsertFriendshipWithProps", &json!({
        "id1": alice_id,
        "id2": bob_id,
        "since": "2024-01-15",
        "strength": 0.85,
    })).await?;

    println!("Created friendship: {result:#?}");

    // Update the friendship strength
    let result: serde_json::Value = client.query("UpsertFriendshipWithProps", &json!({
        "id1": alice_id,
        "id2": bob_id,
        "since": "2024-01-15",
        "strength": 0.95,
    })).await?;

    println!("Updated friendship: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var alice map[string]any
    if err := client.Query("CreatePerson", helix.WithData(map[string]any{
        "name": "Alice",
        "age":  uint32(25),
    })).Scan(&alice); err != nil {
        log.Fatalf("CreatePerson (Alice) failed: %s", err)
    }
    aliceID := alice["person"].(map[string]any)["id"].(string)

    var bob map[string]any
    if err := client.Query("CreatePerson", helix.WithData(map[string]any{
        "name": "Bob",
        "age":  uint32(28),
    })).Scan(&bob); err != nil {
        log.Fatalf("CreatePerson (Bob) failed: %s", err)
    }
    bobID := bob["person"].(map[string]any)["id"].(string)

    // Create friendship with properties
    var result map[string]any
    if err := client.Query("UpsertFriendshipWithProps", helix.WithData(map[string]any{
        "id1":      aliceID,
        "id2":      bobID,
        "since":    "2024-01-15",
        "strength": float32(0.85),
    })).Scan(&result); err != nil {
        log.Fatalf("UpsertFriendshipWithProps failed: %s", err)
    }

    fmt.Printf("Created friendship: %#v\n", result)

    // Update the friendship strength
    if err := client.Query("UpsertFriendshipWithProps", helix.WithData(map[string]any{
        "id1":      aliceID,
        "id2":      bobID,
        "since":    "2024-01-15",
        "strength": float32(0.95),
    })).Scan(&result); err != nil {
        log.Fatalf("UpsertFriendshipWithProps failed: %s", err)
    }

    fmt.Printf("Updated friendship: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const alice = await client.query("CreatePerson", {
        name: "Alice",
        age: 25,
    });
    const aliceId: string = alice.person.id;

    const bob = await client.query("CreatePerson", {
        name: "Bob",
        age: 28,
    });
    const bobId: string = bob.person.id;

    // Create friendship with properties
    let result = await client.query("UpsertFriendshipWithProps", {
        id1: aliceId,
        id2: bobId,
        since: "2024-01-15",
        strength: 0.85,
    });

    console.log("Created friendship:", result);

    // Update the friendship strength
    result = await client.query("UpsertFriendshipWithProps", {
        id1: aliceId,
        id2: bobId,
        since: "2024-01-15",
        strength: 0.95,
    });

    console.log("Updated friendship:", result);
}

main().catch((err) => {
    console.error("UpsertFriendshipWithProps query failed:", err);
});
# Create two people first
curl -X POST \
  http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25}'

curl -X POST \
  http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":28}'

# Create friendship with properties
curl -X POST \
  http://localhost:6969/UpsertFriendshipWithProps \
  -H 'Content-Type: application/json' \
  -d '{"id1":"<alice_id>","id2":"<bob_id>","since":"2024-01-15","strength":0.85}'

# Update the friendship strength
curl -X POST \
  http://localhost:6969/UpsertFriendshipWithProps \
  -H 'Content-Type: application/json' \
  -d '{"id1":"<alice_id>","id2":"<bob_id>","since":"2024-01-15","strength":0.95}'

Example 3: Flexible From/To ordering

The ::From() and ::To() can be specified in either order.

QUERY UpsertEdgeToFrom(id1: ID, id2: ID, since: String) =>
    person1 <- N<Person>(id1)
    person2 <- N<Person>(id2)
    existing <- E<Knows>
    // To and From can be in either order
    edge <- existing::UpsertE({since: since})::To(person2)::From(person1)
    RETURN edge
N::Person {
    INDEX name: String,
    age: U32,
}

E::Knows {
    From: Person,
    To: Person,
    Properties: {
        since: String,
    }
}

How Upsert differs from Add

OperationBehavior
AddEAlways creates a new edge (can create duplicates)
UpsertEOperates on traversal context: updates if edge exists between nodes, creates if not

💡 Tip

When updating, UpsertE merges properties: it updates specified properties while preserving any existing properties that aren’t included in the upsert.

Upsert Vectors

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Upsert Vectors using UpsertV  

Create new vector embeddings or update existing ones with insert-or-update semantics.

::UpsertV(vector, {properties})

ℹ️ Info

UpsertV is a traversal step that operates on an existing traversal context. The type comes from the preceding traversal (V<Type>), not from the upsert call itself. If the traversal returns existing vectors, they are updated; if no vectors are found, a new vector is created.

⚠️ Warning

Vector data is required for UpsertV. You can provide vector data as:

  • A literal array of floats (e.g., [0.1, 0.2, 0.3])
  • The Embed() function to generate embeddings from text
  • A variable containing vector data

Example 1: Basic vector upsert with properties

QUERY UpsertDoc(vector: [F64], content: String) =>
    existing <- V<Document>::WHERE(_::{content}::EQ(content))
    doc <- existing::UpsertV(vector, {content: content})
    RETURN doc
V::Document {
    content: String,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# First call creates a new document
doc = client.query("UpsertDoc", {
    "vector": [0.1, 0.2, 0.3, 0.4],
    "content": "Introduction to machine learning",
})

print("Created document:", doc)

# Subsequent calls with same vector update the existing document
updated = client.query("UpsertDoc", {
    "vector": [0.1, 0.2, 0.3, 0.4],
    "content": "Updated introduction to machine learning",
})

print("Updated document:", updated)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // First call creates a new document
    let doc: serde_json::Value = client.query("UpsertDoc", &json!({
        "vector": [0.1, 0.2, 0.3, 0.4],
        "content": "Introduction to machine learning",
    })).await?;

    println!("Created document: {doc:#?}");

    // Subsequent calls update the existing document
    let updated: serde_json::Value = client.query("UpsertDoc", &json!({
        "vector": [0.1, 0.2, 0.3, 0.4],
        "content": "Updated introduction to machine learning",
    })).await?;

    println!("Updated document: {updated:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // First call creates a new document
    payload := map[string]any{
        "vector":  []float32{0.1, 0.2, 0.3, 0.4},
        "content": "Introduction to machine learning",
    }

    var doc map[string]any
    if err := client.Query("UpsertDoc", helix.WithData(payload)).Scan(&doc); err != nil {
        log.Fatalf("UpsertDoc failed: %s", err)
    }

    fmt.Printf("Created document: %#v\n", doc)

    // Subsequent calls update the existing document
    payload["content"] = "Updated introduction to machine learning"

    var updated map[string]any
    if err := client.Query("UpsertDoc", helix.WithData(payload)).Scan(&updated); err != nil {
        log.Fatalf("UpsertDoc failed: %s", err)
    }

    fmt.Printf("Updated document: %#v\n", updated)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // First call creates a new document
    const doc = await client.query("UpsertDoc", {
        vector: [0.1, 0.2, 0.3, 0.4],
        content: "Introduction to machine learning",
    });

    console.log("Created document:", doc);

    // Subsequent calls update the existing document
    const updated = await client.query("UpsertDoc", {
        vector: [0.1, 0.2, 0.3, 0.4],
        content: "Updated introduction to machine learning",
    });

    console.log("Updated document:", updated);
}

main().catch((err) => {
    console.error("UpsertDoc query failed:", err);
});
# First call creates a new document
curl -X POST \
  http://localhost:6969/UpsertDoc \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"content":"Introduction to machine learning"}'

# Subsequent calls update the existing document
curl -X POST \
  http://localhost:6969/UpsertDoc \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"content":"Updated introduction to machine learning"}'

Example 2: Upsert vector using the Embed function

You can use the built-in Embed function to generate embeddings from text.

⚠️ Warning

All vectors in a vector type must have the same dimensions. If you change your embedding model, the new vectors will have different dimensions and will cause an error. Ensure you use the same embedding model consistently for all vectors.

QUERY UpsertDocEmbed(text: String) =>
    existing <- V<Document>::WHERE(_::{content}::EQ(text))
    doc <- existing::UpsertV(Embed(text), {content: text})
    RETURN doc
V::Document {
    content: String,
}
OPENAI_API_KEY=your_api_key

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Upsert with automatic embedding generation
doc = client.query("UpsertDocEmbed", {
    "text": "Introduction to machine learning",
})

print("Upserted document:", doc)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Upsert with automatic embedding generation
    let doc: serde_json::Value = client.query("UpsertDocEmbed", &json!({
        "text": "Introduction to machine learning",
    })).await?;

    println!("Upserted document: {doc:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Upsert with automatic embedding generation
    payload := map[string]any{
        "text": "Introduction to machine learning",
    }

    var doc map[string]any
    if err := client.Query("UpsertDocEmbed", helix.WithData(payload)).Scan(&doc); err != nil {
        log.Fatalf("UpsertDocEmbed failed: %s", err)
    }

    fmt.Printf("Upserted document: %#v\n", doc)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Upsert with automatic embedding generation
    const doc = await client.query("UpsertDocEmbed", {
        text: "Introduction to machine learning",
    });

    console.log("Upserted document:", doc);
}

main().catch((err) => {
    console.error("UpsertDocEmbed query failed:", err);
});
curl -X POST \
  http://localhost:6969/UpsertDocEmbed \
  -H 'Content-Type: application/json' \
  -d '{"text":"Introduction to machine learning"}'

Example 3: Complex operation with nodes, edges, and vectors

QUERY ComplexUpsertOperation(
    person_name: String,
    person_age: U32,
    company_name: String,
    position: String,
    resume_content: String
) =>
    existing_person <- N<Person>::WHERE(_::{name}::EQ(person_name))
    person <- existing_person::UpsertN({name: person_name, age: person_age})
    existing_company <- N<Company>::WHERE(_::{name}::EQ(company_name))
    company <- existing_company::UpsertN({name: company_name})
    existing_edge <- E<WorksAt>
    edge <- existing_edge::UpsertE({position: position})::From(person)::To(company)
    existing_resume <- V<Resume>::WHERE(_::{content}::EQ(resume_content))
    resume <- existing_resume::UpsertV(Embed(resume_content), {content: resume_content})
    RETURN person

QUERY GetPerson(name: String) =>
    person <- N<Person>::WHERE(_::{name}::EQ(name))
    RETURN person
N::Person {
    INDEX name: String,
    age: U32,
}

N::Company {
    INDEX name: String,
}

V::Resume {
    content: String,
}

E::WorksAt {
    From: Person,
    To: Company,
    Properties: {
        position: String,
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create or update person, company, employment, and resume all at once
result = client.query("ComplexUpsertOperation", {
    "person_name": "Alice",
    "person_age": 30,
    "company_name": "TechCorp",
    "position": "Software Engineer",
    "resume_content": "Experienced software engineer with 5 years...",
})

print("Upserted person:", result)

# Running again will update existing records instead of creating duplicates
result = client.query("ComplexUpsertOperation", {
    "person_name": "Alice",
    "person_age": 31,  # Updated age
    "company_name": "TechCorp",
    "position": "Senior Software Engineer",  # Updated position
    "resume_content": "Experienced senior software engineer with 6 years...",
})

print("Updated person:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create or update person, company, employment, and resume all at once
    let result: serde_json::Value = client.query("ComplexUpsertOperation", &json!({
        "person_name": "Alice",
        "person_age": 30,
        "company_name": "TechCorp",
        "position": "Software Engineer",
        "resume_content": "Experienced software engineer with 5 years...",
    })).await?;

    println!("Upserted person: {result:#?}");

    // Running again will update existing records instead of creating duplicates
    let result: serde_json::Value = client.query("ComplexUpsertOperation", &json!({
        "person_name": "Alice",
        "person_age": 31,
        "company_name": "TechCorp",
        "position": "Senior Software Engineer",
        "resume_content": "Experienced senior software engineer with 6 years...",
    })).await?;

    println!("Updated person: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create or update person, company, employment, and resume all at once
    payload := map[string]any{
        "person_name":    "Alice",
        "person_age":     uint32(30),
        "company_name":   "TechCorp",
        "position":       "Software Engineer",
        "resume_content": "Experienced software engineer with 5 years...",
    }

    var result map[string]any
    if err := client.Query("ComplexUpsertOperation", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("ComplexUpsertOperation failed: %s", err)
    }

    fmt.Printf("Upserted person: %#v\n", result)

    // Running again will update existing records instead of creating duplicates
    payload["person_age"] = uint32(31)
    payload["position"] = "Senior Software Engineer"
    payload["resume_content"] = "Experienced senior software engineer with 6 years..."

    if err := client.Query("ComplexUpsertOperation", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("ComplexUpsertOperation failed: %s", err)
    }

    fmt.Printf("Updated person: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create or update person, company, employment, and resume all at once
    let result = await client.query("ComplexUpsertOperation", {
        person_name: "Alice",
        person_age: 30,
        company_name: "TechCorp",
        position: "Software Engineer",
        resume_content: "Experienced software engineer with 5 years...",
    });

    console.log("Upserted person:", result);

    // Running again will update existing records instead of creating duplicates
    result = await client.query("ComplexUpsertOperation", {
        person_name: "Alice",
        person_age: 31,
        company_name: "TechCorp",
        position: "Senior Software Engineer",
        resume_content: "Experienced senior software engineer with 6 years...",
    });

    console.log("Updated person:", result);
}

main().catch((err) => {
    console.error("ComplexUpsertOperation query failed:", err);
});
# Create or update person, company, employment, and resume all at once
curl -X POST \
  http://localhost:6969/ComplexUpsertOperation \
  -H 'Content-Type: application/json' \
  -d '{
    "person_name": "Alice",
    "person_age": 30,
    "company_name": "TechCorp",
    "position": "Software Engineer",
    "resume_content": "Experienced software engineer with 5 years..."
  }'

# Running again will update existing records
curl -X POST \
  http://localhost:6969/ComplexUpsertOperation \
  -H 'Content-Type: application/json' \
  -d '{
    "person_name": "Alice",
    "person_age": 31,
    "company_name": "TechCorp",
    "position": "Senior Software Engineer",
    "resume_content": "Experienced senior software engineer with 6 years..."
  }'

How Upsert differs from Add

OperationBehavior
AddVAlways creates a new vector
UpsertVOperates on traversal context: updates if vectors found, creates if empty

💡 Tip

When updating, UpsertV merges properties: it updates specified properties while preserving any existing properties that aren’t included in the upsert. The vector data itself is also updated.

Updating Items

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Update Nodes using UPDATE  

Modify properties on existing elements.

::UPDATE({<properties_list>})

Example 1: Updating a person’s profile

QUERY UpdateUser(user_id: ID, new_name: String, new_age: U32) =>
    updated <- N<Person>(user_id)::UPDATE({
        name: new_name,
        age: new_age
    })
    RETURN updated

QUERY CreatePerson(name: String, age: U32) =>
    person <- AddN<Person>({
        name: name,
        age: age,
    })
    RETURN person
N::Person {
    name: String,
    age: U32,
}

ℹ️ Info

You only need to include the fields you want to change. Any omitted properties stay the same.

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

alice = client.query("CreatePerson", {
    "name": "Alice",
    "age": 25,
})[0]["person"]

updated = client.query("UpdateUser", {
    "user_id": alice["id"],
    "new_name": "Alice Johnson",
    "new_age": 26,
})[0]["updated"]

print("Updated user:", updated)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let result: serde_json::Value = client.query("CreatePerson", &json!({
        "name": "Alice",
        "age": 25,
    })).await?;
    let alice_id = result["person"]["id"].as_str().unwrap().to_string();

    let updated: serde_json::Value = client.query("UpdateUser", &json!({
        "user_id": alice_id,
        "new_name": "Alice Johnson",
        "new_age": 26,
    })).await?;

    println!("Updated user: {updated:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    createPayload := map[string]any{
        "name": "Alice",
        "age":  uint32(25),
    }

    var createResp map[string]any
    if err := client.Query("CreatePerson", helix.WithData(createPayload)).Scan(&createResp); err != nil {
        log.Fatalf("CreatePerson failed: %s", err)
    }

    updatePayload := map[string]any{
        "user_id":  createResp["person"].(map[string]any)["id"],
        "new_name": "Alice Johnson",
        "new_age":  uint32(26),
    }

    var updated map[string]any
    if err := client.Query("UpdateUser", helix.WithData(updatePayload)).Scan(&updated); err != nil {
        log.Fatalf("UpdateUser failed: %s", err)
    }

    fmt.Printf("Updated user: %#v\n", updated)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const createResp = await client.query("CreatePerson", {
        name: "Alice",
        age: 25,
    });

    const updated = await client.query("UpdateUser", {
        user_id: createResp.person.id,
        new_name: "Alice Johnson",
        new_age: 26,
    });

    console.log("Updated user:", updated);
}

main().catch((err) => {
    console.error("UpdateUser failed:", err);
});
curl -X POST \
  http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25}'

curl -X POST \
  http://localhost:6969/UpdateUser \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<person_id>","new_name":"Alice Johnson","new_age":26}'

🚨 Danger

The following examples would not work as the types don’t match

QUERY UpdateUser(userID: ID) =>
    // No email field in Person node
    updatedUsers <- N<Person>(userID)::UPDATE({ email: "john@example.com" })

    // Age as string instead of U32
    updatedUsers <- N<Person>(userID)::UPDATE({ age: "Hello" })

    RETURN updatedUsers

Delete Operation

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

DROP

Drops elements from your database. The DROP effects the elements returned by the traversal.

DROP <traversal>

You can use the drop on any traversal that returns a list of elements. DROP will do nothing if the result of the expression is not a list of elements or if the list is empty.

Example 1: Removing a user node by ID

Dropping a node will also remove all related edges that are connected to it.

QUERY DeleteUserNode (user_id: ID) =>
    DROP N<User>(user_id)
    RETURN "Removed user node"

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({ name: name, age: age, email: email })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
}

E::Follows {
    From: User,
    To: User,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

yara = client.query("CreateUser", {
    "name": "Yara",
    "age": 24,
    "email": "yara@example.com",
})
yara_id = yara[0]["user"]["id"]

result = client.query("DeleteUserNode", {"user_id": yara_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let yara: serde_json::Value = client
        .query(
            "CreateUser",
            &json!({
                "name": "Yara",
                "age": 24,
                "email": "yara@example.com",
            }),
        )
        .await?;

    let yara_id = yara["user"]["id"].as_str().unwrap().to_string();

    let result: serde_json::Value = client
        .query(
            "DeleteUserNode",
            &json!({
                "user_id": yara_id,
            }),
        )
        .await?;

    println!("DeleteUserNode result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    yaraPayload := map[string]any{
        "name":  "Yara",
        "age":   uint8(24),
        "email": "yara@example.com",
    }
    var yara map[string]any
    if err := client.Query("CreateUser", helix.WithData(yaraPayload)).Scan(&yara); err != nil {
        log.Fatalf("CreateUser (Yara) failed: %s", err)
    }
    yaraID := yara["user"].(map[string]any)["id"].(string)

    var result map[string]any
    if err := client.Query("DeleteUserNode", helix.WithData(map[string]any{
        "user_id": yaraID,
    })).Scan(&result); err != nil {
        log.Fatalf("DeleteUserNode failed: %s", err)
    }

    fmt.Printf("DeleteUserNode result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const yara = await client.query("CreateUser", {
        name: "Yara",
        age: 24,
        email: "yara@example.com",
    });
    const yaraId: string = yara.user.id;

    const result = await client.query("DeleteUserNode", {
        user_id: yaraId,
    });

    console.log("DeleteUserNode result:", result);
}

main().catch((err) => {
    console.error("DeleteUserNode query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Yara\",\"age\":24,\"email\":\"yara@example.com\"}'
curl -X POST \
  http://localhost:6969/DeleteUserNode \
  -H 'Content-Type: application/json' \
  -d '{\"user_id\":\"<yara_id>\"}'

Example 2: Removing outgoing neighbors

Use DROP on the outgoing traversal to delete connected neighbor nodes and the connecting edges.

QUERY DeleteOutgoingNeighbors (user_id: ID) =>
    DROP N<User>(user_id)::Out<Follows>
    RETURN "Removed outgoing neighbors"

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({ name: name, age: age, email: email })
    RETURN user

QUERY CreateRelationships (user1_id: ID, user2_id: ID) =>
    follows <- AddE<Follows>::From(user1_id)::To(user2_id)
    RETURN follows
N::User {
    name: String,
    age: U8,
    email: String,
}

E::Follows {
    From: User,
    To: User,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

lena = client.query("CreateUser", {
    "name": "Lena",
    "age": 30,
    "email": "lena@example.com",
})
lena_id = lena[0]["user"]["id"]

mason = client.query("CreateUser", {
    "name": "Mason",
    "age": 29,
    "email": "mason@example.com",
})
mason_id = mason[0]["user"]["id"]

client.query("CreateRelationships", {
    "user1_id": lena_id,
    "user2_id": mason_id,
})

result = client.query("DeleteOutgoingNeighbors", {"user_id": lena_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let lena: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Lena",
        "age": 30,
        "email": "lena@example.com",
    })).await?;
    let lena_id = lena["user"]["id"].as_str().unwrap().to_string();

    let mason: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Mason",
        "age": 29,
        "email": "mason@example.com",
    })).await?;
    let mason_id = mason["user"]["id"].as_str().unwrap().to_string();

    let _relationship_result: serde_json::Value = client.query("CreateRelationships", &json!({
        "user1_id": lena_id,
        "user2_id": mason_id,
    })).await?;

    let result: serde_json::Value = client.query("DeleteOutgoingNeighbors", &json!({
        "user_id": lena_id,
    })).await?;

    println!("DeleteOutgoingNeighbors result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    lenaPayload := map[string]any{
        "name":  "Lena",
        "age":   uint8(30),
        "email": "lena@example.com",
    }
    var lena map[string]any
    if err := client.Query("CreateUser", helix.WithData(lenaPayload)).Scan(&lena); err != nil {
        log.Fatalf("CreateUser (Lena) failed: %s", err)
    }
    lenaID := lena["user"].(map[string]any)["id"].(string)

    masonPayload := map[string]any{
        "name":  "Mason",
        "age":   uint8(29),
        "email": "mason@example.com",
    }
    var mason map[string]any
    if err := client.Query("CreateUser", helix.WithData(masonPayload)).Scan(&mason); err != nil {
        log.Fatalf("CreateUser (Mason) failed: %s", err)
    }
    masonID := mason["user"].(map[string]any)["id"].(string)

    client.Query("CreateRelationships", helix.WithData(map[string]any{
        "user1_id": lenaID,
        "user2_id": masonID,
    }))

    var result map[string]any
    if err := client.Query("DeleteOutgoingNeighbors", helix.WithData(map[string]any{
        "user_id": lenaID,
    })).Scan(&result); err != nil {
        log.Fatalf("DeleteOutgoingNeighbors failed: %s", err)
    }

    fmt.Printf("DeleteOutgoingNeighbors result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const lena = await client.query("CreateUser", {
        name: "Lena",
        age: 30,
        email: "lena@example.com",
    });
    const lenaId: string = lena.user.id;

    const mason = await client.query("CreateUser", {
        name: "Mason",
        age: 29,
        email: "mason@example.com",
    });
    const masonId: string = mason.user.id;

    await client.query("CreateRelationships", {
        user1_id: lenaId,
        user2_id: masonId,
    });

    const result = await client.query("DeleteOutgoingNeighbors", {
        user_id: lenaId,
    });

    console.log("DeleteOutgoingNeighbors result:", result);
}

main().catch((err) => {
    console.error("DeleteOutgoingNeighbors query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Lena\",\"age\":30,\"email\":\"lena@example.com\"}'
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Mason\",\"age\":29,\"email\":\"mason@example.com\"}'
curl -X POST \
  http://localhost:6969/CreateRelationships \
  -H 'Content-Type: application/json' \
  -d '{\"user1_id\":\"<lena_id>\",\"user2_id\":\"<mason_id>\"}'

curl -X POST \
  http://localhost:6969/DeleteOutgoingNeighbors \
  -H 'Content-Type: application/json' \
  -d '{\"user_id\":\"<lena_id>\"}'

Example 3: Removing incoming neighbors

Delete any users that follow the target user by traversing incoming connections.

QUERY DeleteIncomingNeighbors (user_id: ID) =>
    DROP N<User>(user_id)::In<Follows>
    RETURN "Removed incoming neighbors"

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({ name: name, age: age, email: email })
    RETURN user

QUERY CreateRelationships (user1_id: ID, user2_id: ID) =>
    follows <- AddE<Follows>::From(user1_id)::To(user2_id)
    RETURN follows
N::User {
    name: String,
    age: U8,
    email: String,
}

E::Follows {
    From: User,
    To: User,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

ophelia = client.query("CreateUser", {
    "name": "Ophelia",
    "age": 32,
    "email": "ophelia@example.com",
})
ophelia_id = ophelia[0]["user"]["id"]

paul = client.query("CreateUser", {
    "name": "Paul",
    "age": 31,
    "email": "paul@example.com",
})
paul_id = paul[0]["user"]["id"]

client.query("CreateRelationships", {
    "user1_id": paul_id,
    "user2_id": ophelia_id,
})

result = client.query("DeleteIncomingNeighbors", {"user_id": ophelia_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let ophelia: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Ophelia",
        "age": 32,
        "email": "ophelia@example.com",
    })).await?;
    let ophelia_id = ophelia["user"]["id"].as_str().unwrap().to_string();

    let paul: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Paul",
        "age": 31,
        "email": "paul@example.com",
    })).await?;
    let paul_id = paul["user"]["id"].as_str().unwrap().to_string();

    let _relationship_result: serde_json::Value = client.query("CreateRelationships", &json!({
        "user1_id": paul_id,
        "user2_id": ophelia_id,
    })).await?;

    let result: serde_json::Value = client.query("DeleteIncomingNeighbors", &json!({
        "user_id": ophelia_id,
    })).await?;

    println!("DeleteIncomingNeighbors result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    opheliaPayload := map[string]any{
        "name":  "Ophelia",
        "age":   uint8(32),
        "email": "ophelia@example.com",
    }
    var ophelia map[string]any
    if err := client.Query("CreateUser", helix.WithData(opheliaPayload)).Scan(&ophelia); err != nil {
        log.Fatalf("CreateUser (Ophelia) failed: %s", err)
    }
    opheliaID := ophelia["user"].(map[string]any)["id"].(string)

    paulPayload := map[string]any{
        "name":  "Paul",
        "age":   uint8(31),
        "email": "paul@example.com",
    }
    var paul map[string]any
    if err := client.Query("CreateUser", helix.WithData(paulPayload)).Scan(&paul); err != nil {
        log.Fatalf("CreateUser (Paul) failed: %s", err)
    }
    paulID := paul["user"].(map[string]any)["id"].(string)

    client.Query("CreateRelationships", helix.WithData(map[string]any{
        "user1_id": paulID,
        "user2_id": opheliaID,
    }))

    var result map[string]any
    if err := client.Query("DeleteIncomingNeighbors", helix.WithData(map[string]any{
        "user_id": opheliaID,
    })).Scan(&result); err != nil {
        log.Fatalf("DeleteIncomingNeighbors failed: %s", err)
    }

    fmt.Printf("DeleteIncomingNeighbors result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const ophelia = await client.query("CreateUser", {
        name: "Ophelia",
        age: 32,
        email: "ophelia@example.com",
    });
    const opheliaId: string = ophelia.user.id;

    const paul = await client.query("CreateUser", {
        name: "Paul",
        age: 31,
        email: "paul@example.com",
    });
    const paulId: string = paul.user.id;

    await client.query("CreateRelationships", {
        user1_id: paulId,
        user2_id: opheliaId,
    });

    const result = await client.query("DeleteIncomingNeighbors", {
        user_id: opheliaId,
    });

    console.log("DeleteIncomingNeighbors result:", result);
}

main().catch((err) => {
    console.error("DeleteIncomingNeighbors query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Ophelia\",\"age\":32,\"email\":\"ophelia@example.com\"}'
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Paul\",\"age\":31,\"email\":\"paul@example.com\"}'
curl -X POST \
  http://localhost:6969/CreateRelationships \
  -H 'Content-Type: application/json' \
  -d '{\"user1_id\":\"<paul_id>\",\"user2_id\":\"<ophelia_id>\"}'

curl -X POST \
  http://localhost:6969/DeleteIncomingNeighbors \
  -H 'Content-Type: application/json' \
  -d '{\"user_id\":\"<ophelia_id>\"}'

Example 4: Removing outgoing edges only

Strip all follows edges that originate from the user without touching the neighbor nodes.

QUERY DeleteOutgoingEdges (user_id: ID) =>
    DROP N<User>(user_id)::OutE<Follows>
    RETURN "Removed outgoing edges"

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({ name: name, age: age, email: email })
    RETURN user

QUERY CreateRelationships (user1_id: ID, user2_id: ID) =>
    follows <- AddE<Follows>::From(user1_id)::To(user2_id)
    RETURN follows
N::User {
    name: String,
    age: U8,
    email: String,
}

E::Follows {
    From: User,
    To: User,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

riley = client.query("CreateUser", {
    "name": "Riley",
    "age": 26,
    "email": "riley@example.com",
})
riley_id = riley[0]["user"]["id"]

sam = client.query("CreateUser", {
    "name": "Sam",
    "age": 25,
    "email": "sam@example.com",
})
sam_id = sam[0]["user"]["id"]

client.query("CreateRelationships", {
    "user1_id": riley_id,
    "user2_id": sam_id,
})

result = client.query("DeleteOutgoingEdges", {"user_id": riley_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let riley: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Riley",
        "age": 26,
        "email": "riley@example.com",
    })).await?;
    let riley_id = riley["user"]["id"].as_str().unwrap().to_string();

    let sam: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Sam",
        "age": 25,
        "email": "sam@example.com",
    })).await?;
    let sam_id = sam["user"]["id"].as_str().unwrap().to_string();

    let _relationship_result: serde_json::Value = client.query("CreateRelationships", &json!({
        "user1_id": riley_id,
        "user2_id": sam_id,
    })).await?;

    let result: serde_json::Value = client.query("DeleteOutgoingEdges", &json!({
        "user_id": riley_id,
    })).await?;

    println!("DeleteOutgoingEdges result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    rileyPayload := map[string]any{
        "name":  "Riley",
        "age":   uint8(26),
        "email": "riley@example.com",
    }
    var riley map[string]any
    if err := client.Query("CreateUser", helix.WithData(rileyPayload)).Scan(&riley); err != nil {
        log.Fatalf("CreateUser (Riley) failed: %s", err)
    }
    rileyID := riley["user"].(map[string]any)["id"].(string)

    samPayload := map[string]any{
        "name":  "Sam",
        "age":   uint8(25),
        "email": "sam@example.com",
    }
    var sam map[string]any
    if err := client.Query("CreateUser", helix.WithData(samPayload)).Scan(&sam); err != nil {
        log.Fatalf("CreateUser (Sam) failed: %s", err)
    }
    samID := sam["user"].(map[string]any)["id"].(string)

    client.Query("CreateRelationships", helix.WithData(map[string]any{
        "user1_id": rileyID,
        "user2_id": samID,
    }))

    var result map[string]any
    if err := client.Query("DeleteOutgoingEdges", helix.WithData(map[string]any{
        "user_id": rileyID,
    })).Scan(&result); err != nil {
        log.Fatalf("DeleteOutgoingEdges failed: %s", err)
    }

    fmt.Printf("DeleteOutgoingEdges result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const riley = await client.query("CreateUser", {
        name: "Riley",
        age: 26,
        email: "riley@example.com",
    });
    const rileyId: string = riley.user.id;

    const sam = await client.query("CreateUser", {
        name: "Sam",
        age: 25,
        email: "sam@example.com",
    });
    const samId: string = sam.user.id;

    await client.query("CreateRelationships", {
        user1_id: rileyId,
        user2_id: samId,
    });

    const result = await client.query("DeleteOutgoingEdges", {
        user_id: rileyId,
    });

    console.log("DeleteOutgoingEdges result:", result);
}

main().catch((err) => {
    console.error("DeleteOutgoingEdges query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Riley\",\"age\":26,\"email\":\"riley@example.com\"}'
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Sam\",\"age\":25,\"email\":\"sam@example.com\"}'
curl -X POST \
  http://localhost:6969/CreateRelationships \
  -H 'Content-Type: application/json' \
  -d '{\"user1_id\":\"<riley_id>\",\"user2_id\":\"<sam_id>\"}'

curl -X POST \
  http://localhost:6969/DeleteOutgoingEdges \
  -H 'Content-Type: application/json' \
  -d '{\"user_id\":\"<riley_id>\"}'

Example 5: Removing incoming edges only

Target follows edges pointing at the user while leaving the neighbor nodes untouched.

QUERY DeleteIncomingEdges (user_id: ID) =>
    DROP N<User>(user_id)::InE<Follows>
    RETURN "Removed incoming edges"

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({ name: name, age: age, email: email })
    RETURN user

QUERY CreateRelationships (user1_id: ID, user2_id: ID) =>
    follows <- AddE<Follows>::From(user1_id)::To(user2_id)
    RETURN follows
N::User {
    name: String,
    age: U8,
    email: String,
}

E::Follows {
    From: User,
    To: User,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

uma = client.query("CreateUser", {
    "name": "Uma",
    "age": 28,
    "email": "uma@example.com",
})
uma_id = uma[0]["user"]["id"]

vince = client.query("CreateUser", {
    "name": "Vince",
    "age": 29,
    "email": "vince@example.com",
})
vince_id = vince[0]["user"]["id"]

client.query("CreateRelationships", {
    "user1_id": vince_id,
    "user2_id": uma_id,
})

result = client.query("DeleteIncomingEdges", {"user_id": uma_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let uma: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Uma",
        "age": 28,
        "email": "uma@example.com",
    })).await?;
    let uma_id = uma["user"]["id"].as_str().unwrap().to_string();

    let vince: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Vince",
        "age": 29,
        "email": "vince@example.com",
    })).await?;
    let vince_id = vince["user"]["id"].as_str().unwrap().to_string();

    let _relationship_result: serde_json::Value = client.query("CreateRelationships", &json!({
        "user1_id": vince_id,
        "user2_id": uma_id,
    })).await?;

    let result: serde_json::Value = client.query("DeleteIncomingEdges", &json!({
        "user_id": uma_id,
    })).await?;

    println!("DeleteIncomingEdges result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    umaPayload := map[string]any{
        "name":  "Uma",
        "age":   uint8(28),
        "email": "uma@example.com",
    }
    var uma map[string]any
    if err := client.Query("CreateUser", helix.WithData(umaPayload)).Scan(&uma); err != nil {
        log.Fatalf("CreateUser (Uma) failed: %s", err)
    }
    umaID := uma["user"].(map[string]any)["id"].(string)

    vincePayload := map[string]any{
        "name":  "Vince",
        "age":   uint8(29),
        "email": "vince@example.com",
    }
    var vince map[string]any
    if err := client.Query("CreateUser", helix.WithData(vincePayload)).Scan(&vince); err != nil {
        log.Fatalf("CreateUser (Vince) failed: %s", err)
    }
    vinceID := vince["user"].(map[string]any)["id"].(string)

    client.Query("CreateRelationships", helix.WithData(map[string]any{
        "user1_id": vinceID,
        "user2_id": umaID,
    }))

    var result map[string]any
    if err := client.Query("DeleteIncomingEdges", helix.WithData(map[string]any{
        "user_id": umaID,
    })).Scan(&result); err != nil {
        log.Fatalf("DeleteIncomingEdges failed: %s", err)
    }

    fmt.Printf("DeleteIncomingEdges result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const uma = await client.query("CreateUser", {
        name: "Uma",
        age: 28,
        email: "uma@example.com",
    });
    const umaId: string = uma.user.id;

    const vince = await client.query("CreateUser", {
        name: "Vince",
        age: 29,
        email: "vince@example.com",
    });
    const vinceId: string = vince.user.id;

    await client.query("CreateRelationships", {
        user1_id: vinceId,
        user2_id: umaId,
    });

    const result = await client.query("DeleteIncomingEdges", {
        user_id: umaId,
    });

    console.log("DeleteIncomingEdges result:", result);
}

main().catch((err) => {
    console.error("DeleteIncomingEdges query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Uma\",\"age\":28,\"email\":\"uma@example.com\"}'
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{\"name\":\"Vince\",\"age\":29,\"email\":\"vince@example.com\"}'
curl -X POST \
  http://localhost:6969/CreateRelationships \
  -H 'Content-Type: application/json' \
  -d '{\"user1_id\":\"<vince_id>\",\"user2_id\":\"<uma_id>\"}'

curl -X POST \
  http://localhost:6969/DeleteIncomingEdges \
  -H 'Content-Type: application/json' \
  -d '{\"user_id\":\"<uma_id>\"}'

ℹ️ Note

Currently, Helix does not support deleting vectors from the graph. As a workaround, you can delete the node and/or the edge that the vector is attached to.

Nodes

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

How do I select nodes in HelixDB?

Use N to select nodes from your graph by ID, type, or property values to begin traversals.

#![allow(unused)]
fn main() {
N<Type>(node_id)
N<Type>({field: value})
}

Example 1: Selecting a user by ID

QUERY GetUser (user_id: ID) =>
    user <- N<User>(user_id)
    RETURN user

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
}
from helix.client import Client

client = Client(local=True, port=6969)

user = client.query("CreateUser", {
    "name": "Alice",
    "age": 25,
    "email": "alice@example.com",
})
user_id = user[0]["user"]["id"]

result = client.query("GetUser", {"user_id": user_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "age": 25,
        "email": "alice@example.com",
    })).await?;
    let user_id = alice["user"]["id"].as_str().unwrap().to_string();

    let result: serde_json::Value = client.query("GetUser", &json!({
        "user_id": user_id,
    })).await?;

    println!("GetUser result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    createPayload := map[string]any{
        "name":  "Alice",
        "age":   uint8(25),
        "email": "alice@example.com",
    }

    var created map[string]any
    if err := client.Query("CreateUser", helix.WithData(createPayload)).Scan(&created); err != nil {
        log.Fatalf("CreateUser failed: %s", err)
    }

    userID := created["user"].(map[string]any)["id"].(string)

    var result map[string]any
    if err := client.Query("GetUser", helix.WithData(map[string]any{"user_id": userID})).Scan(&result); err != nil {
        log.Fatalf("GetUser failed: %s", err)
    }

    fmt.Printf("GetUser result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const created = await client.query("CreateUser", {
        name: "Alice",
        age: 25,
        email: "alice@example.com",
    });
    const userId: string = created.user.id;

    const result = await client.query("GetUser", {
        user_id: userId,
    });

    console.log("GetUser result:", result);
}

main().catch((err) => {
    console.error("GetUser query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/GetUser \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<user_id>"}'

Example 2: Selecting all users

QUERY GetAllUsers () =>
    users <- N<User>
    RETURN users
N::User {
    name: String,
    age: U8,
    email: String,
}
from helix.client import Client

client = Client(local=True, port=6969)

result = client.query("GetAllUsers")
print(result)
use helix_rs::{HelixDB, HelixDBClient};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let result: serde_json::Value = client.query("GetAllUsers", &()).await?;
    println!("GetAllUsers result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var result map[string]any
    if err := client.Query("GetAllUsers").Scan(&result); err != nil {
        log.Fatalf("GetAllUsers failed: %s", err)
    }

    fmt.Printf("GetAllUsers result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("GetAllUsers", {});
    console.log("GetAllUsers result:", result);
}

main().catch((err) => {
    console.error("GetAllUsers query failed:", err);
});
curl -X POST \
  http://localhost:6969/GetAllUsers \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 3: Selecting nodes by property value

Select nodes by matching INDEX field values instead of ID. This is useful when you need to find nodes with specific property values.

QUERY GetUserByEmail (email: String) =>
    user <- N<User>({email: email})
    RETURN user
N::User {
    INDEX email: String,
    name: String,
    age: U8,
}

ℹ️ Note

Property-based selection returns all nodes matching the specified INDEX field values. For more complex filtering conditions, use WHERE clauses.

ℹ️ Note

You can also do specific property-based filtering, e.g., returning only ID, see the Property Filtering. You can also do aggregation steps, e.g., returning the count of nodes, see the Aggregations.

Edges

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

E   Edge

Select edges from your graph to begin traversal.

E<Type>(edge_id)

Example 1: Selecting a follows edge by ID

QUERY GetFollowEdge (edge_id: ID) =>
    follow_edge <- E<Follows>(edge_id)
    RETURN follow_edge

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user

QUERY CreateRelationships (user1_id: ID, user2_id: ID) =>
    follows <- AddE<Follows>::From(user1_id)::To(user2_id)
    RETURN follows

N::User {
    name: String,
    age: U8,
    email: String,
}

E::Follows {
    From: User,
    To: User,
}
from helix.client import Client

client = Client(local=True, port=6969)

alice = client.query("CreateUser", {
    "name": "Alice",
    "age": 25,
    "email": "alice@example.com",
})
alice_id = alice[0]["user"]["id"]

bob = client.query("CreateUser", {
    "name": "Bob",
    "age": 28,
    "email": "bob@example.com",
})
bob_id = bob[0]["user"]["id"]

follows = client.query("CreateRelationships", {
    "user1_id": alice_id,
    "user2_id": bob_id,
})
edge_id = follows[0]["follows"]["id"]

result = client.query("GetFollowEdge", {"edge_id": edge_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "age": 25,
        "email": "alice@example.com",
    })).await?;
    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();

    let bob: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Bob",
        "age": 28,
        "email": "bob@example.com",
    })).await?;
    let bob_id = bob["user"]["id"].as_str().unwrap().to_string();

    let follows: serde_json::Value = client.query("CreateRelationships", &json!({
        "user1_id": alice_id,
        "user2_id": bob_id,
    })).await?;
    let edge_id = follows["follows"]["id"].as_str().unwrap().to_string();

    let result: serde_json::Value = client.query("GetFollowEdge", &json!({
        "edge_id": edge_id,
    })).await?;

    println!("GetFollowEdge result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    alicePayload := map[string]any{
        "name":  "Alice",
        "age":   uint8(25),
        "email": "alice@example.com",
    }

    var alice map[string]any
    if err := client.Query("CreateUser", helix.WithData(alicePayload)).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }
    aliceID := alice["user"].(map[string]any)["id"].(string)

    bobPayload := map[string]any{
        "name":  "Bob",
        "age":   uint8(28),
        "email": "bob@example.com",
    }

    var bob map[string]any
    if err := client.Query("CreateUser", helix.WithData(bobPayload)).Scan(&bob); err != nil {
        log.Fatalf("CreateUser (Bob) failed: %s", err)
    }
    bobID := bob["user"].(map[string]any)["id"].(string)

    followsPayload := map[string]any{
        "user1_id": aliceID,
        "user2_id": bobID,
    }

    var follows map[string]any
    if err := client.Query("CreateRelationships", helix.WithData(followsPayload)).Scan(&follows); err != nil {
        log.Fatalf("CreateRelationships failed: %s", err)
    }
    edgeID := follows["follows"].(map[string]any)["id"].(string)

    var result map[string]any
    if err := client.Query("GetFollowEdge", helix.WithData(map[string]any{"edge_id": edgeID})).Scan(&result); err != nil {
        log.Fatalf("GetFollowEdge failed: %s", err)
    }

    fmt.Printf("GetFollowEdge result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const alice = await client.query("CreateUser", {
        name: "Alice",
        age: 25,
        email: "alice@example.com",
    });
    const aliceId: string = alice.user.id;

    const bob = await client.query("CreateUser", {
        name: "Bob",
        age: 28,
        email: "bob@example.com",
    });
    const bobId: string = bob.user.id;

    const follows = await client.query("CreateRelationships", {
        user1_id: aliceId,
        user2_id: bobId,
    });
    const edgeId: string = follows.follows.id;

    const result = await client.query("GetFollowEdge", {
        edge_id: edgeId,
    });

    console.log("GetFollowEdge result:", result);
}

main().catch((err) => {
    console.error("GetFollowEdge query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":28,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateRelationships \
  -H 'Content-Type: application/json' \
  -d '{"user1_id":"<alice_id>","user2_id":"<bob_id>"}'

curl -X POST \
  http://localhost:6969/GetFollowEdge \
  -H 'Content-Type: application/json' \
  -d '{"edge_id":"<edge_id>"}'

Example 2: Selecting all follows edges

QUERY GetAllFollows () =>
    follows <- E<Follows>
    RETURN follows
E::Follows {
    From: User,
    To: User,
}
from helix.client import Client

client = Client(local=True, port=6969)

result = client.query("GetAllFollows")
print(result)
use helix_rs::{HelixDB, HelixDBClient};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let result: serde_json::Value = client.query("GetAllFollows", &()).await?;
    println!("GetAllFollows result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var result map[string]any
    if err := client.Query("GetAllFollows").Scan(&result); err != nil {
        log.Fatalf("GetAllFollows failed: %s", err)
    }

    fmt.Printf("GetAllFollows result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("GetAllFollows", {});
    console.log("GetAllFollows result:", result);
}

main().catch((err) => {
    console.error("GetAllFollows query failed:", err);
});
curl -X POST \
  http://localhost:6969/GetAllFollows \
  -H 'Content-Type: application/json' \
  -d '{}'

Vectors

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

V   Vector

Select vectors from your graph to begin traversal.

V<Type>(vector_id)

Example 1: Selecting a vector by ID

QUERY GetDocumentVector (vector_id: ID) =>
    doc_vector <- V<Document>(vector_id)
    RETURN doc_vector

QUERY CreateDocumentVector (vector: [F64], content: String) =>
    doc_vector <- AddV<Document>(vector, {
        content: content
    })
    RETURN doc_vector
V::Document {
    content: String,
}
from helix.client import Client

client = Client(local=True, port=6969)

vector_payload = [0.12, 0.34, 0.56, 0.78]

created = client.query("CreateDocumentVector", {
    "vector": vector_payload,
    "content": "Chunk about vector queries.",
})
vector_id = created[0]["doc_vector"]["id"]

result = client.query("GetDocumentVector", {"vector_id": vector_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let created: serde_json::Value = client
        .query(
            "CreateDocumentVector",
            &json!({
                "vector": [0.12, 0.34, 0.56, 0.78],
                "content": "Chunk about vector queries.",
            }),
        )
        .await?;
    let vector_id = created[0]["doc_vector"]["id"].as_str().unwrap().to_string();

    let result: serde_json::Value = client
        .query("GetDocumentVector", &json!({ "vector_id": vector_id }))
        .await?;

    println!("GetDocumentVector result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    createPayload := map[string]any{
        "vector":  []float64{0.12, 0.34, 0.56, 0.78},
        "content": "Chunk about vector queries.",
    }

    var created map[string]any
    if err := client.Query("CreateDocumentVector", helix.WithData(createPayload)).Scan(&created); err != nil {
        log.Fatalf("CreateDocumentVector failed: %s", err)
    }

    docVector := created["doc_vector"].(map[string]any)
    vectorID := docVector["id"].(string)

    var result map[string]any
    if err := client.Query("GetDocumentVector", helix.WithData(map[string]any{
        "vector_id": vectorID,
    })).Scan(&result); err != nil {
        log.Fatalf("GetDocumentVector failed: %s", err)
    }

    fmt.Printf("GetDocumentVector result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const created = await client.query("CreateDocumentVector", {
        vector: [0.12, 0.34, 0.56, 0.78],
        content: "Chunk about vector queries.",
    });
    const vectorId: string = created.doc_vector.id;

    const result = await client.query("GetDocumentVector", {
        vector_id: vectorId,
    });

    console.log("GetDocumentVector result:", result);
}

main().catch((err) => {
    console.error("GetDocumentVector query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateDocumentVector \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.12,0.34,0.56,0.78],"content":"Chunk about vector queries."}'

curl -X POST \
  http://localhost:6969/GetDocumentVector \
  -H 'Content-Type: application/json' \
  -d '{"vector_id":"<vector_id>"}'

Conditional Steps

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Filtering with WHERE  

Filter elements based on specific conditions.

::WHERE(<condition>)
::WHERE(_::{property}::COMPARISON(value))

ℹ️ Note

The condition in the WHERE step must evaluate to a boolean value. If the condition is not met, the element will be filtered out from the results.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Comparison Operations

The following operations can be used to compare values. EQ and NEQ can be used to compare strings, booleans, and numbers. GT, GTE, LT, and LTE can only be used to compare numbers.

String, Boolean, and Number Operations

OperationDescriptionExample
::EQ(value)Equals::WHERE(_::{status}::EQ("active"))
::NEQ(value)Not equals::WHERE(_::{age}::NEQ(25))

Number Operations

OperationDescriptionExample
::GT(value)Greater than::WHERE(_::{age}::GT(25))
::LT(value)Less than::WHERE(_::{age}::LT(30))
::GTE(value)Greater than or equal::WHERE(_::{rating}::GTE(4.5))
::LTE(value)Less than or equal::WHERE(_::{priority}::LTE(2))

String Operations

OperationDescriptionExample
::CONTAINS(value)String contains substring::WHERE(_::{name}::CONTAINS("john"))
::CONTAINS(object)Array contains object::WHERE(_::Out<Follows>::CONTAINS(user))

Array/Set Operations

OperationDescriptionExample
::IS_IN(array)Value exists in array::WHERE(_::{status}::IS_IN(["active", "pending"]))

Example 1: Basic filtering with WHERE

QUERY GetAdultUsers () =>
    adult_users <- N<User>::WHERE(_::{age}::GT(18))
    RETURN adult_users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 16, "email": "bob@example.com"},
    {"name": "Charlie", "age": 30, "email": "charlie@example.com"},
    {"name": "Diana", "age": 17, "email": "diana@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetAdultUsers", {})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 16, "bob@example.com"),
        ("Charlie", 30, "charlie@example.com"),
        ("Diana", 17, "diana@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetAdultUsers", &json!({})).await?;
    println!("Adult users: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(16), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(30), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(17), "email": "diana@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetAdultUsers", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetAdultUsers failed: %s", err)
    }

    fmt.Printf("Adult users: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 16, email: "bob@example.com" },
        { name: "Charlie", age: 30, email: "charlie@example.com" },
        { name: "Diana", age: 17, email: "diana@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetAdultUsers", {});
    console.log("Adult users:", result);
}

main().catch((err) => {
    console.error("GetAdultUsers query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":16,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":30,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":17,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/GetAdultUsers \
  -H 'Content-Type: application/json' \
  -d '{}'

💡 Tip

For multiple conditions, see Multiple Conditional Steps.

Example 2: String and equality filtering

QUERY GetActiveUsers (status: String) =>
    active_users <- N<User>::WHERE(_::{status}::EQ(status))
    RETURN active_users

QUERY CreateUser (name: String, age: U8, email: String, status: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email,
        status: status
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
    status: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com", "status": "active"},
    {"name": "Bob", "age": 30, "email": "bob@example.com", "status": "inactive"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com", "status": "active"},
    {"name": "Diana", "age": 22, "email": "diana@example.com", "status": "pending"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetActiveUsers", {"status": "active"})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com", "active"),
        ("Bob", 30, "bob@example.com", "inactive"),
        ("Charlie", 28, "charlie@example.com", "active"),
        ("Diana", 22, "diana@example.com", "pending"),
    ];

    for (name, age, email, status) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
            "status": status,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetActiveUsers", &json!({
        "status": "active"
    })).await?;
    println!("Active users: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com", "status": "active"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com", "status": "inactive"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com", "status": "active"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com", "status": "pending"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    searchPayload := map[string]any{"status": "active"}
    var result map[string]any
    if err := client.Query("GetActiveUsers", helix.WithData(searchPayload)).Scan(&result); err != nil {
        log.Fatalf("GetActiveUsers failed: %s", err)
    }

    fmt.Printf("Active users: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com", status: "active" },
        { name: "Bob", age: 30, email: "bob@example.com", status: "inactive" },
        { name: "Charlie", age: 28, email: "charlie@example.com", status: "active" },
        { name: "Diana", age: 22, email: "diana@example.com", status: "pending" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetActiveUsers", { status: "active" });
    console.log("Active users:", result);
}

main().catch((err) => {
    console.error("GetActiveUsers query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com","status":"inactive"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22,"email":"diana@example.com","status":"pending"}'

curl -X POST \
  http://localhost:6969/GetActiveUsers \
  -H 'Content-Type: application/json' \
  -d '{"status":"active"}'

Filter by Relationships with EXISTS  

Returns true if a traversal has any results. Otherwise, it returns false.

EXISTS(<traversal>)

Example 1: Using EXISTS for relationship filtering

QUERY GetUsersWithFollowers () =>
    users <- N<User>::WHERE(EXISTS(_::In<Follows>))
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user

QUERY CreateFollow (follower_id: ID, following_id: ID) =>
    follower <- N<User>(follower_id)
    following <- N<User>(following_id)
    AddE<Follows>::From(follower)::To(following)
    RETURN "Success"
N::User {
    name: String,
    age: U8,
    email: String
}

E::Follows {
    From: User,
    To: User
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
]

user_ids = []
for user in users:
    result = client.query("CreateUser", user)
    user_ids.append(result[0]["user"]["id"])

client.query("CreateFollow", {"follower_id": user_ids[1], "following_id": user_ids[0]})
client.query("CreateFollow", {"follower_id": user_ids[2], "following_id": user_ids[0]})

result = client.query("GetUsersWithFollowers", {})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 28, "charlie@example.com"),
    ];

    let mut user_ids = Vec::new();
    for (name, age, email) in &users {
        let result: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
        user_ids.push(result["user"]["id"].as_str().unwrap().to_string());
    }

    let _follow1: serde_json::Value = client.query("CreateFollow", &json!({
        "follower_id": user_ids[1],
        "following_id": user_ids[0]
    })).await?;

    let _follow2: serde_json::Value = client.query("CreateFollow", &json!({
        "follower_id": user_ids[2],
        "following_id": user_ids[0]
    })).await?;

    let result: serde_json::Value = client.query("GetUsersWithFollowers", &json!({})).await?;
    println!("Users with followers: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
    }

    var userIDs []string
    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
        userIDs = append(userIDs, created["user"].(map[string]any)["id"].(string))
    }

    followPayload1 := map[string]any{
        "follower_id":  userIDs[1],
        "following_id": userIDs[0],
    }
    var follow1 map[string]any
    if err := client.Query("CreateFollow", helix.WithData(followPayload1)).Scan(&follow1); err != nil {
        log.Fatalf("CreateFollow failed: %s", err)
    }

    followPayload2 := map[string]any{
        "follower_id":  userIDs[2],
        "following_id": userIDs[0],
    }
    var follow2 map[string]any
    if err := client.Query("CreateFollow", helix.WithData(followPayload2)).Scan(&follow2); err != nil {
        log.Fatalf("CreateFollow failed: %s", err)
    }

    var result map[string]any
    if err := client.Query("GetUsersWithFollowers", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUsersWithFollowers failed: %s", err)
    }

    fmt.Printf("Users with followers: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
    ];

    const userIds: string[] = [];
    for (const user of users) {
        const result = await client.query("CreateUser", user);
        userIds.push(result.user.id);
    }

    await client.query("CreateFollow", {
        follower_id: userIds[1],
        following_id: userIds[0]
    });

    await client.query("CreateFollow", {
        follower_id: userIds[2],
        following_id: userIds[0]
    });

    const result = await client.query("GetUsersWithFollowers", {});
    console.log("Users with followers:", result);
}

main().catch((err) => {
    console.error("GetUsersWithFollowers query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateFollow \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"<bob_id>","following_id":"<alice_id>"}'

curl -X POST \
  http://localhost:6969/CreateFollow \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"<charlie_id>","following_id":"<alice_id>"}'

curl -X POST \
  http://localhost:6969/GetUsersWithFollowers \
  -H 'Content-Type: application/json' \
  -d '{}'

Multiple Conditional Steps

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Negate a condition with NOT (!)  

Negate a condition to match the opposite.

::WHERE(!<condition>)

Example: ! can be used infront of any condition that returns a boolean.

QUERY GetActiveUsersWithFollowers () =>
    followers <- N<User>::WHERE(
        !AND( // Negate the AND condition
            !_::{is_active}::EQ(true), // Negate the EQ condition
            _::Out<Follows>::COUNT::LT(10)
        )
    )
    RETURN followers
N::User {
    name: String,
    is_active: Boolean
}

E::Follows {
    From: User,
    To: User
}

The ! operator can also be combined with EXISTS to find elements without certain relationships:

QUERY GetUsersWithoutFollowers () =>
    users <- N<User>::WHERE(!EXISTS(_::In<Follows>))
    RETURN users

Enforce all conditions with AND  

Takes a list of conditions and returns true only if all conditions are true.

::WHERE(AND(<condition1>, <condition2>, ...))

Enforce any condition with OR  

Takes a list of conditions and returns true if at least one condition is true.

::WHERE(OR(<condition1>, <condition2>, ...))

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Using AND for range filtering

QUERY GetYoungAdults () =>
    users <- N<User>::WHERE(AND(_::{age}::GT(18), _::{age}::LT(30)))
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 16, "email": "bob@example.com"},
    {"name": "Charlie", "age": 35, "email": "charlie@example.com"},
    {"name": "Diana", "age": 22, "email": "diana@example.com"},
    {"name": "Eve", "age": 17, "email": "eve@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetYoungAdults", {})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 16, "bob@example.com"),
        ("Charlie", 35, "charlie@example.com"),
        ("Diana", 22, "diana@example.com"),
        ("Eve", 17, "eve@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetYoungAdults", &json!({})).await?;
    println!("Young adults: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(16), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(35), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com"},
        {"name": "Eve", "age": uint8(17), "email": "eve@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetYoungAdults", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetYoungAdults failed: %s", err)
    }

    fmt.Printf("Young adults: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 16, email: "bob@example.com" },
        { name: "Charlie", age: 35, email: "charlie@example.com" },
        { name: "Diana", age: 22, email: "diana@example.com" },
        { name: "Eve", age: 17, email: "eve@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetYoungAdults", {});
    console.log("Young adults:", result);
}

main().catch((err) => {
    console.error("GetYoungAdults query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":16,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":35,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","age":17,"email":"eve@example.com"}'

curl -X POST \
  http://localhost:6969/GetYoungAdults \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Using OR for multiple valid options

QUERY GetSpecificUsers () =>
    users <- N<User>::WHERE(OR(_::{name}::EQ("Alice"), _::{name}::EQ("Bob")))
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
    {"name": "Diana", "age": 22, "email": "diana@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetSpecificUsers", {})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 28, "charlie@example.com"),
        ("Diana", 22, "diana@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetSpecificUsers", &json!({})).await?;
    println!("Specific users: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetSpecificUsers", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetSpecificUsers failed: %s", err)
    }

    fmt.Printf("Specific users: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
        { name: "Diana", age: 22, email: "diana@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetSpecificUsers", {});
    console.log("Specific users:", result);
}

main().catch((err) => {
    console.error("GetSpecificUsers query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/GetSpecificUsers \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 3: Complex nested AND and OR conditions

QUERY GetFilteredUsers () =>
    users <- N<User>::WHERE(
        AND(
            _::{age}::GT(18),
            OR(_::{name}::EQ("Alice"), _::{name}::EQ("Bob"))
        )
    )
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 16, "email": "bob@example.com"},
    {"name": "Alice", "age": 17, "email": "alice2@example.com"},
    {"name": "Charlie", "age": 30, "email": "charlie@example.com"},
    {"name": "Bob", "age": 22, "email": "bob2@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetFilteredUsers", {})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 16, "bob@example.com"),
        ("Alice", 17, "alice2@example.com"),
        ("Charlie", 30, "charlie@example.com"),
        ("Bob", 22, "bob2@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetFilteredUsers", &json!({})).await?;
    println!("Filtered users: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(16), "email": "bob@example.com"},
        {"name": "Alice", "age": uint8(17), "email": "alice2@example.com"},
        {"name": "Charlie", "age": uint8(30), "email": "charlie@example.com"},
        {"name": "Bob", "age": uint8(22), "email": "bob2@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetFilteredUsers", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetFilteredUsers failed: %s", err)
    }

    fmt.Printf("Filtered users: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 16, email: "bob@example.com" },
        { name: "Alice", age: 17, email: "alice2@example.com" },
        { name: "Charlie", age: 30, email: "charlie@example.com" },
        { name: "Bob", age: 22, email: "bob2@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetFilteredUsers", {});
    console.log("Filtered users:", result);
}

main().catch((err) => {
    console.error("GetFilteredUsers query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":16,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":17,"email":"alice2@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":30,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":22,"email":"bob2@example.com"}'

curl -X POST \
  http://localhost:6969/GetFilteredUsers \
  -H 'Content-Type: application/json' \
  -d '{}'

Anonymous Traversals

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

_::<traversal>::<operation>

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Filter by relationship count

QUERY GetInfluentialUsers () =>
    users <- N<User>::WHERE(_::In<Follows>::COUNT::GT(100))
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user

QUERY CreateFollow (follower_id: ID, following_id: ID) =>
    follower <- N<User>(follower_id)
    following <- N<User>(following_id)
    AddE<Follows>::From(follower)::To(following)
    RETURN "Success"
N::User {
    name: String,
    age: U8,
    email: String
}

E::Follows {
    From: User,
    To: User
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
]

user_ids = []
for user in users:
    result = client.query("CreateUser", user)
    user_ids.append(result[0]["user"]["id"])

for j in range(150):
    fake_user = client.query("CreateUser", {
        "name": f"Follower{j}",
        "age": 20,
        "email": f"follower{j}@example.com"
    })
    fake_id = fake_user[0]["user"]["id"]
    client.query("CreateFollow", {"follower_id": fake_id, "following_id": user_ids[0]})

result = client.query("GetInfluentialUsers", {})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 28, "charlie@example.com"),
    ];

    let mut user_ids = Vec::new();
    for (name, age, email) in &users {
        let result: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
        user_ids.push(result["user"]["id"].as_str().unwrap().to_string());
    }

    for j in 0..150 {
        let fake_user: serde_json::Value = client.query("CreateUser", &json!({
            "name": format!("Follower{}", j),
            "age": 20,
            "email": format!("follower{}@example.com", j),
        })).await?;
        let fake_id = fake_user["user"]["id"].as_str().unwrap().to_string();

        let _follow: serde_json::Value = client.query("CreateFollow", &json!({
            "follower_id": fake_id,
            "following_id": user_ids[0]
        })).await?;
    }

    let result: serde_json::Value = client.query("GetInfluentialUsers", &json!({})).await?;
    println!("Influential users: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
    }

    var userIDs []string
    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
        userIDs = append(userIDs, created["user"].(map[string]any)["id"].(string))
    }

    for j := 0; j < 150; j++ {
        fakeUser := map[string]any{
            "name":  fmt.Sprintf("Follower%d", j),
            "age":   uint8(20),
            "email": fmt.Sprintf("follower%d@example.com", j),
        }

        var fakeCreated map[string]any
        if err := client.Query("CreateUser", helix.WithData(fakeUser)).Scan(&fakeCreated); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
        fakeID := fakeCreated["user"].(map[string]any)["id"].(string)

        followPayload := map[string]any{
            "follower_id":  fakeID,
            "following_id": userIDs[0],
        }
        var follow map[string]any
        if err := client.Query("CreateFollow", helix.WithData(followPayload)).Scan(&follow); err != nil {
            log.Fatalf("CreateFollow failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetInfluentialUsers", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetInfluentialUsers failed: %s", err)
    }

    fmt.Printf("Influential users: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
    ];

    const userIds: string[] = [];
    for (const user of users) {
        const result = await client.query("CreateUser", user);
        userIds.push(result.user.id);
    }

    for (let j = 0; j < 150; j++) {
        const fakeUser = await client.query("CreateUser", {
            name: `Follower${j}`,
            age: 20,
            email: `follower${j}@example.com`,
        });
        const fakeId: string = fakeUser.user.id;

        await client.query("CreateFollow", {
            follower_id: fakeId,
            following_id: userIds[0]
        });
    }

    const result = await client.query("GetInfluentialUsers", {});
    console.log("Influential users:", result);
}

main().catch((err) => {
    console.error("GetInfluentialUsers query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Follower1","age":20,"email":"follower1@example.com"}'

curl -X POST \
  http://localhost:6969/CreateFollow \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"<follower_id>","following_id":"<alice_id>"}'

curl -X POST \
  http://localhost:6969/GetInfluentialUsers \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Property-based filtering with anonymous traversal

QUERY GetActiveUsersWithPosts () =>
    users <- N<User>::WHERE(AND(_::{status}::EQ("active"), _::Out<HasPost>::COUNT::GT(0)))
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String, status: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email,
        status: status
    })
    RETURN user

QUERY CreatePost (user_id: ID, title: String, content: String) =>
    user <- N<User>(user_id)
    post <- AddN<Post>({
        title: title,
        content: content
    })
    AddE<HasPost>::From(user)::To(post)
    RETURN post
N::User {
    name: String,
    age: U8,
    email: String,
    status: String
}

N::Post {
    title: String,
    content: String
}

E::HasPost {
    From: User,
    To: Post
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com", "status": "active"},
    {"name": "Bob", "age": 30, "email": "bob@example.com", "status": "active"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com", "status": "inactive"},
    {"name": "Diana", "age": 22, "email": "diana@example.com", "status": "active"},
]

user_ids = []
for user in users:
    result = client.query("CreateUser", user)
    user_ids.append(result[0]["user"]["id"])

client.query("CreatePost", {
    "user_id": user_ids[0],
    "title": "My First Post",
    "content": "This is Alice's first post"
})

client.query("CreatePost", {
    "user_id": user_ids[1],
    "title": "Bob's Thoughts",
    "content": "This is Bob's post"
})

result = client.query("GetActiveUsersWithPosts", {})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com", "active"),
        ("Bob", 30, "bob@example.com", "active"),
        ("Charlie", 28, "charlie@example.com", "inactive"),
        ("Diana", 22, "diana@example.com", "active"),
    ];

    let mut user_ids = Vec::new();
    for (name, age, email, status) in &users {
        let result: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
            "status": status,
        })).await?;
        user_ids.push(result["user"]["id"].as_str().unwrap().to_string());
    }

    let _post1: serde_json::Value = client.query("CreatePost", &json!({
        "user_id": user_ids[0],
        "title": "My First Post",
        "content": "This is Alice's first post"
    })).await?;

    let _post2: serde_json::Value = client.query("CreatePost", &json!({
        "user_id": user_ids[1],
        "title": "Bob's Thoughts",
        "content": "This is Bob's post"
    })).await?;

    let result: serde_json::Value = client.query("GetActiveUsersWithPosts", &json!({})).await?;
    println!("Active users with posts: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com", "status": "active"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com", "status": "active"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com", "status": "inactive"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com", "status": "active"},
    }

    var userIDs []string
    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
        userIDs = append(userIDs, created["user"].(map[string]any)["id"].(string))
    }

    post1Payload := map[string]any{
        "user_id": userIDs[0],
        "title":   "My First Post",
        "content": "This is Alice's first post",
    }
    var post1 map[string]any
    if err := client.Query("CreatePost", helix.WithData(post1Payload)).Scan(&post1); err != nil {
        log.Fatalf("CreatePost failed: %s", err)
    }

    post2Payload := map[string]any{
        "user_id": userIDs[1],
        "title":   "Bob's Thoughts",
        "content": "This is Bob's post",
    }
    var post2 map[string]any
    if err := client.Query("CreatePost", helix.WithData(post2Payload)).Scan(&post2); err != nil {
        log.Fatalf("CreatePost failed: %s", err)
    }

    var result map[string]any
    if err := client.Query("GetActiveUsersWithPosts", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetActiveUsersWithPosts failed: %s", err)
    }

    fmt.Printf("Active users with posts: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com", status: "active" },
        { name: "Bob", age: 30, email: "bob@example.com", status: "active" },
        { name: "Charlie", age: 28, email: "charlie@example.com", status: "inactive" },
        { name: "Diana", age: 22, email: "diana@example.com", status: "active" },
    ];

    const userIds: string[] = [];
    for (const user of users) {
        const result = await client.query("CreateUser", user);
        userIds.push(result.user.id);
    }

    await client.query("CreatePost", {
        user_id: userIds[0],
        title: "My First Post",
        content: "This is Alice's first post"
    });

    await client.query("CreatePost", {
        user_id: userIds[1],
        title: "Bob's Thoughts",
        content: "This is Bob's post"
    });

    const result = await client.query("GetActiveUsersWithPosts", {});
    console.log("Active users with posts:", result);
}

main().catch((err) => {
    console.error("GetActiveUsersWithPosts query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com","status":"inactive"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22,"email":"diana@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/CreatePost \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<alice_id>","title":"My First Post","content":"This is Alice'"'"'s first post"}'

curl -X POST \
  http://localhost:6969/CreatePost \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<bob_id>","title":"Bob'"'"'s Thoughts","content":"This is Bob'"'"'s post"}'

curl -X POST \
  http://localhost:6969/GetActiveUsersWithPosts \
  -H 'Content-Type: application/json' \
  -d '{}'

Property Access

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Select specific properties with ::{}

Access properties of an item by using the property names as defined in the schema.

::{<property1>, <property2>, ...}
::{<property1>: <alias>, <property2>}

ℹ️ Note

Property access allows you to return only the fields you need, reducing data transfer and improving query performance.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Basic property selection

QUERY GetUserBasicInfo () =>
    users <- N<User>::RANGE(0, 10)
    RETURN users::{name, age}

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
    {"name": "Diana", "age": 22, "email": "diana@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetUserBasicInfo", {})
print("User basic info:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 28, "charlie@example.com"),
        ("Diana", 22, "diana@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUserBasicInfo", &json!({})).await?;
    println!("User basic info: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUserBasicInfo", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUserBasicInfo failed: %s", err)
    }

    fmt.Printf("User basic info: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
        { name: "Diana", age: 22, email: "diana@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetUserBasicInfo", {});
    console.log("User basic info:", result);
}

main().catch((err) => {
    console.error("GetUserBasicInfo query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/GetUserBasicInfo \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Property selection with renaming

QUERY GetUserDisplayInfo () =>
    users <- N<User>::RANGE(0, 10)
    RETURN users::{displayName: name, userAge: age}

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
    {"name": "Diana", "age": 22, "email": "diana@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetUserDisplayInfo", {})
print("User display info:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 28, "charlie@example.com"),
        ("Diana", 22, "diana@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUserDisplayInfo", &json!({})).await?;
    println!("User display info: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUserDisplayInfo", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUserDisplayInfo failed: %s", err)
    }

    fmt.Printf("User display info: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
        { name: "Diana", age: 22, email: "diana@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetUserDisplayInfo", {});
    console.log("User display info:", result);
}

main().catch((err) => {
    console.error("GetUserDisplayInfo query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/GetUserDisplayInfo \
  -H 'Content-Type: application/json' \
  -d '{}'

Access unique identifiers with ::ID

Access the unique identifier of any node, edge, or vector.

::ID

ℹ️ Note

Every element in HelixDB has a unique ID that can be accessed using the ::ID syntax.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Getting element IDs

QUERY GetUserIDs () =>
    users <- N<User>::RANGE(0, 5)
    RETURN users::ID

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetUserIDs", {})
print("User IDs:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 28, "charlie@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUserIDs", &json!({})).await?;
    println!("User IDs: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUserIDs", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUserIDs failed: %s", err)
    }

    fmt.Printf("User IDs: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetUserIDs", {});
    console.log("User IDs:", result);
}

main().catch((err) => {
    console.error("GetUserIDs query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/GetUserIDs \
  -H 'Content-Type: application/json' \
  -d '{}'

Include all properties with ..

Use the spread operator to include all properties while optionally remapping specific fields.

::{
    <alias>: <property>, 
    ..
}

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Property aliasing with spread operator

QUERY GetUsersWithAlias () =>
    users <- N<User>::RANGE(0, 5)
    RETURN users::{
        userID: ::ID,
        ..
    }

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetUsersWithAlias", {})
print("Users with alias:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 28, "charlie@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUsersWithAlias", &json!({})).await?;
    println!("Users with alias: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUsersWithAlias", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUsersWithAlias failed: %s", err)
    }

    fmt.Printf("Users with alias: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetUsersWithAlias", {});
    console.log("Users with alias:", result);
}

main().catch((err) => {
    console.error("GetUsersWithAlias query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/GetUsersWithAlias \
  -H 'Content-Type: application/json' \
  -d '{}'

Property Additions

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

::{
    <new_property>: <schema_field>,
    <computed_property>: <traversal_expression>
}

ℹ️ Note

Property additions allow you to compute derived values and add metadata to your query results without modifying the underlying schema.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Adding computed properties with traversals

QUERY GetUserDetails () =>
    users <- N<User>::RANGE(0, 5)
    RETURN users::{
        userID: ::ID,
        followerCount: _::In<Follows>::COUNT
    }

QUERY CreateUser (name: String, age: U8) =>
    user <- AddN<User>({
        name: name,
        age: age
    })
    RETURN user

QUERY CreateFollow (follower_id: ID, following_id: ID) =>
    follower <- N<User>(follower_id)
    following <- N<User>(following_id)
    AddE<Follows>::From(follower)::To(following)
    RETURN "Success"
N::User {
    name: String,
    age: U8
}

E::Follows {
    From: User,
    To: User
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25},
    {"name": "Bob", "age": 30},
    {"name": "Charlie", "age": 28},
    {"name": "Diana", "age": 22},
]

user_ids = []
for user in users:
    result = client.query("CreateUser", user)
    user_ids.append(result[0]["user"]["id"])

for i in range(1, len(user_ids)):
    client.query("CreateFollow", {
        "follower_id": user_ids[i],
        "following_id": user_ids[0]
    })

result = client.query("GetUserDetails", {})
print("User details with follower count:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25),
        ("Bob", 30),
        ("Charlie", 28),
        ("Diana", 22),
    ];

    let mut user_ids = Vec::new();
    for (name, age) in &users {
        let result: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
        })).await?;
        user_ids.push(result["user"]["id"].as_str().unwrap().to_string());
    }

    for i in 1..user_ids.len() {
        let _follow: serde_json::Value = client.query("CreateFollow", &json!({
            "follower_id": user_ids[i],
            "following_id": user_ids[0]
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUserDetails", &json!({})).await?;
    println!("User details with follower count: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25)},
        {"name": "Bob", "age": uint8(30)},
        {"name": "Charlie", "age": uint8(28)},
        {"name": "Diana", "age": uint8(22)},
    }

    var userIDs []string
    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
        userIDs = append(userIDs, created["user"].(map[string]any)["id"].(string))
    }

    for i := 1; i < len(userIDs); i++ {
        followPayload := map[string]any{
            "follower_id":  userIDs[i],
            "following_id": userIDs[0],
        }
        var follow map[string]any
        if err := client.Query("CreateFollow", helix.WithData(followPayload)).Scan(&follow); err != nil {
            log.Fatalf("CreateFollow failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUserDetails", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUserDetails failed: %s", err)
    }

    fmt.Printf("User details with follower count: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25 },
        { name: "Bob", age: 30 },
        { name: "Charlie", age: 28 },
        { name: "Diana", age: 22 },
    ];

    const userIds: string[] = [];
    for (const user of users) {
        const result = await client.query("CreateUser", user);
        userIds.push(result.user.id);
    }

    for (let i = 1; i < userIds.length; i++) {
        await client.query("CreateFollow", {
            follower_id: userIds[i],
            following_id: userIds[0]
        });
    }

    const result = await client.query("GetUserDetails", {});
    console.log("User details with follower count:", result);
}

main().catch((err) => {
    console.error("GetUserDetails query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22}'

curl -X POST \
  http://localhost:6969/CreateFollow \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"<bob_id>","following_id":"<alice_id>"}'

curl -X POST \
  http://localhost:6969/CreateFollow \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"<charlie_id>","following_id":"<alice_id>"}'

curl -X POST \
  http://localhost:6969/CreateFollow \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"<diana_id>","following_id":"<alice_id>"}'

curl -X POST \
  http://localhost:6969/GetUserDetails \
  -H 'Content-Type: application/json' \
  -d '{}'

Property Exclusion

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

::!{<property1>, <property2>, ...}

ℹ️ Note

Property exclusion is useful when you want to return most properties but hide sensitive or unnecessary fields like passwords, internal IDs, or large data fields.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Excluding sensitive properties

QUERY GetPublicUserInfo () =>
    users <- N<User>::RANGE(0, 10)
    RETURN users::!{email, location}

QUERY CreateUser (name: String, age: U8, email: String, posts: U32, followers: U32, following: U32, location: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email,
        posts: posts,
        followers: followers,
        following: following,
        location: location
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
    posts: U32,
    followers: U32,
    following: U32,
    location: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {
        "name": "Alice",
        "age": 25,
        "email": "alice@example.com",
        "posts": 42,
        "followers": 150,
        "following": 89,
        "location": "New York"
    },
    {
        "name": "Bob",
        "age": 30,
        "email": "bob@example.com",
        "posts": 28,
        "followers": 203,
        "following": 156,
        "location": "San Francisco"
    },
    {
        "name": "Charlie",
        "age": 28,
        "email": "charlie@example.com",
        "posts": 67,
        "followers": 89,
        "following": 234,
        "location": "London"
    }
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetPublicUserInfo", {})
print("Public user info (email and location excluded):", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com", 42, 150, 89, "New York"),
        ("Bob", 30, "bob@example.com", 28, 203, 156, "San Francisco"),
        ("Charlie", 28, "charlie@example.com", 67, 89, 234, "London"),
    ];

    for (name, age, email, posts, followers, following, location) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
            "posts": posts,
            "followers": followers,
            "following": following,
            "location": location,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetPublicUserInfo", &json!({})).await?;
    println!("Public user info (email and location excluded): {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {
            "name": "Alice", "age": uint8(25), "email": "alice@example.com",
            "posts": uint32(42), "followers": uint32(150), "following": uint32(89), "location": "New York",
        },
        {
            "name": "Bob", "age": uint8(30), "email": "bob@example.com",
            "posts": uint32(28), "followers": uint32(203), "following": uint32(156), "location": "San Francisco",
        },
        {
            "name": "Charlie", "age": uint8(28), "email": "charlie@example.com",
            "posts": uint32(67), "followers": uint32(89), "following": uint32(234), "location": "London",
        },
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetPublicUserInfo", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetPublicUserInfo failed: %s", err)
    }

    fmt.Printf("Public user info (email and location excluded): %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        {
            name: "Alice",
            age: 25,
            email: "alice@example.com",
            posts: 42,
            followers: 150,
            following: 89,
            location: "New York"
        },
        {
            name: "Bob",
            age: 30,
            email: "bob@example.com",
            posts: 28,
            followers: 203,
            following: 156,
            location: "San Francisco"
        },
        {
            name: "Charlie",
            age: 28,
            email: "charlie@example.com",
            posts: 67,
            followers: 89,
            following: 234,
            location: "London"
        }
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetPublicUserInfo", {});
    console.log("Public user info (email and location excluded):", result);
}

main().catch((err) => {
    console.error("GetPublicUserInfo query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com","posts":42,"followers":150,"following":89,"location":"New York"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com","posts":28,"followers":203,"following":156,"location":"San Francisco"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com","posts":67,"followers":89,"following":234,"location":"London"}'

curl -X POST \
  http://localhost:6969/GetPublicUserInfo \
  -H 'Content-Type: application/json' \
  -d '{}'

Property Remappings

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Simple Remappings  

Sometimes, you may want to rename a property that is returned from a traversal. You can access properties of an item by using the name of the property as defined in the schema.

::{new_name: property_name}
::{alias: _::{property}}

You can use the name directly, or if you want to be more explicit, or in cases where there may be name clashes, you can use the _:: operator.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Basic property remapping

QUERY GetUserWithAlias () =>
    users <- N<User>::RANGE(0, 5)
    RETURN users::{
        userID: ::ID,
        displayName: name
    }

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice Johnson", "age": 25, "email": "alice@example.com"},
    {"name": "Bob Smith", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie Brown", "age": 28, "email": "charlie@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetUserWithAlias", {})
print("Users with remapped properties:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice Johnson", 25, "alice@example.com"),
        ("Bob Smith", 30, "bob@example.com"),
        ("Charlie Brown", 28, "charlie@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUserWithAlias", &json!({})).await?;
    println!("Users with remapped properties: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice Johnson", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob Smith", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie Brown", "age": uint8(28), "email": "charlie@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUserWithAlias", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUserWithAlias failed: %s", err)
    }

    fmt.Printf("Users with remapped properties: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice Johnson", age: 25, email: "alice@example.com" },
        { name: "Bob Smith", age: 30, email: "bob@example.com" },
        { name: "Charlie Brown", age: 28, email: "charlie@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetUserWithAlias", {});
    console.log("Users with remapped properties:", result);
}

main().catch((err) => {
    console.error("GetUserWithAlias query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice Johnson","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob Smith","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie Brown","age":28,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/GetUserWithAlias \
  -H 'Content-Type: application/json' \
  -d '{}'

Nested Mappings  

::|item_name|{
    field: item_name::traversal,
    nested_field: other::{
        property: item_name::ID,
        ..
    }
}

ℹ️ Note

The important thing to note here, is that if we were to access the ID like we have shown previously: nested_field: ID. This ID would be the ID of the other item, not the item_name item due to the tighter scope.

⚠️ Warning

When accessing properties in nested mappings, scope matters. Use the explicit item_name::property syntax to access properties from outer scopes to avoid ambiguity.

Example 1: User posts with nested remappings

QUERY GetUserPosts (user_id: ID) =>
    user <- N<User>(user_id)
    posts <- user::Out<HasPost>
    RETURN user::|usr|{
        posts: posts::{
            postID: ID,
            creatorID: usr::ID,
            creatorName: usr::name,
            ..
        }
    }

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user

QUERY CreatePost (user_id: ID, title: String, content: String) =>
    user <- N<User>(user_id)
    post <- AddN<Post>({
        title: title,
        content: content
    })
    AddE<HasPost>::From(user)::To(post)
    RETURN post
N::User {
    name: String,
    age: U8,
    email: String
}

N::Post {
    title: String,
    content: String
}

E::HasPost {
    From: User,
    To: Post
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

user_result = client.query("CreateUser", {
    "name": "Alice Johnson",
    "age": 25,
    "email": "alice@example.com"
})
user_id = user_result[0]["user"]["id"]

posts = [
    {"title": "My First Post", "content": "This is my first blog post!"},
    {"title": "Learning GraphDB", "content": "Graph databases are fascinating."},
    {"title": "Weekend Plans", "content": "Planning to explore the city."},
]

for post in posts:
    client.query("CreatePost", {
        "user_id": user_id,
        "title": post["title"],
        "content": post["content"]
    })

result = client.query("GetUserPosts", {"user_id": user_id})
print("User posts with nested remappings:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let user_result: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice Johnson",
        "age": 25,
        "email": "alice@example.com"
    })).await?;
    let user_id = user_result["user"]["id"].as_str().unwrap();

    let posts = vec![
        ("My First Post", "This is my first blog post!"),
        ("Learning GraphDB", "Graph databases are fascinating."),
        ("Weekend Plans", "Planning to explore the city."),
    ];

    for (title, content) in &posts {
        let _post: serde_json::Value = client.query("CreatePost", &json!({
            "user_id": user_id,
            "title": title,
            "content": content
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUserPosts", &json!({
        "user_id": user_id
    })).await?;
    println!("User posts with nested remappings: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var userResult map[string]any
    if err := client.Query("CreateUser", helix.WithData(map[string]any{
        "name": "Alice Johnson",
        "age":  uint8(25),
        "email": "alice@example.com",
    })).Scan(&userResult); err != nil {
        log.Fatalf("CreateUser failed: %s", err)
    }
    userID := userResult["user"].(map[string]any)["id"].(string)

    posts := []map[string]string{
        {"title": "My First Post", "content": "This is my first blog post!"},
        {"title": "Learning GraphDB", "content": "Graph databases are fascinating."},
        {"title": "Weekend Plans", "content": "Planning to explore the city."},
    }

    for _, post := range posts {
        var postResult map[string]any
        if err := client.Query("CreatePost", helix.WithData(map[string]any{
            "user_id": userID,
            "title":   post["title"],
            "content": post["content"],
        })).Scan(&postResult); err != nil {
            log.Fatalf("CreatePost failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUserPosts", helix.WithData(map[string]any{
        "user_id": userID,
    })).Scan(&result); err != nil {
        log.Fatalf("GetUserPosts failed: %s", err)
    }

    fmt.Printf("User posts with nested remappings: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const userResult = await client.query("CreateUser", {
        name: "Alice Johnson",
        age: 25,
        email: "alice@example.com"
    });
    const userId = userResult.user.id;

    const posts = [
        { title: "My First Post", content: "This is my first blog post!" },
        { title: "Learning GraphDB", content: "Graph databases are fascinating." },
        { title: "Weekend Plans", content: "Planning to explore the city." },
    ];

    for (const post of posts) {
        await client.query("CreatePost", {
            user_id: userId,
            title: post.title,
            content: post.content
        });
    }

    const result = await client.query("GetUserPosts", { user_id: userId });
    console.log("User posts with nested remappings:", result);
}

main().catch((err) => {
    console.error("GetUserPosts query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice Johnson","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreatePost \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<user_id>","title":"My First Post","content":"This is my first blog post!"}'

curl -X POST \
  http://localhost:6969/CreatePost \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<user_id>","title":"Learning GraphDB","content":"Graph databases are fascinating."}'

curl -X POST \
  http://localhost:6969/CreatePost \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<user_id>","title":"Weekend Plans","content":"Planning to explore the city."}'

curl -X POST \
  http://localhost:6969/GetUserPosts \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<user_id>"}'

Vector search

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

How do I search for similar vectors in HelixDB?

Use SearchV to find vectors similar to your query vector using cosine similarity.

SearchV<Type>(vector, limit)

ℹ️ Note

Currently, Helix only supports using an array of F64 values to represent the vector. We will be adding support for different types such as F32, binary vectors and more in the very near future. Please reach out to us if you need a different vector type.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

QUERY SearchVector (vector: [F64], limit: I64) =>
    documents <- SearchV<Document>(vector, limit)
    RETURN documents

QUERY InsertVector (vector: [F64], content: String, created_at: Date) =>
    document <- AddV<Document>(vector, { content: content, created_at: created_at })
    RETURN document
V::Document {
    content: String,
    created_at: Date
}

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

vector_data = [0.1, 0.2, 0.3, 0.4]
inserted = client.query("InsertVector", {
    "vector": vector_data,
    "content": "Sample document content",
    "created_at": datetime.now(timezone.utc).isoformat(),
})

result = client.query("SearchVector", {
    "vector": vector_data,
    "limit": 10
})
print(result)
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let vector_data = vec![0.1, 0.2, 0.3, 0.4];

    let _inserted: serde_json::Value = client.query("InsertVector", &json!({
        "vector": vector_data,
        "content": "Sample document content",
        "created_at": Utc::now().to_rfc3339(),
    })).await?;

    let result: serde_json::Value = client.query("SearchVector", &json!({
        "vector": vector_data,
        "limit": 10,
    })).await?;

    println!("Search results: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    vectorData := []float64{0.1, 0.2, 0.3, 0.4}

    insertPayload := map[string]any{
        "vector":     vectorData,
        "content":    "Sample document content",
        "created_at": time.Now().UTC().Format(time.RFC3339),
    }

    var inserted map[string]any
    if err := client.Query("InsertVector", helix.WithData(insertPayload)).Scan(&inserted); err != nil {
        log.Fatalf("InsertVector failed: %s", err)
    }

    searchPayload := map[string]any{
        "vector": vectorData,
        "limit":  int64(10),
    }

    var result map[string]any
    if err := client.Query("SearchVector", helix.WithData(searchPayload)).Scan(&result); err != nil {
        log.Fatalf("SearchVector failed: %s", err)
    }

    fmt.Printf("Search results: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const vectorData = [0.1, 0.2, 0.3, 0.4];

    const inserted = await client.query("InsertVector", {
        vector: vectorData,
        content: "Sample document content",
        created_at: new Date().toISOString(),
    });

    const result = await client.query("SearchVector", {
        vector: vectorData,
        limit: 10,
    });

    console.log("Search results:", result);
}

main().catch((err) => {
    console.error("SearchVector query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertVector \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"content":"Sample document content","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/SearchVector \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"limit":10}'

Example 2: Vector search with postfiltering

QUERY SearchRecentDocuments (vector: [F64], limit: I64, cutoff_date: Date) =>
    documents <- SearchV<Document>(vector, limit)::WHERE(_::{created_at}::GTE(cutoff_date))
    RETURN documents

QUERY InsertVector (vector: [F64], content: String, created_at: Date) =>
    document <- AddV<Document>(vector, { content: content, created_at: created_at })
    RETURN document
V::Document {
    content: String,
    created_at: Date
}

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone, timedelta
from helix.client import Client

client = Client(local=True, port=6969)

vector_data = [0.12, 0.34, 0.56, 0.78]

recent_date = datetime.now(timezone.utc).isoformat()
old_date = (datetime.now(timezone.utc) - timedelta(days=45)).isoformat()

client.query("InsertVector", {
    "vector": vector_data,
    "content": "Recent document content",
    "created_at": recent_date,
})

client.query("InsertVector", {
    "vector": [0.15, 0.35, 0.55, 0.75],
    "content": "Old document content", 
    "created_at": old_date,
})

cutoff_date = (datetime.now(timezone.utc) - timedelta(days=30)).isoformat()

result = client.query("SearchRecentDocuments", {
    "vector": vector_data,
    "limit": 5,
    "cutoff_date": cutoff_date,
})

print(result)
use chrono::{Duration, Utc};
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let vector_data = vec![0.12, 0.34, 0.56, 0.78];

    let recent_date = Utc::now().to_rfc3339();
    let old_date = (Utc::now() - Duration::days(45)).to_rfc3339();

    let _recent: serde_json::Value = client.query("InsertVector", &json!({
        "vector": vector_data,
        "content": "Recent document content",
        "created_at": recent_date,
    })).await?;

    let _old: serde_json::Value = client.query("InsertVector", &json!({
        "vector": [0.15, 0.35, 0.55, 0.75],
        "content": "Old document content",
        "created_at": old_date,
    })).await?;

    let cutoff_date = (Utc::now() - Duration::days(30)).to_rfc3339();

    let result: serde_json::Value = client.query("SearchRecentDocuments", &json!({
        "vector": vector_data,
        "limit": 5,
        "cutoff_date": cutoff_date,
    })).await?;

    println!("Filtered search results: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    vectorData := []float64{0.12, 0.34, 0.56, 0.78}

    recentDate := time.Now().UTC().Format(time.RFC3339)
    oldDate := time.Now().UTC().AddDate(0, 0, -45).Format(time.RFC3339)

    recentPayload := map[string]any{
        "vector":     vectorData,
        "content":    "Recent document content",
        "created_at": recentDate,
    }
    var recent map[string]any
    if err := client.Query("InsertVector", helix.WithData(recentPayload)).Scan(&recent); err != nil {
        log.Fatalf("InsertVector (recent) failed: %s", err)
    }

    oldPayload := map[string]any{
        "vector":     []float64{0.15, 0.35, 0.55, 0.75},
        "content":    "Old document content",
        "created_at": oldDate,
    }
    var old map[string]any
    if err := client.Query("InsertVector", helix.WithData(oldPayload)).Scan(&old); err != nil {
        log.Fatalf("InsertVector (old) failed: %s", err)
    }

    cutoffDate := time.Now().UTC().AddDate(0, 0, -30).Format(time.RFC3339)

    searchPayload := map[string]any{
        "vector":      vectorData,
        "limit":       int64(5),
        "cutoff_date": cutoffDate,
    }

    var result map[string]any
    if err := client.Query("SearchRecentDocuments", helix.WithData(searchPayload)).Scan(&result); err != nil {
        log.Fatalf("SearchRecentDocuments failed: %s", err)
    }

    fmt.Printf("Filtered search results: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const vectorData = [0.12, 0.34, 0.56, 0.78];

    const recentDate = new Date().toISOString();
    const oldDate = new Date(Date.now() - 45 * 24 * 60 * 60 * 1000).toISOString();

    await client.query("InsertVector", {
        vector: vectorData,
        content: "Recent document content",
        created_at: recentDate,
    });

    await client.query("InsertVector", {
        vector: [0.15, 0.35, 0.55, 0.75],
        content: "Old document content",
        created_at: oldDate,
    });

    const cutoffDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();

    const result = await client.query("SearchRecentDocuments", {
        vector: vectorData,
        limit: 5,
        cutoff_date: cutoffDate,
    });

    console.log("Filtered search results:", result);
}

main().catch((err) => {
    console.error("SearchRecentDocuments query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertVector \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.12,0.34,0.56,0.78],"content":"Recent document content","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertVector \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.15,0.35,0.55,0.75],"content":"Old document content","created_at":"'"$(date -u -d '45 days ago' +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/SearchRecentDocuments \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.12,0.34,0.56,0.78],"limit":5,"cutoff_date":"'"$(date -u -d '30 days ago' +"%Y-%m-%dT%H:%M:%SZ")"'"}'

Example 3: Using the built in Embed function

You can also use the built in Embed function to search with text directly without sending in the array of floats. It uses the embedding model defined in your config.hx.json file.

⚠️ Warning

All vectors in a vector type must have the same dimensions. If you change your embedding model (e.g., switching from text-embedding-ada-002 to a different model), the new vectors will have different dimensions and will cause an error. Ensure you use the same embedding model consistently for all vectors.

QUERY SearchWithText (text: String, limit: I64) =>
    documents <- SearchV<Document>(Embed(text), limit)
    RETURN documents

QUERY InsertTextAsVector (content: String, created_at: Date) =>
    document <- AddV<Document>(Embed(content), { content: content, created_at: created_at })
    RETURN document
V::Document {
    content: String,
    created_at: Date
}
OPENAI_API_KEY=your_api_key

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

sample_texts = [
    "Introduction to machine learning algorithms",
    "Deep neural networks and AI",
    "Natural language processing techniques"
]

for text in sample_texts:
    client.query("InsertTextAsVector", {
        "content": text,
        "created_at": datetime.now(timezone.utc).isoformat(),
    })

result = client.query("SearchWithText", {
    "text": "machine learning algorithms",
    "limit": 10,
})

print(result)
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let sample_texts = vec![
        "Introduction to machine learning algorithms",
        "Deep neural networks and AI",
        "Natural language processing techniques"
    ];

    for text in &sample_texts {
        let _inserted: serde_json::Value = client.query("InsertTextAsVector", &json!({
            "content": text,
            "created_at": Utc::now().to_rfc3339(),
        })).await?;
    }

    let result: serde_json::Value = client.query("SearchWithText", &json!({
        "text": "machine learning algorithms",
        "limit": 10,
    })).await?;

    println!("Text search results: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    sampleTexts := []string{
        "Introduction to machine learning algorithms",
        "Deep neural networks and AI",
        "Natural language processing techniques",
    }

    for _, text := range sampleTexts {
        insertPayload := map[string]any{
            "content":    text,
            "created_at": time.Now().UTC().Format(time.RFC3339),
        }

        var inserted map[string]any
        if err := client.Query("InsertTextAsVector", helix.WithData(insertPayload)).Scan(&inserted); err != nil {
            log.Fatalf("InsertTextAsVector failed: %s", err)
        }
    }

    searchPayload := map[string]any{
        "text":  "machine learning algorithms",
        "limit": int64(10),
    }

    var result map[string]any
    if err := client.Query("SearchWithText", helix.WithData(searchPayload)).Scan(&result); err != nil {
        log.Fatalf("SearchWithText failed: %s", err)
    }

    fmt.Printf("Text search results: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const sampleTexts = [
        "Introduction to machine learning algorithms",
        "Deep neural networks and AI",
        "Natural language processing techniques"
    ];

    for (const text of sampleTexts) {
        await client.query("InsertTextAsVector", {
            content: text,
            created_at: new Date().toISOString(),
        });
    }

    const result = await client.query("SearchWithText", {
        text: "machine learning algorithms",
        limit: 10,
    });

    console.log("Text search results:", result);
}

main().catch((err) => {
    console.error("SearchWithText query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Introduction to machine learning algorithms","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Deep neural networks and AI","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Natural language processing techniques","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/SearchWithText \
  -H 'Content-Type: application/json' \
  -d '{"text":"machine learning algorithms","limit":10}'

Embedding Vectors

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Embed Text using Embed

Automatically embed text into vectors using your configured embedding model.

AddV<Type>(Embed(text))
AddV<Type>(Embed(text), {properties})
SearchV<Type>(Embed(text), limit)

ℹ️ Note

The text is automatically embedded with an embedding model of your choice (can be defined in your config.hx.json file). The default embedding model is text-embedding-ada-002 from OpenAI.

⚠️ Warning

Make sure to set your OPENAI_API_KEY environment variable with your API key in the same location as the queries.hx, schema.hx and config.hx.json files. When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

⚠️ Warning

All vectors in a vector type must have the same dimensions. If you change your embedding model (e.g., switching from text-embedding-ada-002 to a different model), the new vectors will have different dimensions and will cause an error. Ensure you use the same embedding model consistently for all vectors.

Example 1: Creating a vector from text

QUERY InsertTextAsVector (content: String, created_at: Date) =>
    document <- AddV<Document>(Embed(content), { content: content, created_at: created_at })
    RETURN document
V::Document {
    content: String,
    created_at: Date
}
OPENAI_API_KEY=your_api_key

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

print(client.query("InsertTextAsVector", {
    "content": "Machine learning is transforming the way we work.",
    "created_at": datetime.now(timezone.utc).isoformat(),
}))
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let payload = json!({
        "content": "Machine learning is transforming the way we work.",
        "created_at": Utc::now().to_rfc3339(),
    });

    let result: serde_json::Value = client.query("InsertTextAsVector", &payload).await?;
    println!("Created document vector: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    payload := map[string]any{
        "content":    "Machine learning is transforming the way we work.",
        "created_at": time.Now().UTC().Format(time.RFC3339),
    }

    var result map[string]any
    if err := client.Query("InsertTextAsVector", helix.WithData(payload)).Scan(&result); err != nil {
        log.Fatalf("InsertTextAsVector failed: %s", err)
    }

    fmt.Printf("Created document vector: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const result = await client.query("InsertTextAsVector", {
        content: "Machine learning is transforming the way we work.",
        created_at: new Date().toISOString(),
    });

    console.log("Created document vector:", result);
}

main().catch((err) => {
    console.error("InsertTextAsVector query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Machine learning is transforming the way we work.","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

Example 2: Searching with text embeddings

QUERY SearchWithText (query: String, limit: I64) =>
    documents <- SearchV<Document>(Embed(query), limit)
    RETURN documents

QUERY InsertTextAsVector (content: String, created_at: Date) =>
    document <- AddV<Document>(Embed(content), { content: content, created_at: created_at })
    RETURN document
V::Document {
    content: String,
    created_at: Date
}
OPENAI_API_KEY=your_api_key

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

sample_texts = [
    "Artificial intelligence is revolutionizing automation",
    "Machine learning algorithms for predictive analytics",
    "Deep learning applications in computer vision"
]

for text in sample_texts:
    client.query("InsertTextAsVector", {
        "content": text,
        "created_at": datetime.now(timezone.utc).isoformat(),
    })

result = client.query("SearchWithText", {
    "query": "artificial intelligence and automation",
    "limit": 5,
})

print(result)
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let sample_texts = vec![
        "Artificial intelligence is revolutionizing automation",
        "Machine learning algorithms for predictive analytics",
        "Deep learning applications in computer vision"
    ];

    for text in &sample_texts {
        let _inserted: serde_json::Value = client.query("InsertTextAsVector", &json!({
            "content": text,
            "created_at": Utc::now().to_rfc3339(),
        })).await?;
    }

    let result: serde_json::Value = client.query("SearchWithText", &json!({
        "query": "artificial intelligence and automation",
        "limit": 5,
    })).await?;

    println!("Search results: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    sampleTexts := []string{
        "Artificial intelligence is revolutionizing automation",
        "Machine learning algorithms for predictive analytics",
        "Deep learning applications in computer vision",
    }

    for _, text := range sampleTexts {
        insertPayload := map[string]any{
            "content":    text,
            "created_at": time.Now().UTC().Format(time.RFC3339),
        }

        var inserted map[string]any
        if err := client.Query("InsertTextAsVector", helix.WithData(insertPayload)).Scan(&inserted); err != nil {
            log.Fatalf("InsertTextAsVector failed: %s", err)
        }
    }

    searchPayload := map[string]any{
        "query": "artificial intelligence and automation",
        "limit": int64(5),
    }

    var result map[string]any
    if err := client.Query("SearchWithText", helix.WithData(searchPayload)).Scan(&result); err != nil {
        log.Fatalf("SearchWithText failed: %s", err)
    }

    fmt.Printf("Search results: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const sampleTexts = [
        "Artificial intelligence is revolutionizing automation",
        "Machine learning algorithms for predictive analytics",
        "Deep learning applications in computer vision"
    ];

    for (const text of sampleTexts) {
        await client.query("InsertTextAsVector", {
            content: text,
            created_at: new Date().toISOString(),
        });
    }

    const result = await client.query("SearchWithText", {
        query: "artificial intelligence and automation",
        limit: 5,
    });

    console.log("Search results:", result);
}

main().catch((err) => {
    console.error("SearchWithText query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Artificial intelligence is revolutionizing automation","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Machine learning algorithms for predictive analytics","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Deep learning applications in computer vision","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/SearchWithText \
  -H 'Content-Type: application/json' \
  -d '{"query":"artificial intelligence and automation","limit":5}'

Example 3: Creating a vector and connecting it to a user

QUERY CreateUserDocument (user_id: ID, content: String, created_at: Date) =>
    document <- AddV<Document>(Embed(content), { content: content, created_at: created_at })
    edge <- AddE<User_to_Document_Embedding>::From(user_id)::To(document)
    RETURN document

QUERY CreateUser (name: String, email: String) =>
    user <- AddN<User>({
        name: name,
        email: email
    })
    RETURN user
N::User {
    name: String,
    email: String
}

V::Document {
    content: String,
    created_at: Date
}

E::User_to_Document_Embedding {
    From: User,
    To: Document,
}
OPENAI_API_KEY=your_api_key

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

user = client.query("CreateUser", {
    "name": "Alice Johnson",
    "email": "alice@example.com",
})
user_id = user[0]["user"]["id"]

result = client.query("CreateUserDocument", {
    "user_id": user_id,
    "content": "This is my personal note about project planning.",
    "created_at": datetime.now(timezone.utc).isoformat(),
})

print(result)
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let user: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice Johnson",
        "email": "alice@example.com",
    })).await?;
    let user_id = user["user"]["id"].as_str().unwrap().to_string();

    let result: serde_json::Value = client.query("CreateUserDocument", &json!({
        "user_id": user_id,
        "content": "This is my personal note about project planning.",
        "created_at": Utc::now().to_rfc3339(),
    })).await?;

    println!("Created user document: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    userPayload := map[string]any{
        "name":  "Alice Johnson",
        "email": "alice@example.com",
    }

    var user map[string]any
    if err := client.Query("CreateUser", helix.WithData(userPayload)).Scan(&user); err != nil {
        log.Fatalf("CreateUser failed: %s", err)
    }

    userID := user["user"].(map[string]any)["id"].(string)

    docPayload := map[string]any{
        "user_id":    userID,
        "content":    "This is my personal note about project planning.",
        "created_at": time.Now().UTC().Format(time.RFC3339),
    }

    var result map[string]any
    if err := client.Query("CreateUserDocument", helix.WithData(docPayload)).Scan(&result); err != nil {
        log.Fatalf("CreateUserDocument failed: %s", err)
    }

    fmt.Printf("Created user document: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const user = await client.query("CreateUser", {
        name: "Alice Johnson",
        email: "alice@example.com",
    });
    const userId: string = user.user.id;

    const result = await client.query("CreateUserDocument", {
        user_id: userId,
        content: "This is my personal note about project planning.",
        created_at: new Date().toISOString(),
    });

    console.log("Created user document:", result);
}

main().catch((err) => {
    console.error("CreateUserDocument query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice Johnson","email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUserDocument \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"<user_id>","content":"This is my personal note about project planning.","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

Example 4: Semantic search with postfiltering

QUERY SearchRecentNotes (query: String, limit: I64, cutoff_date: Date) =>
    documents <- SearchV<Document>(Embed(query), limit)
                    ::WHERE(_::{created_at}::GTE(cutoff_date))
    RETURN documents

QUERY InsertTextAsVector (content: String, created_at: Date) =>
    document <- AddV<Document>(Embed(content), { content: content, created_at: created_at })
    RETURN document
V::Document {
    content: String,
    created_at: Date
}
OPENAI_API_KEY=your_api_key

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone, timedelta
from helix.client import Client

client = Client(local=True, port=6969)

recent_date = datetime.now(timezone.utc).isoformat()
old_date = (datetime.now(timezone.utc) - timedelta(days=10)).isoformat()

client.query("InsertTextAsVector", {
    "content": "Project milestone review scheduled for next week",
    "created_at": recent_date,
})

client.query("InsertTextAsVector", {
    "content": "Weekly status report from last month",
    "created_at": old_date,
})

cutoff_date = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat()

result = client.query("SearchRecentNotes", {
    "query": "project deadlines and milestones",
    "limit": 10,
    "cutoff_date": cutoff_date,
})

print(result)
use chrono::{Duration, Utc};
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let recent_date = Utc::now().to_rfc3339();
    let old_date = (Utc::now() - Duration::days(10)).to_rfc3339();

    let _recent: serde_json::Value = client.query("InsertTextAsVector", &json!({
        "content": "Project milestone review scheduled for next week",
        "created_at": recent_date,
    })).await?;

    let _old: serde_json::Value = client.query("InsertTextAsVector", &json!({
        "content": "Weekly status report from last month",
        "created_at": old_date,
    })).await?;

    let cutoff_date = (Utc::now() - Duration::days(7)).to_rfc3339();

    let result: serde_json::Value = client.query("SearchRecentNotes", &json!({
        "query": "project deadlines and milestones",
        "limit": 10,
        "cutoff_date": cutoff_date,
    })).await?;

    println!("Recent search results: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    recentDate := time.Now().UTC().Format(time.RFC3339)
    oldDate := time.Now().UTC().AddDate(0, 0, -10).Format(time.RFC3339)

    recentPayload := map[string]any{
        "content":    "Project milestone review scheduled for next week",
        "created_at": recentDate,
    }
    var recent map[string]any
    if err := client.Query("InsertTextAsVector", helix.WithData(recentPayload)).Scan(&recent); err != nil {
        log.Fatalf("InsertTextAsVector (recent) failed: %s", err)
    }

    oldPayload := map[string]any{
        "content":    "Weekly status report from last month",
        "created_at": oldDate,
    }
    var old map[string]any
    if err := client.Query("InsertTextAsVector", helix.WithData(oldPayload)).Scan(&old); err != nil {
        log.Fatalf("InsertTextAsVector (old) failed: %s", err)
    }

    cutoffDate := time.Now().UTC().AddDate(0, 0, -7).Format(time.RFC3339)

    searchPayload := map[string]any{
        "query":       "project deadlines and milestones",
        "limit":       int64(10),
        "cutoff_date": cutoffDate,
    }

    var result map[string]any
    if err := client.Query("SearchRecentNotes", helix.WithData(searchPayload)).Scan(&result); err != nil {
        log.Fatalf("SearchRecentNotes failed: %s", err)
    }

    fmt.Printf("Recent search results: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const recentDate = new Date().toISOString();
    const oldDate = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString();

    await client.query("InsertTextAsVector", {
        content: "Project milestone review scheduled for next week",
        created_at: recentDate,
    });

    await client.query("InsertTextAsVector", {
        content: "Weekly status report from last month",
        created_at: oldDate,
    });

    const cutoffDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();

    const result = await client.query("SearchRecentNotes", {
        query: "project deadlines and milestones",
        limit: 10,
        cutoff_date: cutoffDate,
    });

    console.log("Recent search results:", result);
}

main().catch((err) => {
    console.error("SearchRecentNotes query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Project milestone review scheduled for next week","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertTextAsVector \
  -H 'Content-Type: application/json' \
  -d '{"content":"Weekly status report from last month","created_at":"'"$(date -u -d '10 days ago' +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/SearchRecentNotes \
  -H 'Content-Type: application/json' \
  -d '{"query":"project deadlines and milestones","limit":10,"cutoff_date":"'"$(date -u -d '7 days ago' +"%Y-%m-%dT%H:%M:%SZ")"'"}'

Keyword Search

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Keyword Search using BM25 with SearchBM25  

Search for keywords in nodes using the BM25 ranking algorithm.

SearchBM25<Type>(text, limit)

ℹ️ Note

BM25 is a ranking function used for full-text search. It searches through the text properties of nodes and ranks results based on keyword relevance and frequency.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

QUERY SearchKeyword (keywords: String, limit: I64) =>
    documents <- SearchBM25<Document>(keywords, limit)
    RETURN documents

QUERY InsertDocument (content: String, created_at: Date) =>
    document <- AddN<Document>({ content: content, created_at: created_at })
    RETURN document
N::Document {
    content: String,
    created_at: Date
}

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone
from helix.client import Client

client = Client(local=True, port=6969)

sample_docs = [
    "Machine learning algorithms for data analysis",
    "Introduction to artificial intelligence and neural networks",
    "Database optimization techniques and performance tuning",
    "Web development with modern JavaScript frameworks"
]

for content in sample_docs:
    client.query("InsertDocument", {
        "content": content,
        "created_at": datetime.now(timezone.utc).isoformat(),
    })

result = client.query("SearchKeyword", {
    "keywords": "machine learning algorithms",
    "limit": 5
})

print(result)
use chrono::Utc;
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let sample_docs = vec![
        "Machine learning algorithms for data analysis",
        "Introduction to artificial intelligence and neural networks",
        "Database optimization techniques and performance tuning",
        "Web development with modern JavaScript frameworks"
    ];

    for content in &sample_docs {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "content": content,
            "created_at": Utc::now().to_rfc3339(),
        })).await?;
    }

    let result: serde_json::Value = client.query("SearchKeyword", &json!({
        "keywords": "machine learning algorithms",
        "limit": 5,
    })).await?;

    println!("Search results: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    sampleDocs := []string{
        "Machine learning algorithms for data analysis",
        "Introduction to artificial intelligence and neural networks",
        "Database optimization techniques and performance tuning",
        "Web development with modern JavaScript frameworks",
    }

    for _, content := range sampleDocs {
        insertPayload := map[string]any{
            "content":    content,
            "created_at": time.Now().UTC().Format(time.RFC3339),
        }

        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(insertPayload)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument failed: %s", err)
        }
    }

    searchPayload := map[string]any{
        "keywords": "machine learning algorithms",
        "limit":    int64(5),
    }

    var result map[string]any
    if err := client.Query("SearchKeyword", helix.WithData(searchPayload)).Scan(&result); err != nil {
        log.Fatalf("SearchKeyword failed: %s", err)
    }

    fmt.Printf("Search results: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const sampleDocs = [
        "Machine learning algorithms for data analysis",
        "Introduction to artificial intelligence and neural networks",
        "Database optimization techniques and performance tuning",
        "Web development with modern JavaScript frameworks"
    ];

    for (const content of sampleDocs) {
        await client.query("InsertDocument", {
            content: content,
            created_at: new Date().toISOString(),
        });
    }

    const result = await client.query("SearchKeyword", {
        keywords: "machine learning algorithms",
        limit: 5
    });

    console.log("Search results:", result);
}

main().catch((err) => {
    console.error("SearchKeyword query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"content":"Machine learning algorithms for data analysis","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"content":"Introduction to artificial intelligence and neural networks","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"content":"Database optimization techniques and performance tuning","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/SearchKeyword \
  -H 'Content-Type: application/json' \
  -d '{"keywords":"machine learning algorithms","limit":5}'

Example 2: Keyword search with postfiltering

QUERY SearchRecentKeywords (keywords: String, limit: I64, cutoff_date: Date) =>
    searched_docs <- SearchBM25<Document>(keywords, limit)
    documents <- searched_docs::WHERE(_::{created_at}::GTE(cutoff_date))
    RETURN documents

QUERY InsertDocument (content: String, created_at: Date) =>
    document <- AddN<Document>({ content: content, created_at: created_at })
    RETURN document
N::Document {
    content: String,
    created_at: Date
}

Here’s how to run the query using the SDKs or curl

from datetime import datetime, timezone, timedelta
from helix.client import Client

client = Client(local=True, port=6969)

recent_date = datetime.now(timezone.utc).isoformat()
old_date = (datetime.now(timezone.utc) - timedelta(days=15)).isoformat()

recent_docs = [
    "Modern machine learning techniques in 2024",
    "Latest artificial intelligence research papers"
]

for content in recent_docs:
    client.query("InsertDocument", {
        "content": content,
        "created_at": recent_date,
    })

old_docs = [
    "Traditional machine learning approaches from last year",
    "Historical AI development milestones"
]

for content in old_docs:
    client.query("InsertDocument", {
        "content": content,
        "created_at": old_date,
    })

cutoff_date = (datetime.now(timezone.utc) - timedelta(days=10)).isoformat()

result = client.query("SearchRecentKeywords", {
    "keywords": "machine learning artificial intelligence",
    "limit": 5,
    "cutoff_date": cutoff_date,
})

print(result)
use chrono::{Duration, Utc};
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let recent_date = Utc::now().to_rfc3339();
    let old_date = (Utc::now() - Duration::days(15)).to_rfc3339();

    let recent_docs = vec![
        "Modern machine learning techniques in 2024",
        "Latest artificial intelligence research papers"
    ];

    for content in &recent_docs {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "content": content,
            "created_at": recent_date,
        })).await?;
    }

    let old_docs = vec![
        "Traditional machine learning approaches from last year",
        "Historical AI development milestones"
    ];

    for content in &old_docs {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "content": content,
            "created_at": old_date,
        })).await?;
    }

    let cutoff_date = (Utc::now() - Duration::days(10)).to_rfc3339();

    let result: serde_json::Value = client.query("SearchRecentKeywords", &json!({
        "keywords": "machine learning artificial intelligence",
        "limit": 5,
        "cutoff_date": cutoff_date,
    })).await?;

    println!("Filtered search results: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    recentDate := time.Now().UTC().Format(time.RFC3339)
    oldDate := time.Now().UTC().AddDate(0, 0, -15).Format(time.RFC3339)

    recentDocs := []string{
        "Modern machine learning techniques in 2024",
        "Latest artificial intelligence research papers",
    }

    for _, content := range recentDocs {
        insertPayload := map[string]any{
            "content":    content,
            "created_at": recentDate,
        }
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(insertPayload)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument (recent) failed: %s", err)
        }
    }

    oldDocs := []string{
        "Traditional machine learning approaches from last year",
        "Historical AI development milestones",
    }

    for _, content := range oldDocs {
        insertPayload := map[string]any{
            "content":    content,
            "created_at": oldDate,
        }
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(insertPayload)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument (old) failed: %s", err)
        }
    }

    cutoffDate := time.Now().UTC().AddDate(0, 0, -10).Format(time.RFC3339)

    searchPayload := map[string]any{
        "keywords":    "machine learning artificial intelligence",
        "limit":       int64(5),
        "cutoff_date": cutoffDate,
    }

    var result map[string]any
    if err := client.Query("SearchRecentKeywords", helix.WithData(searchPayload)).Scan(&result); err != nil {
        log.Fatalf("SearchRecentKeywords failed: %s", err)
    }

    fmt.Printf("Filtered search results: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const recentDate = new Date().toISOString();
    const oldDate = new Date(Date.now() - 15 * 24 * 60 * 60 * 1000).toISOString();

    const recentDocs = [
        "Modern machine learning techniques in 2024",
        "Latest artificial intelligence research papers"
    ];

    for (const content of recentDocs) {
        await client.query("InsertDocument", {
            content: content,
            created_at: recentDate,
        });
    }

    const oldDocs = [
        "Traditional machine learning approaches from last year",
        "Historical AI development milestones"
    ];

    for (const content of oldDocs) {
        await client.query("InsertDocument", {
            content: content,
            created_at: oldDate,
        });
    }

    const cutoffDate = new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString();

    const result = await client.query("SearchRecentKeywords", {
        keywords: "machine learning artificial intelligence",
        limit: 5,
        cutoff_date: cutoffDate,
    });

    console.log("Filtered search results:", result);
}

main().catch((err) => {
    console.error("SearchRecentKeywords query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"content":"Modern machine learning techniques in 2024","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"content":"Latest artificial intelligence research papers","created_at":"'"$(date -u +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"content":"Traditional machine learning approaches from last year","created_at":"'"$(date -u -d '15 days ago' +"%Y-%m-%dT%H:%M:%SZ")"'"}'

curl -X POST \
  http://localhost:6969/SearchRecentKeywords \
  -H 'Content-Type: application/json' \
  -d '{"keywords":"machine learning artificial intelligence","limit":5,"cutoff_date":"'"$(date -u -d '10 days ago' +"%Y-%m-%dT%H:%M:%SZ")"'"}'

Rerankers Overview

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

What are Rerankers?

Rerankers are powerful post-processing operations that improve the quality and diversity of search results by reordering them after the initial retrieval phase. They enable you to:

  • Combine results from multiple search strategies (hybrid search)
  • Reduce redundancy by diversifying results
  • Optimize the relevance-diversity trade-off
  • Improve the overall user experience of your search application

When to Use Rerankers

Apply rerankers in your query pipeline when you need to:

  • Merge multiple search methods: Combine vector search with BM25 keyword search, or merge results from multiple vector searches
  • Diversify results: Eliminate near-duplicate content and show varied perspectives
  • Optimize ranking: Fine-tune the balance between relevance and variety based on your use case
  • Improve search quality: Leverage sophisticated ranking algorithms without changing your underlying search infrastructure

Available Rerankers

HelixQL provides two powerful reranking strategies:

RerankRRF (Reciprocal Rank Fusion)

A technique for combining multiple ranked lists without requiring score calibration. Perfect for hybrid search scenarios where you want to merge results from different search methods.

::RerankRRF             // Uses default k=60
::RerankRRF(k: 30.0)    // Custom k parameter

RerankMMR (Maximal Marginal Relevance)

A diversification technique that balances relevance with diversity to reduce redundancy. Ideal when you want to show varied results instead of similar or duplicate content.

::RerankMMR(lambda: 0.7)                           

Basic Usage Pattern

RRF Usage:

QUERY SearchDocuments(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankRRF           // Apply reranking
        ::RANGE(0, 10)          // Get top 10 results
    RETURN results

MMR Usage:

QUERY SearchDocuments(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankMMR(lambda: 0.7)  // Apply reranking
        ::RANGE(0, 10)            // Get top 10 results
    RETURN results

Chaining Rerankers

You can chain multiple rerankers together for complex result optimization:

QUERY AdvancedSearch(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 150)
        ::RerankRRF(k: 60)       // First: combine multiple rankings
        ::RerankMMR(lambda: 0.6) // Then: diversify results
        ::RANGE(0, 10)
    RETURN results

Best Practices

  1. Retrieve more results initially: Fetch 100-200 candidates to give rerankers sufficient options to work with
  2. Apply rerankers before RANGE: Rerank first, then limit the number of results returned
  3. Choose the right reranker: Use RRF for combining searches, MMR for diversification
  4. Test with your data: Experiment with different parameters to find what works best for your use case

Common Patterns

// Pattern 1: Simple diversification
SearchV<Document>(vec, 100)::RerankMMR(lambda: 0.7)::RANGE(0, 10)

// Pattern 2: Hybrid search fusion
SearchV<Document>(vec, 100)::RerankRRF::RANGE(0, 10)

// Pattern 3: Fusion + diversification
SearchV<Document>(vec, 150)::RerankRRF::RerankMMR(lambda: 0.6)::RANGE(0, 10)

RerankRRF

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Rerank with RRF (Reciprocal Rank Fusion)  

RerankRRF is a powerful technique for combining multiple ranked lists without requiring score calibration. It’s particularly effective for hybrid search scenarios where you want to merge results from different search methods like vector search and BM25 keyword search.

::RerankRRF             // Uses default k=60
::RerankRRF(k: 60)      // Custom k parameter

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

How It Works

RRF uses a simple but effective formula to combine rankings:

RRF_score(d) = Σ 1/(k + rank_i(d))

Where:

  • d is a document/result
  • rank_i(d) is the rank of document d in the i-th result list
  • k is a constant that controls the impact of ranking differences

The algorithm works by:

  1. Taking multiple ranked lists of results
  2. For each item, calculating its RRF score based on its positions across all lists
  3. Re-ranking items by their combined RRF scores
  4. Items that appear highly ranked in multiple lists get boosted

When to Use RerankRRF

Use RerankRRF when you want to:

  • Merge multiple search methods: Combine vector search with BM25, or multiple vector searches with different embeddings
  • Avoid score normalization: RRF doesn’t require calibrating or normalizing scores from different search systems
  • Boost consensus results: Items that rank highly across multiple search strategies will be prioritized
  • Implement hybrid search: Create a unified ranking from complementary search approaches

Parameters

k (optional, default: 60)

Controls how much weight is given to ranking position:

  • Higher values (e.g., 80-100): Reduce the impact of ranking differences, treat all highly-ranked items more equally
  • Lower values (e.g., 20-40): Emphasize top-ranked items more strongly, create sharper distinctions
  • Default (60): Provides a balanced approach suitable for most use cases

ℹ️ Note

Start with the default value of 60 and adjust based on your specific data distribution and requirements.

Example 1: Basic reranking with default parameters

QUERY SearchDocuments(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankRRF
        ::RANGE(0, 10)
    RETURN results

QUERY InsertDocument(vector: [F64], title: String, content: String) =>
    document <- AddV<Document>(vector, {
        title: title,
        content: content
    })
    RETURN document
V::Document {
    title: String,
    content: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

documents = [
    {"vector": [0.1, 0.2, 0.3, 0.4], "title": "Machine Learning Basics", "content": "Introduction to ML"},
    {"vector": [0.2, 0.3, 0.4, 0.5], "title": "Deep Learning", "content": "Neural networks explained"},
    {"vector": [0.15, 0.25, 0.35, 0.45], "title": "AI Applications", "content": "Real-world AI uses"},
]

for doc in documents:
    client.query("InsertDocument", doc)

query_vector = [0.12, 0.22, 0.32, 0.42]
results = client.query("SearchDocuments", {"query_vec": query_vector})
print("Search results:", results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let documents = vec![
        (vec![0.1, 0.2, 0.3, 0.4], "Machine Learning Basics", "Introduction to ML"),
        (vec![0.2, 0.3, 0.4, 0.5], "Deep Learning", "Neural networks explained"),
        (vec![0.15, 0.25, 0.35, 0.45], "AI Applications", "Real-world AI uses"),
    ];

    for (vector, title, content) in &documents {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "vector": vector,
            "title": title,
            "content": content,
        })).await?;
    }

    let query_vector = vec![0.12, 0.22, 0.32, 0.42];
    let results: serde_json::Value = client.query("SearchDocuments", &json!({
        "query_vec": query_vector,
    })).await?;

    println!("Search results: {results:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    documents := []map[string]any{
        {"vector": []float64{0.1, 0.2, 0.3, 0.4}, "title": "Machine Learning Basics", "content": "Introduction to ML"},
        {"vector": []float64{0.2, 0.3, 0.4, 0.5}, "title": "Deep Learning", "content": "Neural networks explained"},
        {"vector": []float64{0.15, 0.25, 0.35, 0.45}, "title": "AI Applications", "content": "Real-world AI uses"},
    }

    for _, doc := range documents {
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(doc)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument failed: %s", err)
        }
    }

    queryVector := []float64{0.12, 0.22, 0.32, 0.42}
    var results map[string]any
    if err := client.Query("SearchDocuments", helix.WithData(map[string]any{
        "query_vec": queryVector,
    })).Scan(&results); err != nil {
        log.Fatalf("SearchDocuments failed: %s", err)
    }

    fmt.Printf("Search results: %#v\n", results)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const documents = [
        { vector: [0.1, 0.2, 0.3, 0.4], title: "Machine Learning Basics", content: "Introduction to ML" },
        { vector: [0.2, 0.3, 0.4, 0.5], title: "Deep Learning", content: "Neural networks explained" },
        { vector: [0.15, 0.25, 0.35, 0.45], title: "AI Applications", content: "Real-world AI uses" },
    ];

    for (const doc of documents) {
        await client.query("InsertDocument", doc);
    }

    const queryVector = [0.12, 0.22, 0.32, 0.42];
    const results = await client.query("SearchDocuments", {
        query_vec: queryVector,
    });

    console.log("Search results:", results);
}

main().catch((err) => {
    console.error("SearchDocuments query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"title":"Machine Learning Basics","content":"Introduction to ML"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.2,0.3,0.4,0.5],"title":"Deep Learning","content":"Neural networks explained"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.15,0.25,0.35,0.45],"title":"AI Applications","content":"Real-world AI uses"}'

curl -X POST \
  http://localhost:6969/SearchDocuments \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.12,0.22,0.32,0.42]}'

Example 2: Custom k parameter for more aggressive reranking

QUERY SearchWithCustomK(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankRRF(k: 30.0)
        ::RANGE(0, 20)
    RETURN results

QUERY InsertDocument(vector: [F64], title: String, content: String) =>
    document <- AddV<Document>(vector, {
        title: title,
        content: content
    })
    RETURN document
V::Document {
    title: String,
    content: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

documents = [
    {"vector": [0.1, 0.2, 0.3, 0.4], "title": "Python Tutorial", "content": "Learn Python basics"},
    {"vector": [0.2, 0.3, 0.4, 0.5], "title": "Advanced Python", "content": "Python design patterns"},
    {"vector": [0.15, 0.25, 0.35, 0.45], "title": "Python for Data Science", "content": "Using pandas and numpy"},
]

for doc in documents:
    client.query("InsertDocument", doc)

query_vector = [0.12, 0.22, 0.32, 0.42]
results = client.query("SearchWithCustomK", {"query_vec": query_vector})
print("Search results with custom k:", results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let documents = vec![
        (vec![0.1, 0.2, 0.3, 0.4], "Python Tutorial", "Learn Python basics"),
        (vec![0.2, 0.3, 0.4, 0.5], "Advanced Python", "Python design patterns"),
        (vec![0.15, 0.25, 0.35, 0.45], "Python for Data Science", "Using pandas and numpy"),
    ];

    for (vector, title, content) in &documents {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "vector": vector,
            "title": title,
            "content": content,
        })).await?;
    }

    let query_vector = vec![0.12, 0.22, 0.32, 0.42];
    let results: serde_json::Value = client.query("SearchWithCustomK", &json!({
        "query_vec": query_vector,
    })).await?;

    println!("Search results with custom k: {results:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    documents := []map[string]any{
        {"vector": []float64{0.1, 0.2, 0.3, 0.4}, "title": "Python Tutorial", "content": "Learn Python basics"},
        {"vector": []float64{0.2, 0.3, 0.4, 0.5}, "title": "Advanced Python", "content": "Python design patterns"},
        {"vector": []float64{0.15, 0.25, 0.35, 0.45}, "title": "Python for Data Science", "content": "Using pandas and numpy"},
    }

    for _, doc := range documents {
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(doc)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument failed: %s", err)
        }
    }

    queryVector := []float64{0.12, 0.22, 0.32, 0.42}
    var results map[string]any
    if err := client.Query("SearchWithCustomK", helix.WithData(map[string]any{
        "query_vec": queryVector,
    })).Scan(&results); err != nil {
        log.Fatalf("SearchWithCustomK failed: %s", err)
    }

    fmt.Printf("Search results with custom k: %#v\n", results)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const documents = [
        { vector: [0.1, 0.2, 0.3, 0.4], title: "Python Tutorial", content: "Learn Python basics" },
        { vector: [0.2, 0.3, 0.4, 0.5], title: "Advanced Python", content: "Python design patterns" },
        { vector: [0.15, 0.25, 0.35, 0.45], title: "Python for Data Science", content: "Using pandas and numpy" },
    ];

    for (const doc of documents) {
        await client.query("InsertDocument", doc);
    }

    const queryVector = [0.12, 0.22, 0.32, 0.42];
    const results = await client.query("SearchWithCustomK", {
        query_vec: queryVector,
    });

    console.log("Search results with custom k:", results);
}

main().catch((err) => {
    console.error("SearchWithCustomK query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"title":"Python Tutorial","content":"Learn Python basics"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.2,0.3,0.4,0.5],"title":"Advanced Python","content":"Python design patterns"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.15,0.25,0.35,0.45],"title":"Python for Data Science","content":"Using pandas and numpy"}'

curl -X POST \
  http://localhost:6969/SearchWithCustomK \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.12,0.22,0.32,0.42]}'

Example 3: Dynamic k parameter from query input

QUERY FlexibleRerank(query_vec: [F64], k_value: F64) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankRRF(k: k_value)
        ::RANGE(0, 10)
    RETURN results

QUERY InsertDocument(vector: [F64], title: String, content: String) =>
    document <- AddV<Document>(vector, {
        title: title,
        content: content
    })
    RETURN document
V::Document {
    title: String,
    content: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

documents = [
    {"vector": [0.1, 0.2, 0.3, 0.4], "title": "Web Development", "content": "HTML, CSS, JavaScript"},
    {"vector": [0.2, 0.3, 0.4, 0.5], "title": "Backend Engineering", "content": "APIs and databases"},
    {"vector": [0.15, 0.25, 0.35, 0.45], "title": "DevOps", "content": "CI/CD and infrastructure"},
]

for doc in documents:
    client.query("InsertDocument", doc)

query_vector = [0.12, 0.22, 0.32, 0.42]

# Try with different k values
for k in [30.0, 60.0, 90.0]:
    results = client.query("FlexibleRerank", {
        "query_vec": query_vector,
        "k_value": k
    })
    print(f"Results with k={k}:", results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let documents = vec![
        (vec![0.1, 0.2, 0.3, 0.4], "Web Development", "HTML, CSS, JavaScript"),
        (vec![0.2, 0.3, 0.4, 0.5], "Backend Engineering", "APIs and databases"),
        (vec![0.15, 0.25, 0.35, 0.45], "DevOps", "CI/CD and infrastructure"),
    ];

    for (vector, title, content) in &documents {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "vector": vector,
            "title": title,
            "content": content,
        })).await?;
    }

    let query_vector = vec![0.12, 0.22, 0.32, 0.42];

    // Try with different k values
    for k in [30.0, 60.0, 90.0] {
        let results: serde_json::Value = client.query("FlexibleRerank", &json!({
            "query_vec": query_vector.clone(),
            "k_value": k,
        })).await?;
        println!("Results with k={k}: {results:#?}");
    }

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    documents := []map[string]any{
        {"vector": []float64{0.1, 0.2, 0.3, 0.4}, "title": "Web Development", "content": "HTML, CSS, JavaScript"},
        {"vector": []float64{0.2, 0.3, 0.4, 0.5}, "title": "Backend Engineering", "content": "APIs and databases"},
        {"vector": []float64{0.15, 0.25, 0.35, 0.45}, "title": "DevOps", "content": "CI/CD and infrastructure"},
    }

    for _, doc := range documents {
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(doc)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument failed: %s", err)
        }
    }

    queryVector := []float64{0.12, 0.22, 0.32, 0.42}

    // Try with different k values
    for _, k := range []float64{30.0, 60.0, 90.0} {
        var results map[string]any
        if err := client.Query("FlexibleRerank", helix.WithData(map[string]any{
            "query_vec": queryVector,
            "k_value":   k,
        })).Scan(&results); err != nil {
            log.Fatalf("FlexibleRerank failed: %s", err)
        }
        fmt.Printf("Results with k=%.1f: %#v\n", k, results)
    }
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const documents = [
        { vector: [0.1, 0.2, 0.3, 0.4], title: "Web Development", content: "HTML, CSS, JavaScript" },
        { vector: [0.2, 0.3, 0.4, 0.5], title: "Backend Engineering", content: "APIs and databases" },
        { vector: [0.15, 0.25, 0.35, 0.45], title: "DevOps", content: "CI/CD and infrastructure" },
    ];

    for (const doc of documents) {
        await client.query("InsertDocument", doc);
    }

    const queryVector = [0.12, 0.22, 0.32, 0.42];

    // Try with different k values
    for (const k of [30.0, 60.0, 90.0]) {
        const results = await client.query("FlexibleRerank", {
            query_vec: queryVector,
            k_value: k,
        });
        console.log(`Results with k=${k}:`, results);
    }
}

main().catch((err) => {
    console.error("FlexibleRerank query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"title":"Web Development","content":"HTML, CSS, JavaScript"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.2,0.3,0.4,0.5],"title":"Backend Engineering","content":"APIs and databases"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.15,0.25,0.35,0.45],"title":"DevOps","content":"CI/CD and infrastructure"}'

# Try with different k values
curl -X POST \
  http://localhost:6969/FlexibleRerank \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.12,0.22,0.32,0.42],"k_value":30.0}'

curl -X POST \
  http://localhost:6969/FlexibleRerank \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.12,0.22,0.32,0.42],"k_value":60.0}'

Best Practices

Retrieve Sufficient Candidates

Always fetch more results than you ultimately need to give RRF sufficient candidates to work with:

// Good: Fetch 100, return 10
SearchV<Document>(vec, 100)::RerankRRF::RANGE(0, 10)

// Not ideal: Fetch 10, return 10
SearchV<Document>(vec, 10)::RerankRRF::RANGE(0, 10)

Parameter Tuning

Start with the default k=60 and adjust based on your observations:

  • If results seem too similar, try a lower k (30-40) to emphasize top rankings
  • If you want more variety, try a higher k (80-100) to flatten differences
  • Test with real queries and evaluate result quality

Combining with Other Operations

RRF works well with filtering and other result operations:

QUERY AdvancedSearch(query_vec: [F64], min_score: F64) =>
    results <- SearchV<Document>(query_vec, 200)
        ::WHERE(_.distance::GT(min_score))  // Filter first
        ::RerankRRF                          // Then rerank
        ::RANGE(0, 20)                       // Finally limit
    RETURN results

Performance Considerations

RRF is computationally efficient with O(n) complexity, making it suitable for real-time applications. However:

  • Larger candidate sets require more processing
  • Balance result quality with query latency
  • Consider caching frequently-accessed results

Use Cases

Combine text search with visual similarity for product discovery:

SearchV<Product>(query_embedding, 100)::RerankRRF(k: 50)::RANGE(0, 20)

Document Retrieval

Merge semantic search with keyword matching for comprehensive document retrieval:

SearchV<Document>(semantic_vec, 150)::RerankRRF::RANGE(0, 10)

Content Recommendation

Blend multiple recommendation signals (user preferences, popularity, recency):

SearchV<Content>(user_profile_vec, 100)::RerankRRF(k: 70)::RANGE(0, 15)

RerankMMR

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Rerank with MMR (Maximal Marginal Relevance)  

RerankMMR is a diversification technique that balances relevance with diversity to reduce redundancy in search results. It iteratively selects results that are both relevant to the query and dissimilar to already-selected results, ensuring users see varied content instead of near-duplicates.

::RerankMMR(lambda: 0.7)                           // Cosine distance (default)
::RerankMMR(lambda: 0.5, distance: "euclidean")    // Euclidean distance
::RerankMMR(lambda: 0.6, distance: "dotproduct")   // Dot product distance

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

How It Works

MMR uses an iterative selection process with the following formula:

MMR = λ × Sim1(d, q) - (1-λ) × max(Sim2(d, d_i))

Where:

  • d is a candidate document
  • q is the query
  • d_i are already-selected documents
  • λ (lambda) controls the relevance vs. diversity trade-off
  • Sim1 measures relevance to the query
  • Sim2 measures similarity to selected documents

The algorithm works by:

  1. Starting with the most relevant result
  2. For each subsequent position, calculating MMR scores for remaining candidates
  3. Selecting the candidate with the highest MMR score (balancing relevance and novelty)
  4. Repeating until all positions are filled

When to Use RerankMMR

Use RerankMMR when you want to:

  • Diversify search results: Eliminate near-duplicate content and show varied perspectives
  • Reduce redundancy: Avoid showing multiple similar articles, products, or documents
  • Improve user experience: Provide comprehensive coverage rather than repetitive results
  • Balance exploration and relevance: Give users both highly relevant and exploratory options

Parameters

lambda (required)

A value between 0.0 and 1.0 that controls the relevance vs. diversity trade-off:

  • Higher values (0.7-1.0): Favor relevance over diversity

    • Results will be more similar to original ranking
    • Use when relevance is paramount
    • Minimal diversification
  • Lower values (0.0-0.3): Favor diversity over relevance

    • Results will be maximally varied
    • Use when avoiding redundancy is critical
    • May include less relevant but diverse results
  • Balanced values (0.4-0.6): Balance relevance and diversity

    • Good compromise for most use cases
    • Maintains reasonable relevance while reducing duplicates
  • Typical recommendation: Start with 0.5-0.7 for most applications

ℹ️ Note

The optimal lambda value depends on your specific use case. Test different values to find what works best for your users.

distance (optional, default: “cosine”)

The distance metric used for calculating similarity:

  • “cosine”: Cosine similarity (default)

    • Works well for normalized vectors
    • Common choice for text embeddings
    • Range: -1 to 1
  • “euclidean”: Euclidean distance

    • Works well for absolute distances
    • Sensitive to vector magnitude
    • Good for spatial data
  • “dotproduct”: Dot product similarity

    • Works well for unnormalized vectors
    • Computationally efficient
    • Considers vector magnitude

Example 1: Basic diversification with default cosine distance

QUERY DiverseSearch(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankMMR(lambda: 0.7)
        ::RANGE(0, 10)
    RETURN results

QUERY InsertDocument(vector: [F64], title: String, category: String) =>
    document <- AddV<Document>(vector, {
        title: title,
        category: category
    })
    RETURN document
V::Document {
    title: String,
    category: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

documents = [
    {"vector": [0.1, 0.2, 0.3, 0.4], "title": "Introduction to Python", "category": "Programming"},
    {"vector": [0.11, 0.21, 0.31, 0.41], "title": "Python Basics", "category": "Programming"},
    {"vector": [0.5, 0.6, 0.7, 0.8], "title": "Machine Learning Overview", "category": "AI"},
    {"vector": [0.51, 0.61, 0.71, 0.81], "title": "Deep Learning Intro", "category": "AI"},
    {"vector": [0.3, 0.4, 0.5, 0.6], "title": "Data Structures", "category": "Computer Science"},
]

for doc in documents:
    client.query("InsertDocument", doc)

query_vector = [0.12, 0.22, 0.32, 0.42]
results = client.query("DiverseSearch", {"query_vec": query_vector})
print("Diverse search results:", results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let documents = vec![
        (vec![0.1, 0.2, 0.3, 0.4], "Introduction to Python", "Programming"),
        (vec![0.11, 0.21, 0.31, 0.41], "Python Basics", "Programming"),
        (vec![0.5, 0.6, 0.7, 0.8], "Machine Learning Overview", "AI"),
        (vec![0.51, 0.61, 0.71, 0.81], "Deep Learning Intro", "AI"),
        (vec![0.3, 0.4, 0.5, 0.6], "Data Structures", "Computer Science"),
    ];

    for (vector, title, category) in &documents {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "vector": vector,
            "title": title,
            "category": category,
        })).await?;
    }

    let query_vector = vec![0.12, 0.22, 0.32, 0.42];
    let results: serde_json::Value = client.query("DiverseSearch", &json!({
        "query_vec": query_vector,
    })).await?;

    println!("Diverse search results: {results:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    documents := []map[string]any{
        {"vector": []float64{0.1, 0.2, 0.3, 0.4}, "title": "Introduction to Python", "category": "Programming"},
        {"vector": []float64{0.11, 0.21, 0.31, 0.41}, "title": "Python Basics", "category": "Programming"},
        {"vector": []float64{0.5, 0.6, 0.7, 0.8}, "title": "Machine Learning Overview", "category": "AI"},
        {"vector": []float64{0.51, 0.61, 0.71, 0.81}, "title": "Deep Learning Intro", "category": "AI"},
        {"vector": []float64{0.3, 0.4, 0.5, 0.6}, "title": "Data Structures", "category": "Computer Science"},
    }

    for _, doc := range documents {
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(doc)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument failed: %s", err)
        }
    }

    queryVector := []float64{0.12, 0.22, 0.32, 0.42}
    var results map[string]any
    if err := client.Query("DiverseSearch", helix.WithData(map[string]any{
        "query_vec": queryVector,
    })).Scan(&results); err != nil {
        log.Fatalf("DiverseSearch failed: %s", err)
    }

    fmt.Printf("Diverse search results: %#v\n", results)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const documents = [
        { vector: [0.1, 0.2, 0.3, 0.4], title: "Introduction to Python", category: "Programming" },
        { vector: [0.11, 0.21, 0.31, 0.41], title: "Python Basics", category: "Programming" },
        { vector: [0.5, 0.6, 0.7, 0.8], title: "Machine Learning Overview", category: "AI" },
        { vector: [0.51, 0.61, 0.71, 0.81], title: "Deep Learning Intro", category: "AI" },
        { vector: [0.3, 0.4, 0.5, 0.6], title: "Data Structures", category: "Computer Science" },
    ];

    for (const doc of documents) {
        await client.query("InsertDocument", doc);
    }

    const queryVector = [0.12, 0.22, 0.32, 0.42];
    const results = await client.query("DiverseSearch", {
        query_vec: queryVector,
    });

    console.log("Diverse search results:", results);
}

main().catch((err) => {
    console.error("DiverseSearch query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"title":"Introduction to Python","category":"Programming"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.11,0.21,0.31,0.41],"title":"Python Basics","category":"Programming"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.5,0.6,0.7,0.8],"title":"Machine Learning Overview","category":"AI"}'

curl -X POST \
  http://localhost:6969/DiverseSearch \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.12,0.22,0.32,0.42]}'

Example 2: High diversity with euclidean distance

QUERY HighDiversitySearch(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankMMR(lambda: 0.3, distance: "euclidean")
        ::RANGE(0, 15)
    RETURN results

QUERY InsertDocument(vector: [F64], title: String, category: String) =>
    document <- AddV<Document>(vector, {
        title: title,
        category: category
    })
    RETURN document
V::Document {
    title: String,
    category: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

documents = [
    {"vector": [0.1, 0.2, 0.3, 0.4], "title": "React Tutorial", "category": "Frontend"},
    {"vector": [0.12, 0.22, 0.32, 0.42], "title": "React Hooks", "category": "Frontend"},
    {"vector": [0.5, 0.6, 0.7, 0.8], "title": "Node.js Guide", "category": "Backend"},
    {"vector": [0.2, 0.3, 0.4, 0.5], "title": "Vue.js Basics", "category": "Frontend"},
]

for doc in documents:
    client.query("InsertDocument", doc)

query_vector = [0.11, 0.21, 0.31, 0.41]
results = client.query("HighDiversitySearch", {"query_vec": query_vector})
print("High diversity results:", results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let documents = vec![
        (vec![0.1, 0.2, 0.3, 0.4], "React Tutorial", "Frontend"),
        (vec![0.12, 0.22, 0.32, 0.42], "React Hooks", "Frontend"),
        (vec![0.5, 0.6, 0.7, 0.8], "Node.js Guide", "Backend"),
        (vec![0.2, 0.3, 0.4, 0.5], "Vue.js Basics", "Frontend"),
    ];

    for (vector, title, category) in &documents {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "vector": vector,
            "title": title,
            "category": category,
        })).await?;
    }

    let query_vector = vec![0.11, 0.21, 0.31, 0.41];
    let results: serde_json::Value = client.query("HighDiversitySearch", &json!({
        "query_vec": query_vector,
    })).await?;

    println!("High diversity results: {results:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    documents := []map[string]any{
        {"vector": []float64{0.1, 0.2, 0.3, 0.4}, "title": "React Tutorial", "category": "Frontend"},
        {"vector": []float64{0.12, 0.22, 0.32, 0.42}, "title": "React Hooks", "category": "Frontend"},
        {"vector": []float64{0.5, 0.6, 0.7, 0.8}, "title": "Node.js Guide", "category": "Backend"},
        {"vector": []float64{0.2, 0.3, 0.4, 0.5}, "title": "Vue.js Basics", "category": "Frontend"},
    }

    for _, doc := range documents {
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(doc)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument failed: %s", err)
        }
    }

    queryVector := []float64{0.11, 0.21, 0.31, 0.41}
    var results map[string]any
    if err := client.Query("HighDiversitySearch", helix.WithData(map[string]any{
        "query_vec": queryVector,
    })).Scan(&results); err != nil {
        log.Fatalf("HighDiversitySearch failed: %s", err)
    }

    fmt.Printf("High diversity results: %#v\n", results)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const documents = [
        { vector: [0.1, 0.2, 0.3, 0.4], title: "React Tutorial", category: "Frontend" },
        { vector: [0.12, 0.22, 0.32, 0.42], title: "React Hooks", category: "Frontend" },
        { vector: [0.5, 0.6, 0.7, 0.8], title: "Node.js Guide", category: "Backend" },
        { vector: [0.2, 0.3, 0.4, 0.5], title: "Vue.js Basics", category: "Frontend" },
    ];

    for (const doc of documents) {
        await client.query("InsertDocument", doc);
    }

    const queryVector = [0.11, 0.21, 0.31, 0.41];
    const results = await client.query("HighDiversitySearch", {
        query_vec: queryVector,
    });

    console.log("High diversity results:", results);
}

main().catch((err) => {
    console.error("HighDiversitySearch query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"title":"React Tutorial","category":"Frontend"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.5,0.6,0.7,0.8],"title":"Node.js Guide","category":"Backend"}'

curl -X POST \
  http://localhost:6969/HighDiversitySearch \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.11,0.21,0.31,0.41]}'

Example 3: Balanced approach with dot product

QUERY BalancedSearch(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankMMR(lambda: 0.5, distance: "dotproduct")
        ::RANGE(0, 10)
    RETURN results

QUERY InsertDocument(vector: [F64], title: String, category: String) =>
    document <- AddV<Document>(vector, {
        title: title,
        category: category
    })
    RETURN document
V::Document {
    title: String,
    category: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

documents = [
    {"vector": [0.1, 0.2, 0.3, 0.4], "title": "Database Design", "category": "Architecture"},
    {"vector": [0.11, 0.21, 0.31, 0.41], "title": "SQL Optimization", "category": "Database"},
    {"vector": [0.5, 0.6, 0.7, 0.8], "title": "NoSQL Systems", "category": "Database"},
    {"vector": [0.3, 0.4, 0.5, 0.6], "title": "API Design", "category": "Architecture"},
]

for doc in documents:
    client.query("InsertDocument", doc)

query_vector = [0.12, 0.22, 0.32, 0.42]
results = client.query("BalancedSearch", {"query_vec": query_vector})
print("Balanced search results:", results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let documents = vec![
        (vec![0.1, 0.2, 0.3, 0.4], "Database Design", "Architecture"),
        (vec![0.11, 0.21, 0.31, 0.41], "SQL Optimization", "Database"),
        (vec![0.5, 0.6, 0.7, 0.8], "NoSQL Systems", "Database"),
        (vec![0.3, 0.4, 0.5, 0.6], "API Design", "Architecture"),
    ];

    for (vector, title, category) in &documents {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "vector": vector,
            "title": title,
            "category": category,
        })).await?;
    }

    let query_vector = vec![0.12, 0.22, 0.32, 0.42];
    let results: serde_json::Value = client.query("BalancedSearch", &json!({
        "query_vec": query_vector,
    })).await?;

    println!("Balanced search results: {results:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    documents := []map[string]any{
        {"vector": []float64{0.1, 0.2, 0.3, 0.4}, "title": "Database Design", "category": "Architecture"},
        {"vector": []float64{0.11, 0.21, 0.31, 0.41}, "title": "SQL Optimization", "category": "Database"},
        {"vector": []float64{0.5, 0.6, 0.7, 0.8}, "title": "NoSQL Systems", "category": "Database"},
        {"vector": []float64{0.3, 0.4, 0.5, 0.6}, "title": "API Design", "category": "Architecture"},
    }

    for _, doc := range documents {
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(doc)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument failed: %s", err)
        }
    }

    queryVector := []float64{0.12, 0.22, 0.32, 0.42}
    var results map[string]any
    if err := client.Query("BalancedSearch", helix.WithData(map[string]any{
        "query_vec": queryVector,
    })).Scan(&results); err != nil {
        log.Fatalf("BalancedSearch failed: %s", err)
    }

    fmt.Printf("Balanced search results: %#v\n", results)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const documents = [
        { vector: [0.1, 0.2, 0.3, 0.4], title: "Database Design", category: "Architecture" },
        { vector: [0.11, 0.21, 0.31, 0.41], title: "SQL Optimization", category: "Database" },
        { vector: [0.5, 0.6, 0.7, 0.8], title: "NoSQL Systems", category: "Database" },
        { vector: [0.3, 0.4, 0.5, 0.6], title: "API Design", category: "Architecture" },
    ];

    for (const doc of documents) {
        await client.query("InsertDocument", doc);
    }

    const queryVector = [0.12, 0.22, 0.32, 0.42];
    const results = await client.query("BalancedSearch", {
        query_vec: queryVector,
    });

    console.log("Balanced search results:", results);
}

main().catch((err) => {
    console.error("BalancedSearch query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"title":"Database Design","category":"Architecture"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.5,0.6,0.7,0.8],"title":"NoSQL Systems","category":"Database"}'

curl -X POST \
  http://localhost:6969/BalancedSearch \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.12,0.22,0.32,0.42]}'

Example 4: Dynamic lambda for user-controlled diversity

QUERY CustomDiversitySearch(query_vec: [F64], diversity: F64) =>
    results <- SearchV<Document>(query_vec, 100)
        ::RerankMMR(lambda: diversity)
        ::RANGE(0, 10)
    RETURN results

QUERY InsertDocument(vector: [F64], title: String, category: String) =>
    document <- AddV<Document>(vector, {
        title: title,
        category: category
    })
    RETURN document
V::Document {
    title: String,
    category: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

documents = [
    {"vector": [0.1, 0.2, 0.3, 0.4], "title": "Cloud Computing", "category": "Infrastructure"},
    {"vector": [0.11, 0.21, 0.31, 0.41], "title": "AWS Services", "category": "Cloud"},
    {"vector": [0.5, 0.6, 0.7, 0.8], "title": "Docker Containers", "category": "DevOps"},
    {"vector": [0.3, 0.4, 0.5, 0.6], "title": "Kubernetes", "category": "Orchestration"},
]

for doc in documents:
    client.query("InsertDocument", doc)

query_vector = [0.12, 0.22, 0.32, 0.42]

# Try different diversity levels
for diversity_level in [0.3, 0.5, 0.7, 0.9]:
    results = client.query("CustomDiversitySearch", {
        "query_vec": query_vector,
        "diversity": diversity_level
    })
    print(f"Results with diversity={diversity_level}:", results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let documents = vec![
        (vec![0.1, 0.2, 0.3, 0.4], "Cloud Computing", "Infrastructure"),
        (vec![0.11, 0.21, 0.31, 0.41], "AWS Services", "Cloud"),
        (vec![0.5, 0.6, 0.7, 0.8], "Docker Containers", "DevOps"),
        (vec![0.3, 0.4, 0.5, 0.6], "Kubernetes", "Orchestration"),
    ];

    for (vector, title, category) in &documents {
        let _inserted: serde_json::Value = client.query("InsertDocument", &json!({
            "vector": vector,
            "title": title,
            "category": category,
        })).await?;
    }

    let query_vector = vec![0.12, 0.22, 0.32, 0.42];

    // Try different diversity levels
    for diversity in [0.3, 0.5, 0.7, 0.9] {
        let results: serde_json::Value = client.query("CustomDiversitySearch", &json!({
            "query_vec": query_vector.clone(),
            "diversity": diversity,
        })).await?;
        println!("Results with diversity={diversity}: {results:#?}");
    }

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    documents := []map[string]any{
        {"vector": []float64{0.1, 0.2, 0.3, 0.4}, "title": "Cloud Computing", "category": "Infrastructure"},
        {"vector": []float64{0.11, 0.21, 0.31, 0.41}, "title": "AWS Services", "category": "Cloud"},
        {"vector": []float64{0.5, 0.6, 0.7, 0.8}, "title": "Docker Containers", "category": "DevOps"},
        {"vector": []float64{0.3, 0.4, 0.5, 0.6}, "title": "Kubernetes", "category": "Orchestration"},
    }

    for _, doc := range documents {
        var inserted map[string]any
        if err := client.Query("InsertDocument", helix.WithData(doc)).Scan(&inserted); err != nil {
            log.Fatalf("InsertDocument failed: %s", err)
        }
    }

    queryVector := []float64{0.12, 0.22, 0.32, 0.42}

    // Try different diversity levels
    for _, diversity := range []float64{0.3, 0.5, 0.7, 0.9} {
        var results map[string]any
        if err := client.Query("CustomDiversitySearch", helix.WithData(map[string]any{
            "query_vec": queryVector,
            "diversity": diversity,
        })).Scan(&results); err != nil {
            log.Fatalf("CustomDiversitySearch failed: %s", err)
        }
        fmt.Printf("Results with diversity=%.1f: %#v\n", diversity, results)
    }
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const documents = [
        { vector: [0.1, 0.2, 0.3, 0.4], title: "Cloud Computing", category: "Infrastructure" },
        { vector: [0.11, 0.21, 0.31, 0.41], title: "AWS Services", category: "Cloud" },
        { vector: [0.5, 0.6, 0.7, 0.8], title: "Docker Containers", category: "DevOps" },
        { vector: [0.3, 0.4, 0.5, 0.6], title: "Kubernetes", category: "Orchestration" },
    ];

    for (const doc of documents) {
        await client.query("InsertDocument", doc);
    }

    const queryVector = [0.12, 0.22, 0.32, 0.42];

    // Try different diversity levels
    for (const diversity of [0.3, 0.5, 0.7, 0.9]) {
        const results = await client.query("CustomDiversitySearch", {
            query_vec: queryVector,
            diversity: diversity,
        });
        console.log(`Results with diversity=${diversity}:`, results);
    }
}

main().catch((err) => {
    console.error("CustomDiversitySearch query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.1,0.2,0.3,0.4],"title":"Cloud Computing","category":"Infrastructure"}'

curl -X POST \
  http://localhost:6969/InsertDocument \
  -H 'Content-Type: application/json' \
  -d '{"vector":[0.5,0.6,0.7,0.8],"title":"Docker Containers","category":"DevOps"}'

# Try different diversity levels
curl -X POST \
  http://localhost:6969/CustomDiversitySearch \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.12,0.22,0.32,0.42],"diversity":0.7}'

curl -X POST \
  http://localhost:6969/CustomDiversitySearch \
  -H 'Content-Type: application/json' \
  -d '{"query_vec":[0.12,0.22,0.32,0.42],"diversity":0.3}'

Chaining with RerankRRF

Combine RRF and MMR for hybrid search with diversification:

QUERY HybridDiverseSearch(query_vec: [F64]) =>
    results <- SearchV<Document>(query_vec, 150)
        ::RerankRRF(k: 60)           // First: combine rankings
        ::RerankMMR(lambda: 0.6)      // Then: diversify
        ::RANGE(0, 10)
    RETURN results

QUERY InsertDocument(vector: [F64], title: String, category: String) =>
    document <- AddV<Document>(vector, {
        title: title,
        category: category
    })
    RETURN document
V::Document {
    title: String,
    category: String
}

Best Practices

Retrieve Sufficient Candidates

MMR works best with a larger pool of candidates:

// Good: Fetch 100-200, return 10-20
SearchV<Document>(vec, 100)::RerankMMR(lambda: 0.7)::RANGE(0, 10)

// Not ideal: Fetch 10, return 10
SearchV<Document>(vec, 10)::RerankMMR(lambda: 0.7)::RANGE(0, 10)

Lambda Parameter Tuning

Start with these guidelines and adjust based on your results:

  • News/Articles: 0.5-0.6 (balance coverage and relevance)
  • E-commerce: 0.6-0.7 (favor relevance, some variety)
  • Content Discovery: 0.3-0.5 (favor diversity)
  • FAQ/Support: 0.7-0.9 (favor relevance)

Choosing Distance Metrics

  • Text embeddings: Use “cosine” (default)
  • Image embeddings: Use “cosine” or “euclidean”
  • Custom vectors: Test all three and compare results

Combining with Filtering

Apply filters before reranking for better performance:

QUERY FilteredDiverseSearch(query_vec: [F64], category: String) =>
    results <- SearchV<Document>(query_vec, 200)
        ::WHERE(_::{category}::EQ(category))  // Filter first
        ::RerankMMR(lambda: 0.6)               // Then diversify
        ::RANGE(0, 15)
    RETURN results

Performance Considerations

ℹ️ Note

MMR has O(n²) complexity due to pairwise comparisons. For optimal performance:

  • Limit the candidate set (e.g., 100-200 results)
  • Consider caching for frequently-accessed queries
  • Monitor query latency and adjust candidate size accordingly

Key performance factors:

  • Candidate set size: Larger sets increase computation time quadratically
  • Distance metric: Dot product is fastest, euclidean is slowest
  • Result count: More results require more iterations

Troubleshooting

Results seem too similar

Problem: Results still show near-duplicates

Solutions:

  • Lower lambda value (try 0.3-0.5)
  • Increase candidate pool size
  • Verify your vectors capture meaningful differences

Results seem irrelevant

Problem: Diverse results but poor relevance

Solutions:

  • Increase lambda value (try 0.7-0.9)
  • Ensure initial search retrieves quality candidates
  • Consider using RRF before MMR

Performance issues

Problem: Queries are too slow

Solutions:

  • Reduce candidate pool size
  • Use dot product distance metric
  • Return fewer final results
  • Consider caching popular queries

Use Cases

News and Media

Diversify news articles to show varied perspectives:

SearchV<Article>(query_vec, 100)::RerankMMR(lambda: 0.5)::RANGE(0, 10)

E-commerce

Show varied product categories while maintaining relevance:

SearchV<Product>(query_vec, 150)::RerankMMR(lambda: 0.65)::RANGE(0, 20)

Content Discovery

Maximize exploration with diverse recommendations:

SearchV<Content>(user_vec, 200)::RerankMMR(lambda: 0.4, distance: "euclidean")::RANGE(0, 15)

Document Retrieval

Balance comprehensive coverage with relevance:

SearchV<Document>(query_vec, 100)::RerankMMR(lambda: 0.6)::RANGE(0, 10)

Traversals From Nodes

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

The Idea

HelixQL is strongly typed, so you can only traverse edges and access properties that exist in your schema. Select starting points with N or E, then apply traversal steps to walk the graph safely.

::Out   Outgoing Nodes

Return the nodes reached via outgoing edges of a given type.

::Out<EdgeType>

Example 1: Listing who a user follows

QUERY GetUserFollowing (user_id: ID) =>
    following <- N<User>(user_id)::Out<Follows>
    RETURN following

QUERY CreateUser (name: String, handle: String) =>
    user <- AddN<User>({
        name: name,
        handle: handle,
    })
    RETURN user

QUERY FollowUser (follower_id: ID, followed_id: ID, since: Date) =>
    follow_edge <- AddE<Follows>({
        since: since
    })::From(follower_id)::To(followed_id)
    RETURN follow_edge
N::User {
    name: String,
    handle: String,
}

E::Follows {
    From: User,
    To: User,
    Properties: {
        since: Date
    }
}
from helix.client import Client
from datetime import datetime, timezone

client = Client(local=True, port=6969)
since_value = datetime.now(timezone.utc).isoformat()

alice = client.query("CreateUser", {"name": "Alice", "handle": "alice"})
bob = client.query("CreateUser", {"name": "Bob", "handle": "bobby"})

alice_id = alice[0]["user"]["id"]
bob_id = bob[0]["user"]["id"]

client.query("FollowUser", {
    "follower_id": alice_id,
    "followed_id": bob_id,
    "since": since_value,
})

result = client.query("GetUserFollowing", {"user_id": alice_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use chrono::Utc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);
    let since_value = Utc::now().to_rfc3339();

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "handle": "alice",
    })).await?;
    let bob: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Bob",
        "handle": "bobby",
    })).await?;

    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();
    let bob_id = bob["user"]["id"].as_str().unwrap().to_string();

    client.query::<_, serde_json::Value>("FollowUser", &json!({
        "follower_id": alice_id,
        "followed_id": bob_id,
        "since": since_value,
    })).await?;

    let result: serde_json::Value = client.query("GetUserFollowing", &json!({
        "user_id": alice_id,
    })).await?;
    println!("GetUserFollowing result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")
    sinceValue := time.Now().UTC().Format(time.RFC3339)

    var alice map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Alice", "handle": "alice"}),
    ).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }

    var bob map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Bob", "handle": "bobby"}),
    ).Scan(&bob); err != nil {
        log.Fatalf("CreateUser (Bob) failed: %s", err)
    }

    aliceID := alice["user"].(map[string]any)["id"].(string)
    bobID := bob["user"].(map[string]any)["id"].(string)

    if err := client.Query("FollowUser",
        helix.WithData(map[string]any{
            "follower_id": aliceID,
            "followed_id": bobID,
            "since":       sinceValue,
        }),
    ).Scan(&map[string]any{}); err != nil {
        log.Fatalf("FollowUser failed: %s", err)
    }

    var result map[string]any
    if err := client.Query("GetUserFollowing",
        helix.WithData(map[string]any{"user_id": aliceID}),
    ).Scan(&result); err != nil {
        log.Fatalf("GetUserFollowing failed: %s", err)
    }

    fmt.Printf("GetUserFollowing result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");
    const sinceValue = new Date().toISOString();

    const alice = await client.query("CreateUser", {
        name: "Alice",
        handle: "alice",
    });
    const bob = await client.query("CreateUser", {
        name: "Bob",
        handle: "bobby",
    });

    await client.query("FollowUser", {
        follower_id: alice.user.id,
        followed_id: bob.user.id,
        since: sinceValue,
    });

    const result = await client.query("GetUserFollowing", {
        user_id: alice.user.id,
    });

    console.log("GetUserFollowing result:", result);
}

main().catch((err) => {
    console.error("GetUserFollowing query failed:", err);
});
alice=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","handle":"alice"}')
alice_id=$(echo "$alice" | jq -r '.user.id')

bob=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","handle":"bobby"}')
bob_id=$(echo "$bob" | jq -r '.user.id')

since=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

curl -X POST \
  http://localhost:6969/FollowUser \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"'"$alice_id"'","followed_id":"'"$bob_id"'","since":"'"$since"'"}'

curl -X POST \
  http://localhost:6969/GetUserFollowing \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"'"$alice_id"'"}'

::In   Incoming Nodes

Return the nodes that point to the current selection via incoming edges of the given type.

::In<EdgeType>

Example 1: Listing who follows a user

QUERY GetUserFollowers (user_id: ID) =>
    followers <- N<User>(user_id)::In<Follows>
    RETURN followers

QUERY CreateUser (name: String, handle: String) =>
    user <- AddN<User>({
        name: name,
        handle: handle,
    })
    RETURN user

QUERY FollowUser (follower_id: ID, followed_id: ID, since: Date) =>
    follow_edge <- AddE<Follows>({
        since: since
    })::From(follower_id)::To(followed_id)
    RETURN follow_edge
N::User {
    name: String,
    handle: String,
}

E::Follows {
    From: User,
    To: User,
    Properties: {
        since: Date
    }
}
from helix.client import Client
from datetime import datetime, timezone

client = Client(local=True, port=6969)
since_value = datetime.now(timezone.utc).isoformat()

alice = client.query("CreateUser", {"name": "Alice", "handle": "alice"})
bob = client.query("CreateUser", {"name": "Bob", "handle": "bobby"})

alice_id = alice[0]["user"]["id"]
bob_id = bob[0]["user"]["id"]

client.query("FollowUser", {
    "follower_id": alice_id,
    "followed_id": bob_id,
    "since": since_value,
})

result = client.query("GetUserFollowers", {"user_id": bob_id})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use chrono::Utc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);
    let since_value = Utc::now().to_rfc3339();

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "handle": "alice",
    })).await?;
    let bob: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Bob",
        "handle": "bobby",
    })).await?;

    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();
    let bob_id = bob["user"]["id"].as_str().unwrap().to_string();

    client.query::<_, serde_json::Value>("FollowUser", &json!({
        "follower_id": alice_id,
        "followed_id": bob_id,
        "since": since_value,
    })).await?;

    let result: serde_json::Value = client.query("GetUserFollowers", &json!({
        "user_id": bob_id,
    })).await?;
    println!("GetUserFollowers result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")
    sinceValue := time.Now().UTC().Format(time.RFC3339)

    var alice map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Alice", "handle": "alice"}),
    ).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }

    var bob map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Bob", "handle": "bobby"}),
    ).Scan(&bob); err != nil {
        log.Fatalf("CreateUser (Bob) failed: %s", err)
    }

    aliceID := alice["user"].(map[string]any)["id"].(string)
    bobID := bob["user"].(map[string]any)["id"].(string)

    if err := client.Query("FollowUser",
        helix.WithData(map[string]any{
            "follower_id": aliceID,
            "followed_id": bobID,
            "since":       sinceValue,
        }),
    ).Scan(&map[string]any{}); err != nil {
        log.Fatalf("FollowUser failed: %s", err)
    }

    var result map[string]any
    if err := client.Query("GetUserFollowers",
        helix.WithData(map[string]any{"user_id": bobID}),
    ).Scan(&result); err != nil {
        log.Fatalf("GetUserFollowers failed: %s", err)
    }

    fmt.Printf("GetUserFollowers result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");
    const sinceValue = new Date().toISOString();

    const alice = await client.query("CreateUser", {
        name: "Alice",
        handle: "alice",
    });
    const bob = await client.query("CreateUser", {
        name: "Bob",
        handle: "bobby",
    });

    await client.query("FollowUser", {
        follower_id: alice.user.id,
        followed_id: bob.user.id,
        since: sinceValue,
    });

    const result = await client.query("GetUserFollowers", {
        user_id: bob.user.id,
    });

    console.log("GetUserFollowers result:", result);
}

main().catch((err) => {
    console.error("GetUserFollowers query failed:", err);
});
alice=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","handle":"alice"}')
alice_id=$(echo "$alice" | jq -r '.user.id')

bob=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","handle":"bobby"}')
bob_id=$(echo "$bob" | jq -r '.user.id')

since=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

curl -X POST \
  http://localhost:6969/FollowUser \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"'"$alice_id"'","followed_id":"'"$bob_id"'","since":"'"$since"'"}'

curl -X POST \
  http://localhost:6969/GetUserFollowers \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"'"$bob_id"'"}'

::OutE   Outgoing Edges

Return the outgoing edges themselves (including properties and endpoints) for the given edge type.

::OutE<EdgeType>

Example 1: Inspecting follow relationships

QUERY GetFollowingEdges (user_id: ID) =>
    follow_edges <- N<User>(user_id)::OutE<Follows>
    RETURN follow_edges

QUERY CreateUser (name: String, handle: String) =>
    user <- AddN<User>({
        name: name,
        handle: handle,
    })
    RETURN user

QUERY FollowUser (follower_id: ID, followed_id: ID, since: Date) =>
    follow_edge <- AddE<Follows>({
        since: since
    })::From(follower_id)::To(followed_id)
    RETURN follow_edge
N::User {
    name: String,
    handle: String,
}

E::Follows {
    From: User,
    To: User,
    Properties: {
        since: Date
    }
}
from helix.client import Client
from datetime import datetime, timezone

client = Client(local=True, port=6969)
since_value = datetime.now(timezone.utc).isoformat()

alice = client.query("CreateUser", {"name": "Alice", "handle": "alice"})
bob = client.query("CreateUser", {"name": "Bob", "handle": "bobby"})

alice_id = alice[0]["user"]["id"]
bob_id = bob[0]["user"]["id"]

client.query("FollowUser", {
    "follower_id": alice_id,
    "followed_id": bob_id,
    "since": since_value,
})

edges = client.query("GetFollowingEdges", {"user_id": alice_id})
print(edges)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use chrono::Utc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);
    let since_value = Utc::now().to_rfc3339();

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "handle": "alice",
    })).await?;
    let bob: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Bob",
        "handle": "bobby",
    })).await?;

    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();
    let bob_id = bob["user"]["id"].as_str().unwrap().to_string();

    client.query::<_, serde_json::Value>("FollowUser", &json!({
        "follower_id": alice_id,
        "followed_id": bob_id,
        "since": since_value,
    })).await?;

    let edges: serde_json::Value = client.query("GetFollowingEdges", &json!({
        "user_id": alice_id,
    })).await?;
    println!("GetFollowingEdges result: {edges:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")
    sinceValue := time.Now().UTC().Format(time.RFC3339)

    var alice map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Alice", "handle": "alice"}),
    ).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }

    var bob map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Bob", "handle": "bobby"}),
    ).Scan(&bob); err != nil {
        log.Fatalf("CreateUser (Bob) failed: %s", err)
    }

    aliceID := alice["user"].(map[string]any)["id"].(string)
    bobID := bob["user"].(map[string]any)["id"].(string)

    if err := client.Query("FollowUser",
        helix.WithData(map[string]any{
            "follower_id": aliceID,
            "followed_id": bobID,
            "since":       sinceValue,
        }),
    ).Scan(&map[string]any{}); err != nil {
        log.Fatalf("FollowUser failed: %s", err)
    }

    var edges map[string]any
    if err := client.Query("GetFollowingEdges",
        helix.WithData(map[string]any{"user_id": aliceID}),
    ).Scan(&edges); err != nil {
        log.Fatalf("GetFollowingEdges failed: %s", err)
    }

    fmt.Printf("GetFollowingEdges result: %#v\n", edges)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");
    const sinceValue = new Date().toISOString();

    const alice = await client.query("CreateUser", {
        name: "Alice",
        handle: "alice",
    });
    const bob = await client.query("CreateUser", {
        name: "Bob",
        handle: "bobby",
    });

    await client.query("FollowUser", {
        follower_id: alice.user.id,
        followed_id: bob.user.id,
        since: sinceValue,
    });

    const edges = await client.query("GetFollowingEdges", {
        user_id: alice.user.id,
    });

    console.log("GetFollowingEdges result:", edges);
}

main().catch((err) => {
    console.error("GetFollowingEdges query failed:", err);
});
alice=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","handle":"alice"}')
alice_id=$(echo "$alice" | jq -r '.user.id')

bob=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","handle":"bobby"}')
bob_id=$(echo "$bob" | jq -r '.user.id')

since=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

curl -X POST \
  http://localhost:6969/FollowUser \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"'"$alice_id"'","followed_id":"'"$bob_id"'","since":"'"$since"'"}'

curl -X POST \
  http://localhost:6969/GetFollowingEdges \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"'"$alice_id"'"}'

::InE   Incoming Edges

Return incoming edges (with properties and endpoints) for the given type.

::InE<EdgeType>

Example 1: Inspecting who followed a user

QUERY GetFollowerEdges (user_id: ID) =>
    follow_edges <- N<User>(user_id)::InE<Follows>
    RETURN follow_edges

QUERY CreateUser (name: String, handle: String) =>
    user <- AddN<User>({
        name: name,
        handle: handle,
    })
    RETURN user

QUERY FollowUser (follower_id: ID, followed_id: ID, since: Date) =>
    follow_edge <- AddE<Follows>({
        since: since
    })::From(follower_id)::To(followed_id)
    RETURN follow_edge
N::User {
    name: String,
    handle: String,
}

E::Follows {
    From: User,
    To: User,
    Properties: {
        since: Date
    }
}
from helix.client import Client
from datetime import datetime, timezone

client = Client(local=True, port=6969)
since_value = datetime.now(timezone.utc).isoformat()

alice = client.query("CreateUser", {"name": "Alice", "handle": "alice"})
bob = client.query("CreateUser", {"name": "Bob", "handle": "bobby"})

alice_id = alice[0]["user"]["id"]
bob_id = bob[0]["user"]["id"]

client.query("FollowUser", {
    "follower_id": alice_id,
    "followed_id": bob_id,
    "since": since_value,
})

edges = client.query("GetFollowerEdges", {"user_id": bob_id})
print(edges)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use chrono::Utc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);
    let since_value = Utc::now().to_rfc3339();

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "handle": "alice",
    })).await?;
    let bob: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Bob",
        "handle": "bobby",
    })).await?;

    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();
    let bob_id = bob["user"]["id"].as_str().unwrap().to_string();

    client.query::<_, serde_json::Value>("FollowUser", &json!({
        "follower_id": alice_id,
        "followed_id": bob_id,
        "since": since_value,
    })).await?;

    let edges: serde_json::Value = client.query("GetFollowerEdges", &json!({
        "user_id": bob_id,
    })).await?;
    println!("GetFollowerEdges result: {edges:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")
    sinceValue := time.Now().UTC().Format(time.RFC3339)

    var alice map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Alice", "handle": "alice"}),
    ).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }

    var bob map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Bob", "handle": "bobby"}),
    ).Scan(&bob); err != nil {
        log.Fatalf("CreateUser (Bob) failed: %s", err)
    }

    aliceID := alice["user"].(map[string]any)["id"].(string)
    bobID := bob["user"].(map[string]any)["id"].(string)

    if err := client.Query("FollowUser",
        helix.WithData(map[string]any{
            "follower_id": aliceID,
            "followed_id": bobID,
            "since":       sinceValue,
        }),
    ).Scan(&map[string]any{}); err != nil {
        log.Fatalf("FollowUser failed: %s", err)
    }

    var edges map[string]any
    if err := client.Query("GetFollowerEdges",
        helix.WithData(map[string]any{"user_id": bobID}),
    ).Scan(&edges); err != nil {
        log.Fatalf("GetFollowerEdges failed: %s", err)
    }

    fmt.Printf("GetFollowerEdges result: %#v\n", edges)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");
    const sinceValue = new Date().toISOString();

    const alice = await client.query("CreateUser", {
        name: "Alice",
        handle: "alice",
    });
    const bob = await client.query("CreateUser", {
        name: "Bob",
        handle: "bobby",
    });

    await client.query("FollowUser", {
        follower_id: alice.user.id,
        followed_id: bob.user.id,
        since: sinceValue,
    });

    const edges = await client.query("GetFollowerEdges", {
        user_id: bob.user.id,
    });

    console.log("GetFollowerEdges result:", edges);
}

main().catch((err) => {
    console.error("GetFollowerEdges query failed:", err);
});
alice=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","handle":"alice"}')
alice_id=$(echo "$alice" | jq -r '.user.id')

bob=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","handle":"bobby"}')
bob_id=$(echo "$bob" | jq -r '.user.id')

since=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

curl -X POST \
  http://localhost:6969/FollowUser \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"'"$alice_id"'","followed_id":"'"$bob_id"'","since":"'"$since"'"}'

curl -X POST \
  http://localhost:6969/GetFollowerEdges \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"'"$bob_id"'"}'

ℹ️ Note

Combine traversal steps with property filtering or aggregation for richer results. See the Property Access and Aggregations guides.

Traversals From Edges

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

The Idea

When you have an edge, you can traverse to its connected endpoints. Use ::FromN or ::FromV to get the source node or vector, and ::ToN or ::ToV to get the destination node or vector.

::FromN   Source Node

Return the node that the edge originates from.

::FromN

Example 1: Getting the user from a document creation edge

QUERY GetCreatorFromEdge (creation_id: ID) =>
    creator <- E<Creates>(creation_id)::FromN
    RETURN creator

QUERY CreateUser (name: String, email: String) =>
    user <- AddN<User>({
        name: name,
        email: email,
    })
    RETURN user

QUERY CreateDocument (user_id: ID, content: String, vector: [F64]) =>
    document <- AddV<Document>(vector, {
        content: content
    })
    creation_edge <- AddE<Creates>::From(user_id)::To(document)
    RETURN creation_edge
N::User {
    name: String,
    email: String,
}

V::Document {
    content: String
}

E::Creates {
    From: User,
    To: Document
}
from helix.client import Client

client = Client(local=True, port=6969)

alice = client.query("CreateUser", {"name": "Alice", "email": "alice@example.com"})
alice_id = alice[0]["user"]["id"]

creation = client.query("CreateDocument", {
    "user_id": alice_id,
    "content": "This is my first document",
    "vector": [0.1, 0.2, 0.3, 0.4, 0.5],
})
creation_id = creation[0]["creation_edge"]["id"]

result = client.query("GetCreatorFromEdge", {
    "creation_id": creation_id,
})
print(result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "email": "alice@example.com",
    })).await?;
    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();

    let creation: serde_json::Value = client.query("CreateDocument", &json!({
        "user_id": alice_id,
        "content": "This is my first document",
        "vector": [0.1, 0.2, 0.3, 0.4, 0.5],
    })).await?;
    let creation_id = creation["creation_edge"]["id"].as_str().unwrap().to_string();

    let result: serde_json::Value = client.query("GetCreatorFromEdge", &json!({
        "creation_id": creation_id,
    })).await?;
    println!("GetCreatorFromEdge result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var alice map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Alice", "email": "alice@example.com"}),
    ).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }

    aliceID := alice["user"].(map[string]any)["id"].(string)

    var creation map[string]any
    if err := client.Query("CreateDocument",
        helix.WithData(map[string]any{
            "user_id": aliceID,
            "content": "This is my first document",
            "vector": []float64{0.1, 0.2, 0.3, 0.4, 0.5},
        }),
    ).Scan(&creation); err != nil {
        log.Fatalf("CreateDocument failed: %s", err)
    }

    creationID := creation["creation_edge"].(map[string]any)["id"].(string)

    var result map[string]any
    if err := client.Query("GetCreatorFromEdge",
        helix.WithData(map[string]any{"creation_id": creationID}),
    ).Scan(&result); err != nil {
        log.Fatalf("GetCreatorFromEdge failed: %s", err)
    }

    fmt.Printf("GetCreatorFromEdge result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const alice = await client.query("CreateUser", {
        name: "Alice",
        email: "alice@example.com",
    });

    const creation = await client.query("CreateDocument", {
        user_id: alice.user.id,
        content: "This is my first document",
        vector: [0.1, 0.2, 0.3, 0.4, 0.5],
    });

    const result = await client.query("GetCreatorFromEdge", {
        creation_id: creation.creation_edge.id,
    });

    console.log("GetCreatorFromEdge result:", result);
}

main().catch((err) => {
    console.error("GetCreatorFromEdge query failed:", err);
});
alice=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","email":"alice@example.com"}')
alice_id=$(echo "$alice" | jq -r '.user.id')

creation=$(curl -s -X POST \
  http://localhost:6969/CreateDocument \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"'"$alice_id"'","content":"This is my first document","vector":[0.1,0.2,0.3,0.4,0.5]}')
creation_id=$(echo "$creation" | jq -r '.creation_edge.id')

curl -s -X POST \
  http://localhost:6969/GetCreatorFromEdge \
  -H 'Content-Type: application/json' \
  -d '{"creation_id":"'"$creation_id"'"}'

::FromV   Source Vector

Return the vector that the edge originates from.

::FromV

Example 1: Getting the source document from a mentions edge

QUERY GetMentionSource (mention_id: ID) =>
    source_doc <- E<MentionsUser>(mention_id)::FromV
    RETURN source_doc

QUERY CreateUser (name: String, email: String) =>
    user <- AddN<User>({
        name: name,
        email: email,
    })
    RETURN user

QUERY CreateDocument (content: String, vector: [F64]) =>
    document <- AddV<Document>(vector, {
        content: content
    })
    RETURN document

QUERY LinkMention (document_id: ID, user_id: ID) =>
    mention_edge <- AddE<MentionsUser>::From(document_id)::To(user_id)
    RETURN mention_edge
V::Document {
    content: String
}

N::User {
    name: String,
    email: String,
}

E::MentionsUser {
    From: Document,
    To: User
}
from helix.client import Client

client = Client(local=True, port=6969)

alice = client.query("CreateUser", {"name": "Alice", "email": "alice@example.com"})
alice_id = alice[0]["user"]["id"]

document = client.query("CreateDocument", {
    "content": "This document mentions @alice",
    "vector": [0.1, 0.2, 0.3, 0.4, 0.5],
})
document_id = document[0]["document"]["id"]

mention = client.query("LinkMention", {
    "document_id": document_id,
    "user_id": alice_id,
})
mention_id = mention[0]["mention_edge"]["id"]

source = client.query("GetMentionSource", {
    "mention_id": mention_id,
})
print(source)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "email": "alice@example.com",
    })).await?;
    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();

    let document: serde_json::Value = client.query("CreateDocument", &json!({
        "content": "This document mentions @alice",
        "vector": [0.1, 0.2, 0.3, 0.4, 0.5],
    })).await?;
    let document_id = document["document"]["id"].as_str().unwrap().to_string();

    let mention: serde_json::Value = client.query("LinkMention", &json!({
        "document_id": document_id,
        "user_id": alice_id,
    })).await?;
    let mention_id = mention["mention_edge"]["id"].as_str().unwrap().to_string();

    let source: serde_json::Value = client.query("GetMentionSource", &json!({
        "mention_id": mention_id,
    })).await?;
    println!("GetMentionSource result: {source:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var alice map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Alice", "email": "alice@example.com"}),
    ).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }

    aliceID := alice["user"].(map[string]any)["id"].(string)

    var document map[string]any
    if err := client.Query("CreateDocument",
        helix.WithData(map[string]any{
            "content": "This document mentions @alice",
            "vector": []float64{0.1, 0.2, 0.3, 0.4, 0.5},
        }),
    ).Scan(&document); err != nil {
        log.Fatalf("CreateDocument failed: %s", err)
    }

    documentID := document["document"].(map[string]any)["id"].(string)

    var mention map[string]any
    if err := client.Query("LinkMention",
        helix.WithData(map[string]any{
            "document_id": documentID,
            "user_id": aliceID,
        }),
    ).Scan(&mention); err != nil {
        log.Fatalf("LinkMention failed: %s", err)
    }

    mentionID := mention["mention_edge"].(map[string]any)["id"].(string)

    var source map[string]any
    if err := client.Query("GetMentionSource",
        helix.WithData(map[string]any{"mention_id": mentionID}),
    ).Scan(&source); err != nil {
        log.Fatalf("GetMentionSource failed: %s", err)
    }

    fmt.Printf("GetMentionSource result: %#v\n", source)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const alice = await client.query("CreateUser", {
        name: "Alice",
        email: "alice@example.com",
    });

    const document = await client.query("CreateDocument", {
        content: "This document mentions @alice",
        vector: [0.1, 0.2, 0.3, 0.4, 0.5],
    });

    const mention = await client.query("LinkMention", {
        document_id: document.document.id,
        user_id: alice.user.id,
    });

    const source = await client.query("GetMentionSource", {
        mention_id: mention.mention_edge.id,
    });

    console.log("GetMentionSource result:", source);
}

main().catch((err) => {
    console.error("GetMentionSource query failed:", err);
});
alice=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","email":"alice@example.com"}')
alice_id=$(echo "$alice" | jq -r '.user.id')

document=$(curl -s -X POST \
  http://localhost:6969/CreateDocument \
  -H 'Content-Type: application/json' \
  -d '{"content":"This document mentions @alice","vector":[0.1,0.2,0.3,0.4,0.5]}')
document_id=$(echo "$document" | jq -r '.document.id')

mention=$(curl -s -X POST \
  http://localhost:6969/LinkMention \
  -H 'Content-Type: application/json' \
  -d '{"document_id":"'"$document_id"'","user_id":"'"$alice_id"'"}')
mention_id=$(echo "$mention" | jq -r '.mention_edge.id')

curl -s -X POST \
  http://localhost:6969/GetMentionSource \
  -H 'Content-Type: application/json' \
  -d '{"mention_id":"'"$mention_id"'"}'

::ToN   Destination Node

Return the node that the edge points to.

::ToN

Example 1: Getting the followed user from a follow edge

QUERY GetFollowedUser (follow_id: ID) =>
    followed_user <- E<Follows>(follow_id)::ToN
    RETURN followed_user

QUERY CreateUser (name: String, email: String) =>
    user <- AddN<User>({
        name: name,
        email: email,
    })
    RETURN user

QUERY FollowUser (follower_id: ID, followed_id: ID, since: String) =>
    follow_edge <- AddE<Follows>::From(follower_id)::To(followed_id)
    RETURN follow_edge
N::User {
    name: String,
    email: String,
}

E::Follows {
    From: User,
    To: User
}
from helix.client import Client
from datetime import datetime

client = Client(local=True, port=6969)

alice = client.query("CreateUser", {"name": "Alice", "email": "alice@example.com"})
alice_id = alice[0]["user"]["id"]

bob = client.query("CreateUser", {"name": "Bob", "email": "bob@example.com"})
bob_id = bob[0]["user"]["id"]

since_value = datetime.now().isoformat()

follow = client.query("FollowUser", {
    "follower_id": alice_id,
    "followed_id": bob_id,
    "since": since_value,
})
follow_id = follow[0]["follow_edge"]["id"]

followed_user = client.query("GetFollowedUser", {
    "follow_id": follow_id,
})
print(followed_user)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use chrono::Utc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);
    let since_value = Utc::now().to_rfc3339();

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "email": "alice@example.com",
    })).await?;
    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();

    let bob: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Bob",
        "email": "bob@example.com",
    })).await?;
    let bob_id = bob["user"]["id"].as_str().unwrap().to_string();

    let follow: serde_json::Value = client.query("FollowUser", &json!({
        "follower_id": alice_id,
        "followed_id": bob_id,
        "since": since_value,
    })).await?;
    let follow_id = follow["follow_edge"]["id"].as_str().unwrap().to_string();

    let followed_user: serde_json::Value = client.query("GetFollowedUser", &json!({
        "follow_id": follow_id,
    })).await?;
    println!("GetFollowedUser result: {followed_user:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")
    sinceValue := time.Now().UTC().Format(time.RFC3339)

    var alice map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Alice", "email": "alice@example.com"}),
    ).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }

    var bob map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Bob", "email": "bob@example.com"}),
    ).Scan(&bob); err != nil {
        log.Fatalf("CreateUser (Bob) failed: %s", err)
    }

    aliceID := alice["user"].(map[string]any)["id"].(string)
    bobID := bob["user"].(map[string]any)["id"].(string)

    var follow map[string]any
    if err := client.Query("FollowUser",
        helix.WithData(map[string]any{
            "follower_id": aliceID,
            "followed_id": bobID,
            "since": sinceValue,
        }),
    ).Scan(&follow); err != nil {
        log.Fatalf("FollowUser failed: %s", err)
    }

    followID := follow["follow_edge"].(map[string]any)["id"].(string)

    var followedUser map[string]any
    if err := client.Query("GetFollowedUser",
        helix.WithData(map[string]any{"follow_id": followID}),
    ).Scan(&followedUser); err != nil {
        log.Fatalf("GetFollowedUser failed: %s", err)
    }

    fmt.Printf("GetFollowedUser result: %#v\n", followedUser)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");
    const sinceValue = new Date().toISOString();

    const alice = await client.query("CreateUser", {
        name: "Alice",
        email: "alice@example.com",
    });

    const bob = await client.query("CreateUser", {
        name: "Bob",
        email: "bob@example.com",
    });

    const follow = await client.query("FollowUser", {
        follower_id: alice.user.id,
        followed_id: bob.user.id,
        since: sinceValue,
    });

    const followedUser = await client.query("GetFollowedUser", {
        follow_id: follow.follow_edge.id,
    });

    console.log("GetFollowedUser result:", followedUser);
}

main().catch((err) => {
    console.error("GetFollowedUser query failed:", err);
});
alice=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","email":"alice@example.com"}')
alice_id=$(echo "$alice" | jq -r '.user.id')

bob=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","email":"bob@example.com"}')
bob_id=$(echo "$bob" | jq -r '.user.id')

since=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

follow=$(curl -s -X POST \
  http://localhost:6969/FollowUser \
  -H 'Content-Type: application/json' \
  -d '{"follower_id":"'"$alice_id"'","followed_id":"'"$bob_id"'","since":"'"$since"'"}')
follow_id=$(echo "$follow" | jq -r '.follow_edge.id')

curl -s -X POST \
  http://localhost:6969/GetFollowedUser \
  -H 'Content-Type: application/json' \
  -d '{"follow_id":"'"$follow_id"'"}'

::ToV   Destination Vector

Return the vector that the edge points to.

::ToV

Example 1: Inspecting the document vector

QUERY GetDocumentVector (creation_id: ID) =>
    document_vector <- E<Creates>(creation_id)::ToV
    RETURN document_vector

QUERY CreateUser (name: String, email: String) =>
    user <- AddN<User>({
        name: name,
        email: email,
    })
    RETURN user

QUERY CreateDocument (user_id: ID, content: String, vector: [F64]) =>
    document <- AddV<Document>(vector, {
        content: content
    })
    creation_edge <- AddE<Creates>::From(user_id)::To(document)
    RETURN creation_edge
N::User {
    name: String,
    email: String,
}

V::Document {
    content: String
}

E::Creates {
    From: User,
    To: Document
}
from helix.client import Client

client = Client(local=True, port=6969)

alice = client.query("CreateUser", {"name": "Alice", "email": "alice@example.com"})
alice_id = alice[0]["user"]["id"]

creation = client.query("CreateDocument", {
    "user_id": alice_id,
    "content": "This is my first document",
    "vector": [0.1, 0.2, 0.3, 0.4, 0.5],
})
creation_id = creation[0]["creation_edge"]["id"]

vector = client.query("GetDocumentVector", {
    "creation_id": creation_id,
})
print(vector)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let alice: serde_json::Value = client.query("CreateUser", &json!({
        "name": "Alice",
        "email": "alice@example.com",
    })).await?;
    let alice_id = alice["user"]["id"].as_str().unwrap().to_string();

    let creation: serde_json::Value = client.query("CreateDocument", &json!({
        "user_id": alice_id,
        "content": "This is my first document",
        "vector": [0.1, 0.2, 0.3, 0.4, 0.5],
    })).await?;
    let creation_id = creation["creation_edge"]["id"].as_str().unwrap().to_string();

    let vector: serde_json::Value = client.query("GetDocumentVector", &json!({
        "creation_id": creation_id,
    })).await?;
    println!("GetDocumentVector result: {vector:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    var alice map[string]any
    if err := client.Query("CreateUser",
        helix.WithData(map[string]any{"name": "Alice", "email": "alice@example.com"}),
    ).Scan(&alice); err != nil {
        log.Fatalf("CreateUser (Alice) failed: %s", err)
    }

    aliceID := alice["user"].(map[string]any)["id"].(string)

    var creation map[string]any
    if err := client.Query("CreateDocument",
        helix.WithData(map[string]any{
            "user_id": aliceID,
            "content": "This is my first document",
            "vector": []float64{0.1, 0.2, 0.3, 0.4, 0.5},
        }),
    ).Scan(&creation); err != nil {
        log.Fatalf("CreateDocument failed: %s", err)
    }

    creationID := creation["creation_edge"].(map[string]any)["id"].(string)

    var vector map[string]any
    if err := client.Query("GetDocumentVector",
        helix.WithData(map[string]any{"creation_id": creationID}),
    ).Scan(&vector); err != nil {
        log.Fatalf("GetDocumentVector failed: %s", err)
    }

    fmt.Printf("GetDocumentVector result: %#v\n", vector)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const alice = await client.query("CreateUser", {
        name: "Alice",
        email: "alice@example.com",
    });

    const creation = await client.query("CreateDocument", {
        user_id: alice.user.id,
        content: "This is my first document",
        vector: [0.1, 0.2, 0.3, 0.4, 0.5],
    });

    const vector = await client.query("GetDocumentVector", {
        creation_id: creation.creation_edge.id,
    });

    console.log("GetDocumentVector result:", vector);
}

main().catch((err) => {
    console.error("GetDocumentVector query failed:", err);
});
alice=$(curl -s -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","email":"alice@example.com"}')
alice_id=$(echo "$alice" | jq -r '.user.id')

creation=$(curl -s -X POST \
  http://localhost:6969/CreateDocument \
  -H 'Content-Type: application/json' \
  -d '{"user_id":"'"$alice_id"'","content":"This is my first document","vector":[0.1,0.2,0.3,0.4,0.5]}')
creation_id=$(echo "$creation" | jq -r '.creation_edge.id')

curl -s -X POST \
  http://localhost:6969/GetDocumentVector \
  -H 'Content-Type: application/json' \
  -d '{"creation_id":"'"$creation_id"'"}'

ℹ️ Note

Combine edge traversals with property filtering or continue traversing from the destination. See the Property Access and Traversals From Nodes guides.

Shortest Path Algorithms

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Shortest Path Algorithms in Helix  

Helix provides three powerful algorithms for finding shortest paths between nodes in your graph. Each algorithm is optimized for different use cases and graph characteristics.

Available Algorithms

  • BFS — Unweighted shortest paths using breadth-first search

  • Dijkstra — Weighted shortest paths with custom weight calculations

  • A* — Heuristic-guided weighted shortest paths

Algorithm Comparison

AlgorithmBest ForGraph TypeComplexityCustomizable Weights
BFSHop count minimizationUnweightedO(V + E)No
DijkstraFlexible weight calculationsWeightedO((V + E) log V)Yes
A*Goal-directed searchWeighted + HeuristicO((V + E) log V)Yes

ℹ️ Note

All algorithms guarantee finding the optimal shortest path when used correctly. A* can be significantly faster than Dijkstra when a good heuristic is available.

When to Use Each Algorithm

Use BFS When:

  • All edges have equal weight (or no weight)
  • You want to minimize hop count
  • Finding social network distances
  • Analyzing friend-of-friend relationships
  • Simple connectivity analysis

Use Dijkstra When:

  • Edges have different weights (distance, cost, time, etc.)
  • You need custom weight calculations using properties
  • Combining multiple factors into path weights
  • No directional heuristic is available
  • Maximum flexibility is needed

Use A* When:

  • You have a goal-directed heuristic (e.g., geographic distance)
  • Graph has spatial properties
  • Need faster performance for long paths
  • Heuristic can guide the search effectively

Quick Examples

BFS: Unweighted Path

// Find shortest path by hop count
QUERY FindShortestPath(start_id: ID, end_id: ID) =>
    path <- N<City>(start_id)
        ::ShortestPathBFS<Road>
        ::To(end_id)
    RETURN path

Dijkstra: Weighted Path

// Find shortest path by distance
QUERY FindShortestRoute(start_id: ID, end_id: ID) =>
    path <- N<City>(start_id)
        ::ShortestPathDijkstras<Road>(_::{distance})
        ::To(end_id)
    RETURN path

A*: Heuristic-Guided Path

// Find shortest path with geographic heuristic
QUERY FindOptimalRoute(start_id: ID, end_id: ID) =>
    path <- N<City>(start_id)
        ::ShortestPathAStar<Road>(_::{distance}, "straight_line_distance")
        ::To(end_id)
    RETURN path

Custom Weight Expressions

Both Dijkstra and A* support sophisticated weight calculations that can reference:

  • Edge properties: _::{property_name} - Properties on the edge itself
  • Source node: _::FromN::{property_name} - Properties from the edge’s starting node
  • Destination node: _::ToN::{property_name} - Properties from the edge’s ending node
  • Mathematical operations: Combine properties with functions like ADD, MUL, POW, etc.

Example: Traffic-Aware Routing

// Weight = distance * source_traffic_factor
::ShortestPathDijkstras<Road>(MUL(_::{distance}, _::FromN::{traffic_factor}))

Example: Multi-Factor Scoring

// Weight = 0.4*distance + 0.3*traffic + 0.3*(1-reliability)
::ShortestPathDijkstras<Road>(
    ADD(
        MUL(_::{distance}, 0.4),
        ADD(
            MUL(_::FromN::{traffic_factor}, 0.3),
            MUL(SUB(1, _::{reliability}), 0.3)
        )
    )
)

⚠️ Warning

When using custom weights, ensure all weight values are non-negative. Negative weights can lead to incorrect results or infinite loops.

Path Result Structure

All shortest path algorithms return the same result structure:

{
    path: [Node],           // Ordered list of nodes in the path
    edges: [Edge],          // Ordered list of edges in the path
    total_weight: F64,      // Total weight/cost of the path
    hop_count: I64          // Number of edges in the path
}

Performance Considerations

  • BFS: Fastest for unweighted graphs, memory efficient
  • Dijkstra: Moderate performance, very flexible
  • A*: Can be much faster than Dijkstra with a good heuristic, but requires additional node property for heuristic

Tips for Optimal Performance:

  1. Use BFS when weights aren’t needed
  2. Index properties used in weight calculations
  3. For A*, ensure heuristic is admissible (never overestimates)
  4. Consider graph density when choosing algorithms

ShortestPathBFS

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Find Unweighted Shortest Paths with BFS  

ShortestPathBFS uses breadth-first search to find the shortest path between nodes when all edges have equal weight. It’s the fastest algorithm for unweighted graphs and is ideal when you care about minimizing the number of hops rather than total distance or cost.

::ShortestPathBFS<EdgeType>
::To(target_id)

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

How It Works

BFS explores the graph level by level:

  1. Start from the source node
  2. Visit all neighbors at distance 1
  3. Then visit all neighbors at distance 2
  4. Continue until the target is found

This guarantees finding the path with the minimum number of edges (hops).

When to Use ShortestPathBFS

Use BFS when you want to:

  • Minimize hop count: Find paths with the fewest edges
  • Equal edge importance: All edges are equally significant
  • Social network analysis: Find degrees of separation
  • Network routing: Minimize number of router hops
  • Fast performance: Need the fastest shortest path algorithm

ℹ️ Note

If edges have different weights (distance, cost, time), use ShortestPathDijkstras instead.

Example 1: Basic shortest path by hop count

QUERY FindShortestPath(start_id: ID, end_id: ID) =>
    result <- N<City>(start_id)
        ::ShortestPathBFS<Road>
        ::To(end_id)
    RETURN result

QUERY CreateNetwork(city_name: String) =>
    city <- AddN<City>({ name: city_name })
    RETURN city

QUERY ConnectCities(from_id: ID, to_id: ID) =>
    road <- AddE<Road>::From(from_id)::To(to_id)
    RETURN road
N::City {
    name: String
}

E::Road {
    From: City,
    To: City,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create cities
cities = {}
for city_name in ["New York", "Boston", "Philadelphia", "Washington DC", "Baltimore"]:
    result = client.query("CreateNetwork", {"city_name": city_name})
    cities[city_name] = result["city"]["id"]

# Create road network
connections = [
    ("New York", "Boston"),
    ("New York", "Philadelphia"),
    ("Philadelphia", "Washington DC"),
    ("Philadelphia", "Baltimore"),
    ("Baltimore", "Washington DC"),
]

for from_city, to_city in connections:
    client.query("ConnectCities", {
        "from_id": cities[from_city],
        "to_id": cities[to_city]
    })

# Find shortest path
result = client.query("FindShortestPath", {
    "start_id": cities["New York"],
    "end_id": cities["Washington DC"]
})

print(f"Path from New York to Washington DC:")
print(f"Cities: {[node['name'] for node in result['path']]}")
print(f"Hop count: {result['hop_count']}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create cities
    let mut cities: HashMap<String, String> = HashMap::new();
    for city_name in ["New York", "Boston", "Philadelphia", "Washington DC", "Baltimore"] {
        let result: serde_json::Value = client.query("CreateNetwork", &json!({
            "city_name": city_name,
        })).await?;
        cities.insert(city_name.to_string(), result["city"]["id"].as_str().unwrap().to_string());
    }

    // Create road network
    let connections = vec![
        ("New York", "Boston"),
        ("New York", "Philadelphia"),
        ("Philadelphia", "Washington DC"),
        ("Philadelphia", "Baltimore"),
        ("Baltimore", "Washington DC"),
    ];

    for (from_city, to_city) in &connections {
        let _road: serde_json::Value = client.query("ConnectCities", &json!({
            "from_id": cities[*from_city],
            "to_id": cities[*to_city],
        })).await?;
    }

    // Find shortest path
    let result: serde_json::Value = client.query("FindShortestPath", &json!({
        "start_id": cities["New York"],
        "end_id": cities["Washington DC"],
    })).await?;

    println!("Path from New York to Washington DC: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create cities
    cities := make(map[string]string)
    cityNames := []string{"New York", "Boston", "Philadelphia", "Washington DC", "Baltimore"}

    for _, cityName := range cityNames {
        var result map[string]any
        if err := client.Query("CreateNetwork", helix.WithData(map[string]any{
            "city_name": cityName,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateNetwork failed: %s", err)
        }
        cities[cityName] = result["city"].(map[string]any)["id"].(string)
    }

    // Create road network
    connections := [][2]string{
        {"New York", "Boston"},
        {"New York", "Philadelphia"},
        {"Philadelphia", "Washington DC"},
        {"Philadelphia", "Baltimore"},
        {"Baltimore", "Washington DC"},
    }

    for _, conn := range connections {
        var road map[string]any
        if err := client.Query("ConnectCities", helix.WithData(map[string]any{
            "from_id": cities[conn[0]],
            "to_id":   cities[conn[1]],
        })).Scan(&road); err != nil {
            log.Fatalf("ConnectCities failed: %s", err)
        }
    }

    // Find shortest path
    var result map[string]any
    if err := client.Query("FindShortestPath", helix.WithData(map[string]any{
        "start_id": cities["New York"],
        "end_id":   cities["Washington DC"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindShortestPath failed: %s", err)
    }

    fmt.Printf("Path from New York to Washington DC: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create cities
    const cities: Record<string, string> = {};
    const cityNames = ["New York", "Boston", "Philadelphia", "Washington DC", "Baltimore"];

    for (const cityName of cityNames) {
        const result = await client.query("CreateNetwork", { city_name: cityName });
        cities[cityName] = result.city.id;
    }

    // Create road network
    const connections = [
        ["New York", "Boston"],
        ["New York", "Philadelphia"],
        ["Philadelphia", "Washington DC"],
        ["Philadelphia", "Baltimore"],
        ["Baltimore", "Washington DC"],
    ];

    for (const [fromCity, toCity] of connections) {
        await client.query("ConnectCities", {
            from_id: cities[fromCity],
            to_id: cities[toCity],
        });
    }

    // Find shortest path
    const result = await client.query("FindShortestPath", {
        start_id: cities["New York"],
        end_id: cities["Washington DC"],
    });

    console.log("Path from New York to Washington DC:");
    console.log("Cities:", result.path.map((node: any) => node.name));
    console.log("Hop count:", result.hop_count);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create cities and store IDs
NY_ID=$(curl -X POST http://localhost:6969/CreateNetwork \
  -H 'Content-Type: application/json' \
  -d '{"city_name":"New York"}' | jq -r '.city.id')

BOSTON_ID=$(curl -X POST http://localhost:6969/CreateNetwork \
  -H 'Content-Type: application/json' \
  -d '{"city_name":"Boston"}' | jq -r '.city.id')

PHILLY_ID=$(curl -X POST http://localhost:6969/CreateNetwork \
  -H 'Content-Type: application/json' \
  -d '{"city_name":"Philadelphia"}' | jq -r '.city.id')

DC_ID=$(curl -X POST http://localhost:6969/CreateNetwork \
  -H 'Content-Type: application/json' \
  -d '{"city_name":"Washington DC"}' | jq -r '.city.id')

BALTIMORE_ID=$(curl -X POST http://localhost:6969/CreateNetwork \
  -H 'Content-Type: application/json' \
  -d '{"city_name":"Baltimore"}' | jq -r '.city.id')

# Create road network
curl -X POST http://localhost:6969/ConnectCities \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$NY_ID\",\"to_id\":\"$BOSTON_ID\"}"

curl -X POST http://localhost:6969/ConnectCities \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$NY_ID\",\"to_id\":\"$PHILLY_ID\"}"

curl -X POST http://localhost:6969/ConnectCities \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$PHILLY_ID\",\"to_id\":\"$DC_ID\"}"

curl -X POST http://localhost:6969/ConnectCities \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$PHILLY_ID\",\"to_id\":\"$BALTIMORE_ID\"}"

curl -X POST http://localhost:6969/ConnectCities \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$BALTIMORE_ID\",\"to_id\":\"$DC_ID\"}"

# Find shortest path
curl -X POST http://localhost:6969/FindShortestPath \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$NY_ID\",\"end_id\":\"$DC_ID\"}"

Example 2: Social network degree of separation

QUERY FindConnection(person1_id: ID, person2_id: ID) =>
    connection <- N<Person>(person1_id)
        ::ShortestPathBFS<Knows>
        ::To(person2_id)
    RETURN connection

QUERY CreatePerson(name: String) =>
    person <- AddN<Person>({ name: name })
    RETURN person

QUERY CreateFriendship(from_id: ID, to_id: ID) =>
    knows <- AddE<Knows>::From(from_id)::To(to_id)
    RETURN knows
N::Person {
    name: String
}

E::Knows {
    From: Person,
    To: Person,
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create people
people = {}
for name in ["Alice", "Bob", "Carol", "Dave", "Eve", "Frank"]:
    result = client.query("CreatePerson", {"name": name})
    people[name] = result["person"]["id"]

# Create friendship network
friendships = [
    ("Alice", "Bob"),
    ("Bob", "Carol"),
    ("Carol", "Dave"),
    ("Alice", "Eve"),
    ("Eve", "Frank"),
    ("Frank", "Dave"),
]

for person1, person2 in friendships:
    client.query("CreateFriendship", {
        "from_id": people[person1],
        "to_id": people[person2]
    })

# Find shortest connection
result = client.query("FindConnection", {
    "person1_id": people["Alice"],
    "person2_id": people["Dave"]
})

print(f"Connection from Alice to Dave:")
print(f"Path: {' -> '.join([node['name'] for node in result['connection']['path']])}")
print(f"Degrees of separation: {result['connection']['hop_count']}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create people
    let mut people: HashMap<String, String> = HashMap::new();
    for name in ["Alice", "Bob", "Carol", "Dave", "Eve", "Frank"] {
        let result: serde_json::Value = client.query("CreatePerson", &json!({
            "name": name,
        })).await?;
        people.insert(name.to_string(), result["person"]["id"].as_str().unwrap().to_string());
    }

    // Create friendship network
    let friendships = vec![
        ("Alice", "Bob"),
        ("Bob", "Carol"),
        ("Carol", "Dave"),
        ("Alice", "Eve"),
        ("Eve", "Frank"),
        ("Frank", "Dave"),
    ];

    for (person1, person2) in &friendships {
        let _knows: serde_json::Value = client.query("CreateFriendship", &json!({
            "from_id": people[*person1],
            "to_id": people[*person2],
        })).await?;
    }

    // Find shortest connection
    let result: serde_json::Value = client.query("FindConnection", &json!({
        "person1_id": people["Alice"],
        "person2_id": people["Dave"],
    })).await?;

    println!("Connection from Alice to Dave: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create people
    people := make(map[string]string)
    names := []string{"Alice", "Bob", "Carol", "Dave", "Eve", "Frank"}

    for _, name := range names {
        var result map[string]any
        if err := client.Query("CreatePerson", helix.WithData(map[string]any{
            "name": name,
        })).Scan(&result); err != nil {
            log.Fatalf("CreatePerson failed: %s", err)
        }
        people[name] = result["person"].(map[string]any)["id"].(string)
    }

    // Create friendship network
    friendships := [][2]string{
        {"Alice", "Bob"},
        {"Bob", "Carol"},
        {"Carol", "Dave"},
        {"Alice", "Eve"},
        {"Eve", "Frank"},
        {"Frank", "Dave"},
    }

    for _, friendship := range friendships {
        var knows map[string]any
        if err := client.Query("CreateFriendship", helix.WithData(map[string]any{
            "from_id": people[friendship[0]],
            "to_id":   people[friendship[1]],
        })).Scan(&knows); err != nil {
            log.Fatalf("CreateFriendship failed: %s", err)
        }
    }

    // Find shortest connection
    var result map[string]any
    if err := client.Query("FindConnection", helix.WithData(map[string]any{
        "person1_id": people["Alice"],
        "person2_id": people["Dave"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindConnection failed: %s", err)
    }

    fmt.Printf("Connection from Alice to Dave: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create people
    const people: Record<string, string> = {};
    const names = ["Alice", "Bob", "Carol", "Dave", "Eve", "Frank"];

    for (const name of names) {
        const result = await client.query("CreatePerson", { name });
        people[name] = result.person.id;
    }

    // Create friendship network
    const friendships = [
        ["Alice", "Bob"],
        ["Bob", "Carol"],
        ["Carol", "Dave"],
        ["Alice", "Eve"],
        ["Eve", "Frank"],
        ["Frank", "Dave"],
    ];

    for (const [person1, person2] of friendships) {
        await client.query("CreateFriendship", {
            from_id: people[person1],
            to_id: people[person2],
        });
    }

    // Find shortest connection
    const result = await client.query("FindConnection", {
        person1_id: people["Alice"],
        person2_id: people["Dave"],
    });

    console.log("Connection from Alice to Dave:");
    console.log("Path:", result.connection.path.map((n: any) => n.name).join(" -> "));
    console.log("Degrees of separation:", result.connection.hop_count);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create people and store IDs
ALICE_ID=$(curl -X POST http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice"}' | jq -r '.person.id')

BOB_ID=$(curl -X POST http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob"}' | jq -r '.person.id')

CAROL_ID=$(curl -X POST http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Carol"}' | jq -r '.person.id')

DAVE_ID=$(curl -X POST http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Dave"}' | jq -r '.person.id')

EVE_ID=$(curl -X POST http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve"}' | jq -r '.person.id')

FRANK_ID=$(curl -X POST http://localhost:6969/CreatePerson \
  -H 'Content-Type: application/json' \
  -d '{"name":"Frank"}' | jq -r '.person.id')

# Create friendship network
curl -X POST http://localhost:6969/CreateFriendship \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$ALICE_ID\",\"to_id\":\"$BOB_ID\"}"

curl -X POST http://localhost:6969/CreateFriendship \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$BOB_ID\",\"to_id\":\"$CAROL_ID\"}"

curl -X POST http://localhost:6969/CreateFriendship \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$CAROL_ID\",\"to_id\":\"$DAVE_ID\"}"

curl -X POST http://localhost:6969/CreateFriendship \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$ALICE_ID\",\"to_id\":\"$EVE_ID\"}"

curl -X POST http://localhost:6969/CreateFriendship \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$EVE_ID\",\"to_id\":\"$FRANK_ID\"}"

curl -X POST http://localhost:6969/CreateFriendship \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$FRANK_ID\",\"to_id\":\"$DAVE_ID\"}"

# Find shortest connection
curl -X POST http://localhost:6969/FindConnection \
  -H 'Content-Type: application/json' \
  -d "{\"person1_id\":\"$ALICE_ID\",\"person2_id\":\"$DAVE_ID\"}"

Result Structure

ShortestPathBFS returns a result containing:

{
    path: [Node],           // Ordered list of nodes from start to end
    edges: [Edge],          // Ordered list of edges connecting the nodes
    total_weight: F64,      // Always equals hop_count for BFS
    hop_count: I64          // Number of edges in the path
}

Performance Characteristics

  • Time Complexity: O(V + E) where V is vertices and E is edges
  • Space Complexity: O(V) for tracking visited nodes
  • Guarantee: Always finds the shortest path by hop count
  • Best Case: Target is a direct neighbor (1 hop)
  • Worst Case: Target requires exploring entire graph

ℹ️ Note

BFS is the fastest shortest path algorithm for unweighted graphs. For weighted graphs, it may not find the optimal path.

ShortestPathDijkstras

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Find Weighted Shortest Paths with Dijkstra’s Algorithm  

ShortestPathDijkstras finds the optimal shortest path in weighted graphs using Dijkstra’s algorithm. It supports sophisticated weight calculations that can reference edge properties, source node properties, and destination node properties, making it incredibly flexible for real-world routing scenarios.

::ShortestPathDijkstras<EdgeType>(weight_expression)
::To(target_id)

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

How It Works

Dijkstra’s algorithm:

  1. Starts at the source node with distance 0
  2. Explores neighbors, calculating cumulative path weights
  3. Always processes the node with the smallest known distance next
  4. Guarantees finding the optimal shortest path for non-negative weights

ℹ️ Note

All weights must be non-negative. Negative weights can produce incorrect results or infinite loops.

When to Use ShortestPathDijkstras

Use Dijkstra when you need:

  • Weighted paths: Edges have different costs (distance, time, bandwidth)
  • Custom calculations: Combine multiple properties into path weights
  • Multi-factor routing: Consider traffic, reliability, cost simultaneously
  • Property-based weights: Use node or edge properties in calculations
  • Flexibility: Maximum control over what defines “shortest”

Weight Expressions

Weight expressions can reference:

  • _::{property} - Edge property
  • _::FromN::{property} - Source node property
  • _::ToN::{property} - Destination node property
  • Mathematical functions: ADD, MUL, DIV, SUB, POW, SQRT, etc.

Example 1: Simple property-based weight

QUERY FindShortestRoute(start_id: ID, end_id: ID) =>
    result <- N<City>(start_id)
        ::ShortestPathDijkstras<Road>(_::{distance})
        ::To(end_id)
    RETURN result

QUERY CreateCity(name: String, population: I64) =>
    city <- AddN<City>({ name: name, population: population })
    RETURN city

QUERY CreateRoad(from_id: ID, to_id: ID, distance: F64, traffic_level: F64) =>
    road <- AddE<Road>({ distance: distance, traffic_level: traffic_level })::From(from_id)::To(to_id)
    RETURN road
N::City {
    name: String,
    population: I64
}

E::Road {
    From: City,
    To: City,
    Properties: {
        distance: F64,
        traffic_level: F64
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create cities
cities = {}
city_data = [
    ("Los Angeles", 4000000),
    ("San Diego", 1400000),
    ("Phoenix", 1600000),
    ("Las Vegas", 650000),
]

for name, population in city_data:
    result = client.query("CreateCity", {"name": name, "population": population})
    cities[name] = result["city"]["id"]

# Create road network with distances
roads = [
    ("Los Angeles", "San Diego", 120.0, 0.7),
    ("Los Angeles", "Las Vegas", 270.0, 0.5),
    ("San Diego", "Phoenix", 355.0, 0.4),
    ("Las Vegas", "Phoenix", 297.0, 0.6),
]

for from_city, to_city, distance, traffic in roads:
    client.query("CreateRoad", {
        "from_id": cities[from_city],
        "to_id": cities[to_city],
        "distance": distance,
        "traffic_level": traffic
    })

# Find shortest route by distance
result = client.query("FindShortestRoute", {
    "start_id": cities["Los Angeles"],
    "end_id": cities["Phoenix"]
})

print(f"Shortest route from LA to Phoenix:")
print(f"Path: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Total distance: {result['result']['total_weight']:.1f} miles")
print(f"Hops: {result['result']['hop_count']}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create cities
    let mut cities: HashMap<String, String> = HashMap::new();
    let city_data = vec![
        ("Los Angeles", 4000000),
        ("San Diego", 1400000),
        ("Phoenix", 1600000),
        ("Las Vegas", 650000),
    ];

    for (name, population) in &city_data {
        let result: serde_json::Value = client.query("CreateCity", &json!({
            "name": name,
            "population": population,
        })).await?;
        cities.insert(name.to_string(), result["city"]["id"].as_str().unwrap().to_string());
    }

    // Create road network with distances
    let roads = vec![
        ("Los Angeles", "San Diego", 120.0, 0.7),
        ("Los Angeles", "Las Vegas", 270.0, 0.5),
        ("San Diego", "Phoenix", 355.0, 0.4),
        ("Las Vegas", "Phoenix", 297.0, 0.6),
    ];

    for (from_city, to_city, distance, traffic) in &roads {
        let _road: serde_json::Value = client.query("CreateRoad", &json!({
            "from_id": cities[*from_city],
            "to_id": cities[*to_city],
            "distance": distance,
            "traffic_level": traffic,
        })).await?;
    }

    // Find shortest route by distance
    let result: serde_json::Value = client.query("FindShortestRoute", &json!({
        "start_id": cities["Los Angeles"],
        "end_id": cities["Phoenix"],
    })).await?;

    println!("Shortest route from LA to Phoenix: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create cities
    cities := make(map[string]string)
    cityData := []struct {
        name       string
        population int64
    }{
        {"Los Angeles", 4000000},
        {"San Diego", 1400000},
        {"Phoenix", 1600000},
        {"Las Vegas", 650000},
    }

    for _, city := range cityData {
        var result map[string]any
        if err := client.Query("CreateCity", helix.WithData(map[string]any{
            "name":       city.name,
            "population": city.population,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateCity failed: %s", err)
        }
        cities[city.name] = result["city"].(map[string]any)["id"].(string)
    }

    // Create road network with distances
    roads := []struct {
        from, to    string
        distance    float64
        trafficLevel float64
    }{
        {"Los Angeles", "San Diego", 120.0, 0.7},
        {"Los Angeles", "Las Vegas", 270.0, 0.5},
        {"San Diego", "Phoenix", 355.0, 0.4},
        {"Las Vegas", "Phoenix", 297.0, 0.6},
    }

    for _, road := range roads {
        var r map[string]any
        if err := client.Query("CreateRoad", helix.WithData(map[string]any{
            "from_id":       cities[road.from],
            "to_id":         cities[road.to],
            "distance":      road.distance,
            "traffic_level": road.trafficLevel,
        })).Scan(&r); err != nil {
            log.Fatalf("CreateRoad failed: %s", err)
        }
    }

    // Find shortest route by distance
    var result map[string]any
    if err := client.Query("FindShortestRoute", helix.WithData(map[string]any{
        "start_id": cities["Los Angeles"],
        "end_id":   cities["Phoenix"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindShortestRoute failed: %s", err)
    }

    fmt.Printf("Shortest route from LA to Phoenix: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create cities
    const cities: Record<string, string> = {};
    const cityData = [
        { name: "Los Angeles", population: 4000000 },
        { name: "San Diego", population: 1400000 },
        { name: "Phoenix", population: 1600000 },
        { name: "Las Vegas", population: 650000 },
    ];

    for (const city of cityData) {
        const result = await client.query("CreateCity", city);
        cities[city.name] = result.city.id;
    }

    // Create road network with distances
    const roads = [
        { from: "Los Angeles", to: "San Diego", distance: 120.0, traffic: 0.7 },
        { from: "Los Angeles", to: "Las Vegas", distance: 270.0, traffic: 0.5 },
        { from: "San Diego", to: "Phoenix", distance: 355.0, traffic: 0.4 },
        { from: "Las Vegas", to: "Phoenix", distance: 297.0, traffic: 0.6 },
    ];

    for (const road of roads) {
        await client.query("CreateRoad", {
            from_id: cities[road.from],
            to_id: cities[road.to],
            distance: road.distance,
            traffic_level: road.traffic,
        });
    }

    // Find shortest route by distance
    const result = await client.query("FindShortestRoute", {
        start_id: cities["Los Angeles"],
        end_id: cities["Phoenix"],
    });

    console.log("Shortest route from LA to Phoenix:");
    console.log("Path:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Total distance:", result.result.total_weight.toFixed(1), "miles");
    console.log("Hops:", result.result.hop_count);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create cities
LA_ID=$(curl -X POST http://localhost:6969/CreateCity \
  -H 'Content-Type: application/json' \
  -d '{"name":"Los Angeles","population":4000000}' | jq -r '.city.id')

SD_ID=$(curl -X POST http://localhost:6969/CreateCity \
  -H 'Content-Type: application/json' \
  -d '{"name":"San Diego","population":1400000}' | jq -r '.city.id')

PHX_ID=$(curl -X POST http://localhost:6969/CreateCity \
  -H 'Content-Type: application/json' \
  -d '{"name":"Phoenix","population":1600000}' | jq -r '.city.id')

LV_ID=$(curl -X POST http://localhost:6969/CreateCity \
  -H 'Content-Type: application/json' \
  -d '{"name":"Las Vegas","population":650000}' | jq -r '.city.id')

# Create roads
curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$LA_ID\",\"to_id\":\"$SD_ID\",\"distance\":120.0,\"traffic_level\":0.7}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$LA_ID\",\"to_id\":\"$LV_ID\",\"distance\":270.0,\"traffic_level\":0.5}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SD_ID\",\"to_id\":\"$PHX_ID\",\"distance\":355.0,\"traffic_level\":0.4}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$LV_ID\",\"to_id\":\"$PHX_ID\",\"distance\":297.0,\"traffic_level\":0.6}"

# Find shortest route
curl -X POST http://localhost:6969/FindShortestRoute \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$LA_ID\",\"end_id\":\"$PHX_ID\"}"

Example 2: Traffic-aware routing with multi-context weights

QUERY FindTrafficAwareRoute(start_id: ID, end_id: ID) =>
    result <- N<City>(start_id)
        ::ShortestPathDijkstras<Road>(MUL(_::{distance}, _::FromN::{traffic_factor}))
        ::To(end_id)
    RETURN result

QUERY CreateCityWithTraffic(name: String, traffic_factor: F64) =>
    city <- AddN<City>({ name: name, traffic_factor: traffic_factor })
    RETURN city

QUERY CreateRoad(from_id: ID, to_id: ID, distance: F64) =>
    road <- AddE<Road>({ distance: distance })::From(from_id)::To(to_id)
    RETURN road
N::City {
    name: String,
    traffic_factor: F64  // Multiplier: 1.0 = normal, 2.0 = heavy traffic
}

E::Road {
    From: City,
    To: City,
    Properties: {
        distance: F64
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create cities with traffic factors
cities = {}
city_data = [
    ("Downtown", 2.5),      # Heavy traffic
    ("Suburbs", 1.0),       # Normal traffic
    ("Industrial", 1.2),    # Light traffic
    ("Airport", 1.8),       # Moderate traffic
]

for name, traffic_factor in city_data:
    result = client.query("CreateCityWithTraffic", {
        "name": name,
        "traffic_factor": traffic_factor
    })
    cities[name] = result["city"]["id"]

# Create roads (base distances)
roads = [
    ("Downtown", "Suburbs", 10.0),
    ("Downtown", "Industrial", 8.0),
    ("Suburbs", "Airport", 15.0),
    ("Industrial", "Airport", 12.0),
]

for from_city, to_city, distance in roads:
    client.query("CreateRoad", {
        "from_id": cities[from_city],
        "to_id": cities[to_city],
        "distance": distance
    })

# Find traffic-aware route
# Weight = distance * source_traffic_factor
# Avoids routes starting from high-traffic areas
result = client.query("FindTrafficAwareRoute", {
    "start_id": cities["Downtown"],
    "end_id": cities["Airport"]
})

print(f"Traffic-aware route from Downtown to Airport:")
print(f"Path: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Effective distance: {result['result']['total_weight']:.1f}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create cities with traffic factors
    let mut cities: HashMap<String, String> = HashMap::new();
    let city_data = vec![
        ("Downtown", 2.5),
        ("Suburbs", 1.0),
        ("Industrial", 1.2),
        ("Airport", 1.8),
    ];

    for (name, traffic_factor) in &city_data {
        let result: serde_json::Value = client.query("CreateCityWithTraffic", &json!({
            "name": name,
            "traffic_factor": traffic_factor,
        })).await?;
        cities.insert(name.to_string(), result["city"]["id"].as_str().unwrap().to_string());
    }

    // Create roads
    let roads = vec![
        ("Downtown", "Suburbs", 10.0),
        ("Downtown", "Industrial", 8.0),
        ("Suburbs", "Airport", 15.0),
        ("Industrial", "Airport", 12.0),
    ];

    for (from_city, to_city, distance) in &roads {
        let _road: serde_json::Value = client.query("CreateRoad", &json!({
            "from_id": cities[*from_city],
            "to_id": cities[*to_city],
            "distance": distance,
        })).await?;
    }

    // Find traffic-aware route
    let result: serde_json::Value = client.query("FindTrafficAwareRoute", &json!({
        "start_id": cities["Downtown"],
        "end_id": cities["Airport"],
    })).await?;

    println!("Traffic-aware route: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create cities with traffic factors
    cities := make(map[string]string)
    cityData := []struct {
        name          string
        trafficFactor float64
    }{
        {"Downtown", 2.5},
        {"Suburbs", 1.0},
        {"Industrial", 1.2},
        {"Airport", 1.8},
    }

    for _, city := range cityData {
        var result map[string]any
        if err := client.Query("CreateCityWithTraffic", helix.WithData(map[string]any{
            "name":           city.name,
            "traffic_factor": city.trafficFactor,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateCityWithTraffic failed: %s", err)
        }
        cities[city.name] = result["city"].(map[string]any)["id"].(string)
    }

    // Create roads
    roads := []struct {
        from, to string
        distance float64
    }{
        {"Downtown", "Suburbs", 10.0},
        {"Downtown", "Industrial", 8.0},
        {"Suburbs", "Airport", 15.0},
        {"Industrial", "Airport", 12.0},
    }

    for _, road := range roads {
        var r map[string]any
        if err := client.Query("CreateRoad", helix.WithData(map[string]any{
            "from_id":  cities[road.from],
            "to_id":    cities[road.to],
            "distance": road.distance,
        })).Scan(&r); err != nil {
            log.Fatalf("CreateRoad failed: %s", err)
        }
    }

    // Find traffic-aware route
    var result map[string]any
    if err := client.Query("FindTrafficAwareRoute", helix.WithData(map[string]any{
        "start_id": cities["Downtown"],
        "end_id":   cities["Airport"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindTrafficAwareRoute failed: %s", err)
    }

    fmt.Printf("Traffic-aware route: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create cities with traffic factors
    const cities: Record<string, string> = {};
    const cityData = [
        { name: "Downtown", traffic_factor: 2.5 },
        { name: "Suburbs", traffic_factor: 1.0 },
        { name: "Industrial", traffic_factor: 1.2 },
        { name: "Airport", traffic_factor: 1.8 },
    ];

    for (const city of cityData) {
        const result = await client.query("CreateCityWithTraffic", city);
        cities[city.name] = result.city.id;
    }

    // Create roads
    const roads = [
        { from: "Downtown", to: "Suburbs", distance: 10.0 },
        { from: "Downtown", to: "Industrial", distance: 8.0 },
        { from: "Suburbs", to: "Airport", distance: 15.0 },
        { from: "Industrial", to: "Airport", distance: 12.0 },
    ];

    for (const road of roads) {
        await client.query("CreateRoad", {
            from_id: cities[road.from],
            to_id: cities[road.to],
            distance: road.distance,
        });
    }

    // Find traffic-aware route
    const result = await client.query("FindTrafficAwareRoute", {
        start_id: cities["Downtown"],
        end_id: cities["Airport"],
    });

    console.log("Traffic-aware route from Downtown to Airport:");
    console.log("Path:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Effective distance:", result.result.total_weight.toFixed(1));
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create cities with traffic factors
DOWNTOWN_ID=$(curl -X POST http://localhost:6969/CreateCityWithTraffic \
  -H 'Content-Type: application/json' \
  -d '{"name":"Downtown","traffic_factor":2.5}' | jq -r '.city.id')

SUBURBS_ID=$(curl -X POST http://localhost:6969/CreateCityWithTraffic \
  -H 'Content-Type: application/json' \
  -d '{"name":"Suburbs","traffic_factor":1.0}' | jq -r '.city.id')

INDUSTRIAL_ID=$(curl -X POST http://localhost:6969/CreateCityWithTraffic \
  -H 'Content-Type: application/json' \
  -d '{"name":"Industrial","traffic_factor":1.2}' | jq -r '.city.id')

AIRPORT_ID=$(curl -X POST http://localhost:6969/CreateCityWithTraffic \
  -H 'Content-Type: application/json' \
  -d '{"name":"Airport","traffic_factor":1.8}' | jq -r '.city.id')

# Create roads
curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$DOWNTOWN_ID\",\"to_id\":\"$SUBURBS_ID\",\"distance\":10.0}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$DOWNTOWN_ID\",\"to_id\":\"$INDUSTRIAL_ID\",\"distance\":8.0}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SUBURBS_ID\",\"to_id\":\"$AIRPORT_ID\",\"distance\":15.0}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$INDUSTRIAL_ID\",\"to_id\":\"$AIRPORT_ID\",\"distance\":12.0}"

# Find traffic-aware route
curl -X POST http://localhost:6969/FindTrafficAwareRoute \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$DOWNTOWN_ID\",\"end_id\":\"$AIRPORT_ID\"}"

Example 3: Complex multi-factor weight calculation

QUERY FindOptimalRoute(start_id: ID, end_id: ID) =>
    result <- N<Location>(start_id)
        ::ShortestPathDijkstras<Route>(
            ADD(
                MUL(_::{distance}, 0.4),
                ADD(
                    MUL(_::FromN::{traffic_factor}, 0.3),
                    MUL(SUB(1, _::{reliability}), 0.3)
                )
            )
        )
        ::To(end_id)
    RETURN result

QUERY CreateLocation(name: String, traffic_factor: F64) =>
    location <- AddN<Location>({ name: name, traffic_factor: traffic_factor })
    RETURN location

QUERY CreateRoute(from_id: ID, to_id: ID, distance: F64, reliability: F64) =>
    route <- AddE<Route>({ distance: distance, reliability: reliability })::From(from_id)::To(to_id)
    RETURN route
N::Location {
    name: String,
    traffic_factor: F64  // Current traffic multiplier
}

E::Route {
    From: Location,
    To: Location,
    Properties: {
        distance: F64,
        reliability: F64     // 0.0 to 1.0 (1.0 = perfectly reliable)
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create locations with traffic factors
locations = {}
location_data = [
    ("Station A", 1.2),
    ("Station B", 0.8),
    ("Station C", 1.5),
    ("Station D", 1.0),
]

for name, traffic_factor in location_data:
    result = client.query("CreateLocation", {
        "name": name,
        "traffic_factor": traffic_factor
    })
    locations[name] = result["location"]["id"]

# Create routes with distance and reliability
# Weight = 0.4*distance + 0.3*traffic + 0.3*(1-reliability)
routes = [
    ("Station A", "Station B", 100.0, 0.95),
    ("Station A", "Station C", 80.0, 0.70),
    ("Station B", "Station D", 120.0, 0.90),
    ("Station C", "Station D", 90.0, 0.85),
]

for from_loc, to_loc, distance, reliability in routes:
    client.query("CreateRoute", {
        "from_id": locations[from_loc],
        "to_id": locations[to_loc],
        "distance": distance,
        "reliability": reliability
    })

# Find optimal route considering distance, traffic, and reliability
result = client.query("FindOptimalRoute", {
    "start_id": locations["Station A"],
    "end_id": locations["Station D"]
})

print(f"Optimal route from Station A to Station D:")
print(f"Path: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Composite weight: {result['result']['total_weight']:.2f}")
print(f"(40% distance + 30% traffic + 30% unreliability)")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create locations with traffic factors
    let mut locations: HashMap<String, String> = HashMap::new();
    let location_data = vec![
        ("Station A", 1.2),
        ("Station B", 0.8),
        ("Station C", 1.5),
        ("Station D", 1.0),
    ];

    for (name, traffic_factor) in &location_data {
        let result: serde_json::Value = client.query("CreateLocation", &json!({
            "name": name,
            "traffic_factor": traffic_factor,
        })).await?;
        locations.insert(name.to_string(), result["location"]["id"].as_str().unwrap().to_string());
    }

    // Create routes with distance and reliability
    let routes = vec![
        ("Station A", "Station B", 100.0, 0.95),
        ("Station A", "Station C", 80.0, 0.70),
        ("Station B", "Station D", 120.0, 0.90),
        ("Station C", "Station D", 90.0, 0.85),
    ];

    for (from_loc, to_loc, distance, reliability) in &routes {
        let _route: serde_json::Value = client.query("CreateRoute", &json!({
            "from_id": locations[*from_loc],
            "to_id": locations[*to_loc],
            "distance": distance,
            "reliability": reliability,
        })).await?;
    }

    // Find optimal route
    let result: serde_json::Value = client.query("FindOptimalRoute", &json!({
        "start_id": locations["Station A"],
        "end_id": locations["Station D"],
    })).await?;

    println!("Optimal route: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create locations with traffic factors
    locations := make(map[string]string)
    locationData := []struct {
        name          string
        trafficFactor float64
    }{
        {"Station A", 1.2},
        {"Station B", 0.8},
        {"Station C", 1.5},
        {"Station D", 1.0},
    }

    for _, loc := range locationData {
        var result map[string]any
        if err := client.Query("CreateLocation", helix.WithData(map[string]any{
            "name":           loc.name,
            "traffic_factor": loc.trafficFactor,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateLocation failed: %s", err)
        }
        locations[loc.name] = result["location"].(map[string]any)["id"].(string)
    }

    // Create routes with distance and reliability
    routes := []struct {
        from, to    string
        distance    float64
        reliability float64
    }{
        {"Station A", "Station B", 100.0, 0.95},
        {"Station A", "Station C", 80.0, 0.70},
        {"Station B", "Station D", 120.0, 0.90},
        {"Station C", "Station D", 90.0, 0.85},
    }

    for _, route := range routes {
        var r map[string]any
        if err := client.Query("CreateRoute", helix.WithData(map[string]any{
            "from_id":     locations[route.from],
            "to_id":       locations[route.to],
            "distance":    route.distance,
            "reliability": route.reliability,
        })).Scan(&r); err != nil {
            log.Fatalf("CreateRoute failed: %s", err)
        }
    }

    // Find optimal route
    var result map[string]any
    if err := client.Query("FindOptimalRoute", helix.WithData(map[string]any{
        "start_id": locations["Station A"],
        "end_id":   locations["Station D"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindOptimalRoute failed: %s", err)
    }

    fmt.Printf("Optimal route: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create locations with traffic factors
    const locations: Record<string, string> = {};
    const locationData = [
        { name: "Station A", traffic_factor: 1.2 },
        { name: "Station B", traffic_factor: 0.8 },
        { name: "Station C", traffic_factor: 1.5 },
        { name: "Station D", traffic_factor: 1.0 },
    ];

    for (const loc of locationData) {
        const result = await client.query("CreateLocation", loc);
        locations[loc.name] = result.location.id;
    }

    // Create routes with distance and reliability
    const routes = [
        { from: "Station A", to: "Station B", distance: 100.0, reliability: 0.95 },
        { from: "Station A", to: "Station C", distance: 80.0, reliability: 0.70 },
        { from: "Station B", to: "Station D", distance: 120.0, reliability: 0.90 },
        { from: "Station C", to: "Station D", distance: 90.0, reliability: 0.85 },
    ];

    for (const route of routes) {
        await client.query("CreateRoute", {
            from_id: locations[route.from],
            to_id: locations[route.to],
            distance: route.distance,
            reliability: route.reliability,
        });
    }

    // Find optimal route
    const result = await client.query("FindOptimalRoute", {
        start_id: locations["Station A"],
        end_id: locations["Station D"],
    });

    console.log("Optimal route from Station A to Station D:");
    console.log("Path:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Composite weight:", result.result.total_weight.toFixed(2));
    console.log("(40% distance + 30% traffic + 30% unreliability)");
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create locations
STATION_A_ID=$(curl -X POST http://localhost:6969/CreateLocation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Station A","traffic_factor":1.2}' | jq -r '.location.id')

STATION_B_ID=$(curl -X POST http://localhost:6969/CreateLocation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Station B","traffic_factor":0.8}' | jq -r '.location.id')

STATION_C_ID=$(curl -X POST http://localhost:6969/CreateLocation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Station C","traffic_factor":1.5}' | jq -r '.location.id')

STATION_D_ID=$(curl -X POST http://localhost:6969/CreateLocation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Station D","traffic_factor":1.0}' | jq -r '.location.id')

# Create routes
curl -X POST http://localhost:6969/CreateRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$STATION_A_ID\",\"to_id\":\"$STATION_B_ID\",\"distance\":100.0,\"reliability\":0.95}"

curl -X POST http://localhost:6969/CreateRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$STATION_A_ID\",\"to_id\":\"$STATION_C_ID\",\"distance\":80.0,\"reliability\":0.70}"

curl -X POST http://localhost:6969/CreateRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$STATION_B_ID\",\"to_id\":\"$STATION_D_ID\",\"distance\":120.0,\"reliability\":0.90}"

curl -X POST http://localhost:6969/CreateRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$STATION_C_ID\",\"to_id\":\"$STATION_D_ID\",\"distance\":90.0,\"reliability\":0.85}"

# Find optimal route
curl -X POST http://localhost:6969/FindOptimalRoute \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$STATION_A_ID\",\"end_id\":\"$STATION_D_ID\"}"

Property Context Reference

ContextDescriptionExample
_::{property}Edge property_::{distance}
_::FromN::{property}Source node property_::FromN::{traffic_factor}
_::ToN::{property}Destination node property_::ToN::{popularity}

Available Mathematical Functions

Use these functions in weight expressions:

  • Arithmetic: ADD, SUB, MUL, DIV, MOD
  • Power: POW, SQRT, EXP, LN, LOG
  • Rounding: CEIL, FLOOR, ROUND, ABS
  • Trigonometric: SIN, COS, TAN
  • Constants: PI()

See Advanced Weight Expressions for more details.

Performance Considerations

  • Time Complexity: O((V + E) log V) using binary heap
  • Space Complexity: O(V) for distance tracking
  • Weight Calculation: Evaluated once per edge during exploration
  • Indexing: Index properties used in weight calculations for best performance

ℹ️ Note

Complex weight expressions may impact query performance. Profile your queries and consider caching frequently-accessed property values.

ShortestPathAStar

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Find Shortest Paths with A* Algorithm  

ShortestPathAStar uses the A* (A-star) algorithm to find optimal shortest paths in weighted graphs. It combines the precision of Dijkstra’s algorithm with heuristic guidance to dramatically improve performance, especially for long-distance pathfinding in spatial graphs.

::ShortestPathAStar<EdgeType>(weight_expression, "heuristic_property")
::To(target_id)

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

How It Works

A* combines two costs:

  1. g(n): Actual cost from start to current node (like Dijkstra)
  2. h(n): Heuristic estimate from current node to goal

The algorithm prioritizes nodes with the lowest f(n) = g(n) + h(n), guiding search toward the target.

ℹ️ Note

The heuristic must be admissible (never overestimate the true cost) to guarantee finding the optimal path. Common admissibles include straight-line distance for geographic routing.

When to Use A*

A* is ideal when:

  • Spatial graphs: Nodes have geographic or coordinate-based positions
  • Long paths: Target is far from source (A* excels here)
  • Goal-directed: You know the general direction to the target
  • Performance critical: Need faster results than Dijkstra
  • Admissible heuristic available: You have a property that estimates remaining cost

When A* Outperforms Dijkstra

A* can be significantly faster than Dijkstra when:

  • The heuristic effectively guides search toward the target
  • The graph is large and sparse
  • The path length is substantial
  • Node coordinates or positions are available

In these scenarios, A* can be 10-100x faster by avoiding exploration of irrelevant areas.

Heuristic Requirements

The heuristic property must:

  1. Be stored on each node
  2. Estimate cost to reach the target
  3. Never overestimate (admissible)
  4. Be consistent across the graph

Common heuristics:

  • Straight-line distance: For geographic graphs
  • Manhattan distance: For grid-based graphs
  • Euclidean distance: For coordinate-based graphs

Example 1: Geographic routing with straight-line distance heuristic

QUERY FindFastestRoute(start_id: ID, end_id: ID) =>
    result <- N<City>(start_id)
        ::ShortestPathAStar<Highway>(_::{distance}, "straight_line_distance")
        ::To(end_id)
    RETURN result

QUERY CreateCity(name: String, latitude: F64, longitude: F64, straight_line_dist: F64) =>
    city <- AddN<City>({
        name: name,
        latitude: latitude,
        longitude: longitude,
        straight_line_distance: straight_line_dist
    })
    RETURN city

QUERY CreateHighway(from_id: ID, to_id: ID, distance: F64) =>
    highway <- AddE<Highway>({ distance: distance })::From(from_id)::To(to_id)
    RETURN highway
N::City {
    name: String,
    latitude: F64,
    longitude: F64,
    straight_line_distance: F64  // Pre-calculated to target
}

E::Highway {
    From: City,
    To: City,
    Properties: {
        distance: F64  // Actual road distance
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client
import math

client = Client(local=True, port=6969)

# City coordinates (simplified for example)
city_coords = {
    "Seattle": (47.6, -122.3),
    "Portland": (45.5, -122.7),
    "San Francisco": (37.8, -122.4),
    "Los Angeles": (34.0, -118.2),
}

# Target city for heuristic calculation
target = "Los Angeles"
target_lat, target_lon = city_coords[target]

# Calculate straight-line distance heuristic
def calc_heuristic(lat1, lon1, lat2, lon2):
    # Simplified distance calculation (in practice, use haversine)
    return math.sqrt((lat2 - lat1)**2 + (lon2 - lon1)**2) * 69.0  # ~69 miles per degree

# Create cities with heuristic values
cities = {}
for name, (lat, lon) in city_coords.items():
    heuristic = calc_heuristic(lat, lon, target_lat, target_lon)
    result = client.query("CreateCity", {
        "name": name,
        "latitude": lat,
        "longitude": lon,
        "straight_line_dist": heuristic
    })
    cities[name] = result["city"]["id"]

# Create highway network with actual distances
highways = [
    ("Seattle", "Portland", 174.0),
    ("Portland", "San Francisco", 635.0),
    ("San Francisco", "Los Angeles", 383.0),
    ("Seattle", "San Francisco", 808.0),  # Alternative route
]

for from_city, to_city, distance in highways:
    client.query("CreateHighway", {
        "from_id": cities[from_city],
        "to_id": cities[to_city],
        "distance": distance
    })

# Find fastest route using A*
result = client.query("FindFastestRoute", {
    "start_id": cities["Seattle"],
    "end_id": cities["Los Angeles"]
})

print(f"A* route from Seattle to Los Angeles:")
print(f"Path: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Total distance: {result['result']['total_weight']:.1f} miles")
print(f"Hops: {result['result']['hop_count']}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // City coordinates
    let city_coords = vec![
        ("Seattle", 47.6, -122.3),
        ("Portland", 45.5, -122.7),
        ("San Francisco", 37.8, -122.4),
        ("Los Angeles", 34.0, -118.2),
    ];

    let target_lat = 34.0;
    let target_lon = -118.2;

    // Calculate straight-line distance heuristic
    let calc_heuristic = |lat1: f64, lon1: f64, lat2: f64, lon2: f64| -> f64 {
        ((lat2 - lat1).powi(2) + (lon2 - lon1).powi(2)).sqrt() * 69.0
    };

    // Create cities with heuristic values
    let mut cities: HashMap<String, String> = HashMap::new();
    for (name, lat, lon) in &city_coords {
        let heuristic = calc_heuristic(*lat, *lon, target_lat, target_lon);
        let result: serde_json::Value = client.query("CreateCity", &json!({
            "name": name,
            "latitude": lat,
            "longitude": lon,
            "straight_line_dist": heuristic,
        })).await?;
        cities.insert(name.to_string(), result["city"]["id"].as_str().unwrap().to_string());
    }

    // Create highway network
    let highways = vec![
        ("Seattle", "Portland", 174.0),
        ("Portland", "San Francisco", 635.0),
        ("San Francisco", "Los Angeles", 383.0),
        ("Seattle", "San Francisco", 808.0),
    ];

    for (from_city, to_city, distance) in &highways {
        let _highway: serde_json::Value = client.query("CreateHighway", &json!({
            "from_id": cities[*from_city],
            "to_id": cities[*to_city],
            "distance": distance,
        })).await?;
    }

    // Find fastest route using A*
    let result: serde_json::Value = client.query("FindFastestRoute", &json!({
        "start_id": cities["Seattle"],
        "end_id": cities["Los Angeles"],
    })).await?;

    println!("A* route from Seattle to Los Angeles: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "math"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // City coordinates
    cityCoords := map[string][2]float64{
        "Seattle":       {47.6, -122.3},
        "Portland":      {45.5, -122.7},
        "San Francisco": {37.8, -122.4},
        "Los Angeles":   {34.0, -118.2},
    }

    targetLat, targetLon := 34.0, -118.2

    // Calculate straight-line distance heuristic
    calcHeuristic := func(lat1, lon1, lat2, lon2 float64) float64 {
        return math.Sqrt(math.Pow(lat2-lat1, 2)+math.Pow(lon2-lon1, 2)) * 69.0
    }

    // Create cities with heuristic values
    cities := make(map[string]string)
    for name, coords := range cityCoords {
        lat, lon := coords[0], coords[1]
        heuristic := calcHeuristic(lat, lon, targetLat, targetLon)

        var result map[string]any
        if err := client.Query("CreateCity", helix.WithData(map[string]any{
            "name":                  name,
            "latitude":              lat,
            "longitude":             lon,
            "straight_line_dist":    heuristic,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateCity failed: %s", err)
        }
        cities[name] = result["city"].(map[string]any)["id"].(string)
    }

    // Create highway network
    highways := []struct {
        from, to string
        distance float64
    }{
        {"Seattle", "Portland", 174.0},
        {"Portland", "San Francisco", 635.0},
        {"San Francisco", "Los Angeles", 383.0},
        {"Seattle", "San Francisco", 808.0},
    }

    for _, highway := range highways {
        var h map[string]any
        if err := client.Query("CreateHighway", helix.WithData(map[string]any{
            "from_id":  cities[highway.from],
            "to_id":    cities[highway.to],
            "distance": highway.distance,
        })).Scan(&h); err != nil {
            log.Fatalf("CreateHighway failed: %s", err)
        }
    }

    // Find fastest route using A*
    var result map[string]any
    if err := client.Query("FindFastestRoute", helix.WithData(map[string]any{
        "start_id": cities["Seattle"],
        "end_id":   cities["Los Angeles"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindFastestRoute failed: %s", err)
    }

    fmt.Printf("A* route from Seattle to Los Angeles: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // City coordinates
    const cityCoords: Record<string, [number, number]> = {
        "Seattle": [47.6, -122.3],
        "Portland": [45.5, -122.7],
        "San Francisco": [37.8, -122.4],
        "Los Angeles": [34.0, -118.2],
    };

    const [targetLat, targetLon] = cityCoords["Los Angeles"];

    // Calculate straight-line distance heuristic
    const calcHeuristic = (lat1: number, lon1: number, lat2: number, lon2: number): number => {
        return Math.sqrt(Math.pow(lat2 - lat1, 2) + Math.pow(lon2 - lon1, 2)) * 69.0;
    };

    // Create cities with heuristic values
    const cities: Record<string, string> = {};
    for (const [name, [lat, lon]] of Object.entries(cityCoords)) {
        const heuristic = calcHeuristic(lat, lon, targetLat, targetLon);
        const result = await client.query("CreateCity", {
            name,
            latitude: lat,
            longitude: lon,
            straight_line_dist: heuristic,
        });
        cities[name] = result.city.id;
    }

    // Create highway network
    const highways = [
        { from: "Seattle", to: "Portland", distance: 174.0 },
        { from: "Portland", to: "San Francisco", distance: 635.0 },
        { from: "San Francisco", to: "Los Angeles", distance: 383.0 },
        { from: "Seattle", to: "San Francisco", distance: 808.0 },
    ];

    for (const highway of highways) {
        await client.query("CreateHighway", {
            from_id: cities[highway.from],
            to_id: cities[highway.to],
            distance: highway.distance,
        });
    }

    // Find fastest route using A*
    const result = await client.query("FindFastestRoute", {
        start_id: cities["Seattle"],
        end_id: cities["Los Angeles"],
    });

    console.log("A* route from Seattle to Los Angeles:");
    console.log("Path:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Total distance:", result.result.total_weight.toFixed(1), "miles");
    console.log("Hops:", result.result.hop_count);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create cities with heuristic values
SEATTLE_ID=$(curl -X POST http://localhost:6969/CreateCity \
  -H 'Content-Type: application/json' \
  -d '{"name":"Seattle","latitude":47.6,"longitude":-122.3,"straight_line_dist":950.0}' | jq -r '.city.id')

PORTLAND_ID=$(curl -X POST http://localhost:6969/CreateCity \
  -H 'Content-Type: application/json' \
  -d '{"name":"Portland","latitude":45.5,"longitude":-122.7,"straight_line_dist":850.0}' | jq -r '.city.id')

SF_ID=$(curl -X POST http://localhost:6969/CreateCity \
  -H 'Content-Type: application/json' \
  -d '{"name":"San Francisco","latitude":37.8,"longitude":-122.4,"straight_line_dist":380.0}' | jq -r '.city.id')

LA_ID=$(curl -X POST http://localhost:6969/CreateCity \
  -H 'Content-Type: application/json' \
  -d '{"name":"Los Angeles","latitude":34.0,"longitude":-118.2,"straight_line_dist":0.0}' | jq -r '.city.id')

# Create highways
curl -X POST http://localhost:6969/CreateHighway \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SEATTLE_ID\",\"to_id\":\"$PORTLAND_ID\",\"distance\":174.0}"

curl -X POST http://localhost:6969/CreateHighway \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$PORTLAND_ID\",\"to_id\":\"$SF_ID\",\"distance\":635.0}"

curl -X POST http://localhost:6969/CreateHighway \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SF_ID\",\"to_id\":\"$LA_ID\",\"distance\":383.0}"

curl -X POST http://localhost:6969/CreateHighway \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SEATTLE_ID\",\"to_id\":\"$SF_ID\",\"distance\":808.0}"

# Find fastest route using A*
curl -X POST http://localhost:6969/FindFastestRoute \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$SEATTLE_ID\",\"end_id\":\"$LA_ID\"}"

Example 2: Time-optimized routing with traffic-aware weights

QUERY FindQuickestRoute(start_id: ID, end_id: ID) =>
    result <- N<Junction>(start_id)
        ::ShortestPathAStar<Road>(MUL(_::{distance}, _::{traffic_multiplier}), "estimated_time_remaining")
        ::To(end_id)
    RETURN result

QUERY CreateJunction(name: String, est_time: F64) =>
    junction <- AddN<Junction>({
        name: name,
        estimated_time_remaining: est_time
    })
    RETURN junction

QUERY CreateRoad(from_id: ID, to_id: ID, distance: F64, traffic_mult: F64) =>
    road <- AddE<Road>({ distance: distance, traffic_multiplier: traffic_mult })::From(from_id)::To(to_id)
    RETURN road
N::Junction {
    name: String,
    estimated_time_remaining: F64  // Heuristic: est. minutes to destination
}

E::Road {
    From: Junction,
    To: Junction,
    Properties: {
        distance: F64,
        traffic_multiplier: F64  // 1.0 = normal, 2.0 = heavy traffic
    }
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create junctions with time-to-destination heuristic
junctions = {}
junction_data = [
    ("J1", 45.0),  # 45 min estimated to destination
    ("J2", 30.0),
    ("J3", 20.0),
    ("J4", 10.0),
    ("Destination", 0.0),
]

for name, est_time in junction_data:
    result = client.query("CreateJunction", {
        "name": name,
        "est_time": est_time
    })
    junctions[name] = result["junction"]["id"]

# Create roads with distance and current traffic
# Weight = distance * traffic_multiplier (estimates time)
roads = [
    ("J1", "J2", 10.0, 1.5),  # Heavy traffic
    ("J1", "J3", 15.0, 1.0),  # Normal traffic
    ("J2", "J4", 12.0, 1.2),
    ("J3", "J4", 8.0, 1.0),
    ("J4", "Destination", 5.0, 1.0),
]

for from_junc, to_junc, distance, traffic in roads:
    client.query("CreateRoad", {
        "from_id": junctions[from_junc],
        "to_id": junctions[to_junc],
        "distance": distance,
        "traffic_mult": traffic
    })

# Find quickest route considering traffic
result = client.query("FindQuickestRoute", {
    "start_id": junctions["J1"],
    "end_id": junctions["Destination"]
})

print(f"Quickest route from J1 to Destination:")
print(f"Path: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Estimated time: {result['result']['total_weight']:.1f} minutes")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create junctions with time-to-destination heuristic
    let mut junctions: HashMap<String, String> = HashMap::new();
    let junction_data = vec![
        ("J1", 45.0),
        ("J2", 30.0),
        ("J3", 20.0),
        ("J4", 10.0),
        ("Destination", 0.0),
    ];

    for (name, est_time) in &junction_data {
        let result: serde_json::Value = client.query("CreateJunction", &json!({
            "name": name,
            "est_time": est_time,
        })).await?;
        junctions.insert(name.to_string(), result["junction"]["id"].as_str().unwrap().to_string());
    }

    // Create roads with distance and traffic
    let roads = vec![
        ("J1", "J2", 10.0, 1.5),
        ("J1", "J3", 15.0, 1.0),
        ("J2", "J4", 12.0, 1.2),
        ("J3", "J4", 8.0, 1.0),
        ("J4", "Destination", 5.0, 1.0),
    ];

    for (from_junc, to_junc, distance, traffic) in &roads {
        let _road: serde_json::Value = client.query("CreateRoad", &json!({
            "from_id": junctions[*from_junc],
            "to_id": junctions[*to_junc],
            "distance": distance,
            "traffic_mult": traffic,
        })).await?;
    }

    // Find quickest route
    let result: serde_json::Value = client.query("FindQuickestRoute", &json!({
        "start_id": junctions["J1"],
        "end_id": junctions["Destination"],
    })).await?;

    println!("Quickest route: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create junctions with time-to-destination heuristic
    junctions := make(map[string]string)
    junctionData := []struct {
        name    string
        estTime float64
    }{
        {"J1", 45.0},
        {"J2", 30.0},
        {"J3", 20.0},
        {"J4", 10.0},
        {"Destination", 0.0},
    }

    for _, junction := range junctionData {
        var result map[string]any
        if err := client.Query("CreateJunction", helix.WithData(map[string]any{
            "name":     junction.name,
            "est_time": junction.estTime,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateJunction failed: %s", err)
        }
        junctions[junction.name] = result["junction"].(map[string]any)["id"].(string)
    }

    // Create roads with distance and traffic
    roads := []struct {
        from, to      string
        distance      float64
        trafficMult   float64
    }{
        {"J1", "J2", 10.0, 1.5},
        {"J1", "J3", 15.0, 1.0},
        {"J2", "J4", 12.0, 1.2},
        {"J3", "J4", 8.0, 1.0},
        {"J4", "Destination", 5.0, 1.0},
    }

    for _, road := range roads {
        var r map[string]any
        if err := client.Query("CreateRoad", helix.WithData(map[string]any{
            "from_id":      junctions[road.from],
            "to_id":        junctions[road.to],
            "distance":     road.distance,
            "traffic_mult": road.trafficMult,
        })).Scan(&r); err != nil {
            log.Fatalf("CreateRoad failed: %s", err)
        }
    }

    // Find quickest route
    var result map[string]any
    if err := client.Query("FindQuickestRoute", helix.WithData(map[string]any{
        "start_id": junctions["J1"],
        "end_id":   junctions["Destination"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindQuickestRoute failed: %s", err)
    }

    fmt.Printf("Quickest route: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create junctions with time-to-destination heuristic
    const junctions: Record<string, string> = {};
    const junctionData = [
        { name: "J1", est_time: 45.0 },
        { name: "J2", est_time: 30.0 },
        { name: "J3", est_time: 20.0 },
        { name: "J4", est_time: 10.0 },
        { name: "Destination", est_time: 0.0 },
    ];

    for (const junction of junctionData) {
        const result = await client.query("CreateJunction", junction);
        junctions[junction.name] = result.junction.id;
    }

    // Create roads with distance and traffic
    const roads = [
        { from: "J1", to: "J2", distance: 10.0, traffic_mult: 1.5 },
        { from: "J1", to: "J3", distance: 15.0, traffic_mult: 1.0 },
        { from: "J2", to: "J4", distance: 12.0, traffic_mult: 1.2 },
        { from: "J3", to: "J4", distance: 8.0, traffic_mult: 1.0 },
        { from: "J4", to: "Destination", distance: 5.0, traffic_mult: 1.0 },
    ];

    for (const road of roads) {
        await client.query("CreateRoad", {
            from_id: junctions[road.from],
            to_id: junctions[road.to],
            distance: road.distance,
            traffic_mult: road.traffic_mult,
        });
    }

    // Find quickest route
    const result = await client.query("FindQuickestRoute", {
        start_id: junctions["J1"],
        end_id: junctions["Destination"],
    });

    console.log("Quickest route from J1 to Destination:");
    console.log("Path:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Estimated time:", result.result.total_weight.toFixed(1), "minutes");
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create junctions
J1_ID=$(curl -X POST http://localhost:6969/CreateJunction \
  -H 'Content-Type: application/json' \
  -d '{"name":"J1","est_time":45.0}' | jq -r '.junction.id')

J2_ID=$(curl -X POST http://localhost:6969/CreateJunction \
  -H 'Content-Type: application/json' \
  -d '{"name":"J2","est_time":30.0}' | jq -r '.junction.id')

J3_ID=$(curl -X POST http://localhost:6969/CreateJunction \
  -H 'Content-Type: application/json' \
  -d '{"name":"J3","est_time":20.0}' | jq -r '.junction.id')

J4_ID=$(curl -X POST http://localhost:6969/CreateJunction \
  -H 'Content-Type: application/json' \
  -d '{"name":"J4","est_time":10.0}' | jq -r '.junction.id')

DEST_ID=$(curl -X POST http://localhost:6969/CreateJunction \
  -H 'Content-Type: application/json' \
  -d '{"name":"Destination","est_time":0.0}' | jq -r '.junction.id')

# Create roads
curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$J1_ID\",\"to_id\":\"$J2_ID\",\"distance\":10.0,\"traffic_mult\":1.5}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$J1_ID\",\"to_id\":\"$J3_ID\",\"distance\":15.0,\"traffic_mult\":1.0}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$J2_ID\",\"to_id\":\"$J4_ID\",\"distance\":12.0,\"traffic_mult\":1.2}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$J3_ID\",\"to_id\":\"$J4_ID\",\"distance\":8.0,\"traffic_mult\":1.0}"

curl -X POST http://localhost:6969/CreateRoad \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$J4_ID\",\"to_id\":\"$DEST_ID\",\"distance\":5.0,\"traffic_mult\":1.0}"

# Find quickest route
curl -X POST http://localhost:6969/FindQuickestRoute \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$J1_ID\",\"end_id\":\"$DEST_ID\"}"

Admissible Heuristics

For A* to guarantee optimal results, the heuristic must be admissible:

Good Heuristics (Admissible)

  • Straight-line distance: Always ≤ actual road distance
  • Manhattan distance: For grid-based movement
  • Minimum theoretical time: Based on maximum speed limits

Bad Heuristics (Inadmissible)

  • Overestimated distances: May miss optimal path
  • Random values: No guarantee of optimality
  • Negative values: Breaks the algorithm

⚠️ Warning

Using an inadmissible heuristic may cause A* to return suboptimal paths. Always ensure your heuristic never overestimates the true remaining cost.

Performance Comparison

ScenarioDijkstraA* with Good Heuristic
Small graph (< 100 nodes)FastSimilar
Large graph (> 10,000 nodes)Slow10-100x faster
Short pathsFastSimilar
Long pathsSlowMuch faster
No spatial infoOnly optionNot applicable
Spatial graphExplores everythingExplores targeted area

Result Structure

A* returns the same structure as Dijkstra:

{
    path: [Node],           // Ordered list of nodes from start to end
    edges: [Edge],          // Ordered list of edges connecting nodes
    total_weight: F64,      // Total actual weight (not heuristic)
    hop_count: I64          // Number of edges in path
}

Best Practices

Pre-calculate Heuristics

For static targets, pre-calculate and store heuristic values:

// Pre-calculate straight-line distance to common destinations
N::City {
    distance_to_hub: F64,
    distance_to_airport: F64
}

Choose the Right Heuristic

  • Geographic graphs: Use haversine or Euclidean distance
  • Grid graphs: Use Manhattan distance
  • Time-based: Use minimum theoretical time

Verify Admissibility

Test that your heuristic never overestimates:

For all nodes n: h(n) ≤ actual_cost(n, target)

Custom Weight Calculations

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Custom Weight Calculations  

Helix’s shortest path algorithms support sophisticated weight calculations that can reference properties from edges, source nodes, and destination nodes. This enables real-world routing scenarios where path costs depend on multiple factors and contexts.

ℹ️ Note

Custom weights are available in both ShortestPathDijkstras and ShortestPathAStar.

Property Contexts

Property contexts allow you to reference different parts of the graph structure in your weight expressions:

Context SyntaxDescriptionExample Use Case
_::{property}Edge propertyRoad distance, bandwidth, cost
_::FromN::{property}Source node propertyOrigin traffic, elevation, population
_::ToN::{property}Destination node propertyTarget popularity, capacity, demand
_::FromV::{property}Source node vector propertyEmbedding similarity
_::ToV::{property}Destination node vector propertyFeature matching

⚠️ Warning

All weight expressions must evaluate to non-negative values. Negative weights can cause incorrect results or infinite loops.

Context Reference Table

Edge Context: _::{property}

Access properties directly on the edge being evaluated:

// Use edge distance property
::ShortestPathDijkstras<Road>(_::{distance})

// Use edge bandwidth property
::ShortestPathDijkstras<Connection>(_::{bandwidth})

// Use edge reliability property
::ShortestPathDijkstras<Route>(_::{reliability})

Common use cases:

  • Distance-based routing
  • Bandwidth optimization
  • Cost minimization
  • Time-based routing

Source Node Context: _::FromN::{property}

Access properties from the node where the edge originates:

// Weight based on source traffic
::ShortestPathDijkstras<Road>(MUL(_::{distance}, _::FromN::{traffic_factor}))

// Avoid high-elevation starting points
::ShortestPathDijkstras<Trail>(ADD(_::{length}, _::FromN::{elevation}))

// Consider source congestion
::ShortestPathDijkstras<Connection>(DIV(_::{bandwidth}, _::FromN::{load}))

Common use cases:

  • Traffic-aware routing (avoid congested sources)
  • Elevation-based path planning
  • Load balancing (distribute from busy sources)
  • Source capacity constraints

Destination Node Context: _::ToN::{property}

Access properties from the node where the edge terminates:

// Prefer popular destinations
::ShortestPathDijkstras<Road>(DIV(_::{distance}, _::ToN::{popularity}))

// Avoid high-cost destinations
::ShortestPathDijkstras<Route>(MUL(_::{distance}, _::ToN::{cost_multiplier}))

// Consider destination capacity
::ShortestPathDijkstras<Connection>(DIV(_::{bandwidth}, _::ToN::{available_capacity}))

Common use cases:

  • Popularity-weighted routing
  • Destination capacity management
  • Cost-aware pathfinding
  • Attraction-based navigation

Example 1: Time-decay routing with source context

QUERY FindFreshRoute(start_id: ID, end_id: ID) =>
    result <- N<Warehouse>(start_id)
        ::ShortestPathDijkstras<ShippingRoute>(
            MUL(_::{distance}, POW(1.1, _::FromN::{days_since_update}))
        )
        ::To(end_id)
    RETURN result

QUERY CreateWarehouse(name: String, days_old: I64) =>
    warehouse <- AddN<Warehouse>({
        name: name,
        days_since_update: days_old
    })
    RETURN warehouse

QUERY CreateShippingRoute(from_id: ID, to_id: ID, distance: F64) =>
    route <- AddE<ShippingRoute>({ distance: distance })::From(from_id)::To(to_id)
    RETURN route
N::Warehouse {
    name: String,
    days_since_update: I64  // Days since route info was updated
}

E::ShippingRoute {
    distance: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create warehouses with freshness info
# Weight = distance * 1.1^(days_since_update)
# Penalizes routes from warehouses with stale data
warehouses = {}
warehouse_data = [
    ("Main Hub", 0),      # Fresh data
    ("North Branch", 5),  # 5 days old
    ("South Branch", 2),  # 2 days old
    ("East Branch", 10),  # Very stale
    ("Destination", 0),
]

for name, days_old in warehouse_data:
    result = client.query("CreateWarehouse", {
        "name": name,
        "days_old": days_old
    })
    warehouses[name] = result["warehouse"]["id"]

# Create shipping routes
routes = [
    ("Main Hub", "North Branch", 100.0),
    ("Main Hub", "South Branch", 120.0),
    ("North Branch", "Destination", 150.0),
    ("South Branch", "Destination", 140.0),
    ("Main Hub", "East Branch", 80.0),
    ("East Branch", "Destination", 130.0),
]

for from_wh, to_wh, distance in routes:
    client.query("CreateShippingRoute", {
        "from_id": warehouses[from_wh],
        "to_id": warehouses[to_wh],
        "distance": distance
    })

# Find route preferring fresh data sources
result = client.query("FindFreshRoute", {
    "start_id": warehouses["Main Hub"],
    "end_id": warehouses["Destination"]
})

print(f"Route preferring fresh data:")
print(f"Path: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Adjusted weight: {result['result']['total_weight']:.2f}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create warehouses with freshness info
    let mut warehouses: HashMap<String, String> = HashMap::new();
    let warehouse_data = vec![
        ("Main Hub", 0),
        ("North Branch", 5),
        ("South Branch", 2),
        ("East Branch", 10),
        ("Destination", 0),
    ];

    for (name, days_old) in &warehouse_data {
        let result: serde_json::Value = client.query("CreateWarehouse", &json!({
            "name": name,
            "days_old": days_old,
        })).await?;
        warehouses.insert(name.to_string(), result["warehouse"]["id"].as_str().unwrap().to_string());
    }

    // Create shipping routes
    let routes = vec![
        ("Main Hub", "North Branch", 100.0),
        ("Main Hub", "South Branch", 120.0),
        ("North Branch", "Destination", 150.0),
        ("South Branch", "Destination", 140.0),
        ("Main Hub", "East Branch", 80.0),
        ("East Branch", "Destination", 130.0),
    ];

    for (from_wh, to_wh, distance) in &routes {
        let _route: serde_json::Value = client.query("CreateShippingRoute", &json!({
            "from_id": warehouses[*from_wh],
            "to_id": warehouses[*to_wh],
            "distance": distance,
        })).await?;
    }

    // Find route preferring fresh data
    let result: serde_json::Value = client.query("FindFreshRoute", &json!({
        "start_id": warehouses["Main Hub"],
        "end_id": warehouses["Destination"],
    })).await?;

    println!("Route preferring fresh data: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create warehouses with freshness info
    warehouses := make(map[string]string)
    warehouseData := []struct {
        name    string
        daysOld int64
    }{
        {"Main Hub", 0},
        {"North Branch", 5},
        {"South Branch", 2},
        {"East Branch", 10},
        {"Destination", 0},
    }

    for _, wh := range warehouseData {
        var result map[string]any
        if err := client.Query("CreateWarehouse", helix.WithData(map[string]any{
            "name":     wh.name,
            "days_old": wh.daysOld,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateWarehouse failed: %s", err)
        }
        warehouses[wh.name] = result["warehouse"].(map[string]any)["id"].(string)
    }

    // Create shipping routes
    routes := []struct {
        from, to string
        distance float64
    }{
        {"Main Hub", "North Branch", 100.0},
        {"Main Hub", "South Branch", 120.0},
        {"North Branch", "Destination", 150.0},
        {"South Branch", "Destination", 140.0},
        {"Main Hub", "East Branch", 80.0},
        {"East Branch", "Destination", 130.0},
    }

    for _, route := range routes {
        var r map[string]any
        if err := client.Query("CreateShippingRoute", helix.WithData(map[string]any{
            "from_id":  warehouses[route.from],
            "to_id":    warehouses[route.to],
            "distance": route.distance,
        })).Scan(&r); err != nil {
            log.Fatalf("CreateShippingRoute failed: %s", err)
        }
    }

    // Find route preferring fresh data
    var result map[string]any
    if err := client.Query("FindFreshRoute", helix.WithData(map[string]any{
        "start_id": warehouses["Main Hub"],
        "end_id":   warehouses["Destination"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindFreshRoute failed: %s", err)
    }

    fmt.Printf("Route preferring fresh data: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create warehouses with freshness info
    const warehouses: Record<string, string> = {};
    const warehouseData = [
        { name: "Main Hub", days_old: 0 },
        { name: "North Branch", days_old: 5 },
        { name: "South Branch", days_old: 2 },
        { name: "East Branch", days_old: 10 },
        { name: "Destination", days_old: 0 },
    ];

    for (const wh of warehouseData) {
        const result = await client.query("CreateWarehouse", wh);
        warehouses[wh.name] = result.warehouse.id;
    }

    // Create shipping routes
    const routes = [
        { from: "Main Hub", to: "North Branch", distance: 100.0 },
        { from: "Main Hub", to: "South Branch", distance: 120.0 },
        { from: "North Branch", to: "Destination", distance: 150.0 },
        { from: "South Branch", to: "Destination", distance: 140.0 },
        { from: "Main Hub", to: "East Branch", distance: 80.0 },
        { from: "East Branch", to: "Destination", distance: 130.0 },
    ];

    for (const route of routes) {
        await client.query("CreateShippingRoute", {
            from_id: warehouses[route.from],
            to_id: warehouses[route.to],
            distance: route.distance,
        });
    }

    // Find route preferring fresh data
    const result = await client.query("FindFreshRoute", {
        start_id: warehouses["Main Hub"],
        end_id: warehouses["Destination"],
    });

    console.log("Route preferring fresh data:");
    console.log("Path:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Adjusted weight:", result.result.total_weight.toFixed(2));
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create warehouses
MAIN_ID=$(curl -X POST http://localhost:6969/CreateWarehouse \
  -H 'Content-Type: application/json' \
  -d '{"name":"Main Hub","days_old":0}' | jq -r '.warehouse.id')

NORTH_ID=$(curl -X POST http://localhost:6969/CreateWarehouse \
  -H 'Content-Type: application/json' \
  -d '{"name":"North Branch","days_old":5}' | jq -r '.warehouse.id')

SOUTH_ID=$(curl -X POST http://localhost:6969/CreateWarehouse \
  -H 'Content-Type: application/json' \
  -d '{"name":"South Branch","days_old":2}' | jq -r '.warehouse.id')

EAST_ID=$(curl -X POST http://localhost:6969/CreateWarehouse \
  -H 'Content-Type: application/json' \
  -d '{"name":"East Branch","days_old":10}' | jq -r '.warehouse.id')

DEST_ID=$(curl -X POST http://localhost:6969/CreateWarehouse \
  -H 'Content-Type: application/json' \
  -d '{"name":"Destination","days_old":0}' | jq -r '.warehouse.id')

# Create routes
curl -X POST http://localhost:6969/CreateShippingRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$MAIN_ID\",\"to_id\":\"$NORTH_ID\",\"distance\":100.0}"

curl -X POST http://localhost:6969/CreateShippingRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$MAIN_ID\",\"to_id\":\"$SOUTH_ID\",\"distance\":120.0}"

curl -X POST http://localhost:6969/CreateShippingRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$NORTH_ID\",\"to_id\":\"$DEST_ID\",\"distance\":150.0}"

curl -X POST http://localhost:6969/CreateShippingRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SOUTH_ID\",\"to_id\":\"$DEST_ID\",\"distance\":140.0}"

curl -X POST http://localhost:6969/CreateShippingRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$MAIN_ID\",\"to_id\":\"$EAST_ID\",\"distance\":80.0}"

curl -X POST http://localhost:6969/CreateShippingRoute \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$EAST_ID\",\"to_id\":\"$DEST_ID\",\"distance\":130.0}"

# Find route
curl -X POST http://localhost:6969/FindFreshRoute \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$MAIN_ID\",\"end_id\":\"$DEST_ID\"}"

Example 2: Destination-aware routing with popularity weighting

QUERY FindPopularRoute(start_id: ID, end_id: ID) =>
    result <- N<Station>(start_id)
        ::ShortestPathDijkstras<Connection>(
            DIV(_::{distance}, _::ToN::{popularity})
        )
        ::To(end_id)
    RETURN result

QUERY CreateStation(name: String, popularity: F64) =>
    station <- AddN<Station>({
        name: name,
        popularity: popularity
    })
    RETURN station

QUERY CreateConnection(from_id: ID, to_id: ID, distance: F64) =>
    connection <- AddE<Connection>({ distance: distance })::From(from_id)::To(to_id)
    RETURN connection
N::Station {
    name: String,
    popularity: F64  // Visitor traffic score (higher = more popular)
}

E::Connection {
    distance: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create stations with popularity scores
# Weight = distance / destination_popularity
# Prefers routes through popular stations
stations = {}
station_data = [
    ("Entry Point", 5.0),
    ("Scenic Overlook", 8.5),    # Very popular
    ("Hidden Trail", 2.0),        # Less popular
    ("Summit View", 9.0),         # Most popular
    ("Back Route", 3.0),
]

for name, popularity in station_data:
    result = client.query("CreateStation", {
        "name": name,
        "popularity": popularity
    })
    stations[name] = result["station"]["id"]

# Create connections
connections = [
    ("Entry Point", "Scenic Overlook", 100.0),
    ("Entry Point", "Hidden Trail", 80.0),
    ("Scenic Overlook", "Summit View", 120.0),
    ("Hidden Trail", "Back Route", 90.0),
    ("Back Route", "Summit View", 110.0),
]

for from_st, to_st, distance in connections:
    client.query("CreateConnection", {
        "from_id": stations[from_st],
        "to_id": stations[to_st],
        "distance": distance
    })

# Find route preferring popular destinations
result = client.query("FindPopularRoute", {
    "start_id": stations["Entry Point"],
    "end_id": stations["Summit View"]
})

print(f"Route through popular stations:")
print(f"Path: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Popularity-adjusted weight: {result['result']['total_weight']:.2f}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create stations with popularity scores
    let mut stations: HashMap<String, String> = HashMap::new();
    let station_data = vec![
        ("Entry Point", 5.0),
        ("Scenic Overlook", 8.5),
        ("Hidden Trail", 2.0),
        ("Summit View", 9.0),
        ("Back Route", 3.0),
    ];

    for (name, popularity) in &station_data {
        let result: serde_json::Value = client.query("CreateStation", &json!({
            "name": name,
            "popularity": popularity,
        })).await?;
        stations.insert(name.to_string(), result["station"]["id"].as_str().unwrap().to_string());
    }

    // Create connections
    let connections = vec![
        ("Entry Point", "Scenic Overlook", 100.0),
        ("Entry Point", "Hidden Trail", 80.0),
        ("Scenic Overlook", "Summit View", 120.0),
        ("Hidden Trail", "Back Route", 90.0),
        ("Back Route", "Summit View", 110.0),
    ];

    for (from_st, to_st, distance) in &connections {
        let _conn: serde_json::Value = client.query("CreateConnection", &json!({
            "from_id": stations[*from_st],
            "to_id": stations[*to_st],
            "distance": distance,
        })).await?;
    }

    // Find route preferring popular destinations
    let result: serde_json::Value = client.query("FindPopularRoute", &json!({
        "start_id": stations["Entry Point"],
        "end_id": stations["Summit View"],
    })).await?;

    println!("Route through popular stations: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create stations with popularity scores
    stations := make(map[string]string)
    stationData := []struct {
        name       string
        popularity float64
    }{
        {"Entry Point", 5.0},
        {"Scenic Overlook", 8.5},
        {"Hidden Trail", 2.0},
        {"Summit View", 9.0},
        {"Back Route", 3.0},
    }

    for _, st := range stationData {
        var result map[string]any
        if err := client.Query("CreateStation", helix.WithData(map[string]any{
            "name":       st.name,
            "popularity": st.popularity,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateStation failed: %s", err)
        }
        stations[st.name] = result["station"].(map[string]any)["id"].(string)
    }

    // Create connections
    connections := []struct {
        from, to string
        distance float64
    }{
        {"Entry Point", "Scenic Overlook", 100.0},
        {"Entry Point", "Hidden Trail", 80.0},
        {"Scenic Overlook", "Summit View", 120.0},
        {"Hidden Trail", "Back Route", 90.0},
        {"Back Route", "Summit View", 110.0},
    }

    for _, conn := range connections {
        var c map[string]any
        if err := client.Query("CreateConnection", helix.WithData(map[string]any{
            "from_id":  stations[conn.from],
            "to_id":    stations[conn.to],
            "distance": conn.distance,
        })).Scan(&c); err != nil {
            log.Fatalf("CreateConnection failed: %s", err)
        }
    }

    // Find route preferring popular destinations
    var result map[string]any
    if err := client.Query("FindPopularRoute", helix.WithData(map[string]any{
        "start_id": stations["Entry Point"],
        "end_id":   stations["Summit View"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindPopularRoute failed: %s", err)
    }

    fmt.Printf("Route through popular stations: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create stations with popularity scores
    const stations: Record<string, string> = {};
    const stationData = [
        { name: "Entry Point", popularity: 5.0 },
        { name: "Scenic Overlook", popularity: 8.5 },
        { name: "Hidden Trail", popularity: 2.0 },
        { name: "Summit View", popularity: 9.0 },
        { name: "Back Route", popularity: 3.0 },
    ];

    for (const st of stationData) {
        const result = await client.query("CreateStation", st);
        stations[st.name] = result.station.id;
    }

    // Create connections
    const connections = [
        { from: "Entry Point", to: "Scenic Overlook", distance: 100.0 },
        { from: "Entry Point", to: "Hidden Trail", distance: 80.0 },
        { from: "Scenic Overlook", to: "Summit View", distance: 120.0 },
        { from: "Hidden Trail", to: "Back Route", distance: 90.0 },
        { from: "Back Route", to: "Summit View", distance: 110.0 },
    ];

    for (const conn of connections) {
        await client.query("CreateConnection", {
            from_id: stations[conn.from],
            to_id: stations[conn.to],
            distance: conn.distance,
        });
    }

    // Find route preferring popular destinations
    const result = await client.query("FindPopularRoute", {
        start_id: stations["Entry Point"],
        end_id: stations["Summit View"],
    });

    console.log("Route through popular stations:");
    console.log("Path:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Popularity-adjusted weight:", result.result.total_weight.toFixed(2));
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create stations
ENTRY_ID=$(curl -X POST http://localhost:6969/CreateStation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Entry Point","popularity":5.0}' | jq -r '.station.id')

SCENIC_ID=$(curl -X POST http://localhost:6969/CreateStation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Scenic Overlook","popularity":8.5}' | jq -r '.station.id')

HIDDEN_ID=$(curl -X POST http://localhost:6969/CreateStation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Hidden Trail","popularity":2.0}' | jq -r '.station.id')

SUMMIT_ID=$(curl -X POST http://localhost:6969/CreateStation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Summit View","popularity":9.0}' | jq -r '.station.id')

BACK_ID=$(curl -X POST http://localhost:6969/CreateStation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Back Route","popularity":3.0}' | jq -r '.station.id')

# Create connections
curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$ENTRY_ID\",\"to_id\":\"$SCENIC_ID\",\"distance\":100.0}"

curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$ENTRY_ID\",\"to_id\":\"$HIDDEN_ID\",\"distance\":80.0}"

curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SCENIC_ID\",\"to_id\":\"$SUMMIT_ID\",\"distance\":120.0}"

curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$HIDDEN_ID\",\"to_id\":\"$BACK_ID\",\"distance\":90.0}"

curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$BACK_ID\",\"to_id\":\"$SUMMIT_ID\",\"distance\":110.0}"

# Find route
curl -X POST http://localhost:6969/FindPopularRoute \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$ENTRY_ID\",\"end_id\":\"$SUMMIT_ID\"}"

Example 3: Combining source and destination contexts

QUERY FindBalancedRoute(start_id: ID, end_id: ID) =>
    result <- N<Node>(start_id)
        ::ShortestPathDijkstras<Link>(
            MUL(
                MUL(_::{cost}, _::FromN::{load_factor}),
                DIV(1, _::ToN::{capacity})
            )
        )
        ::To(end_id)
    RETURN result

QUERY CreateNode(name: String, load_factor: F64, capacity: F64) =>
    node <- AddN<Node>({
        name: name,
        load_factor: load_factor,
        capacity: capacity
    })
    RETURN node

QUERY CreateLink(from_id: ID, to_id: ID, cost: F64) =>
    link <- AddE<Link>({ cost: cost })::From(from_id)::To(to_id)
    RETURN link
N::Node {
    name: String,
    load_factor: F64,  // Current load (1.0 = normal, 2.0 = heavy)
    capacity: F64      // Available capacity
}

E::Link {
    cost: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create nodes with load and capacity
# Weight = cost * source_load / dest_capacity
# Avoids heavy sources and low-capacity destinations
nodes = {}
node_data = [
    ("Source", 1.0, 100.0),
    ("Router A", 2.5, 80.0),   # Heavy load
    ("Router B", 1.2, 120.0),  # Light load, high capacity
    ("Router C", 1.5, 60.0),
    ("Destination", 1.0, 200.0),
]

for name, load, capacity in node_data:
    result = client.query("CreateNode", {
        "name": name,
        "load_factor": load,
        "capacity": capacity
    })
    nodes[name] = result["node"]["id"]

# Create links
links = [
    ("Source", "Router A", 10.0),
    ("Source", "Router B", 15.0),
    ("Router A", "Destination", 20.0),
    ("Router B", "Router C", 12.0),
    ("Router C", "Destination", 18.0),
]

for from_node, to_node, cost in links:
    client.query("CreateLink", {
        "from_id": nodes[from_node],
        "to_id": nodes[to_node],
        "cost": cost
    })

# Find balanced route
result = client.query("FindBalancedRoute", {
    "start_id": nodes["Source"],
    "end_id": nodes["Destination"]
})

print(f"Balanced route considering load and capacity:")
print(f"Path: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Composite weight: {result['result']['total_weight']:.2f}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create nodes with load and capacity
    let mut nodes: HashMap<String, String> = HashMap::new();
    let node_data = vec![
        ("Source", 1.0, 100.0),
        ("Router A", 2.5, 80.0),
        ("Router B", 1.2, 120.0),
        ("Router C", 1.5, 60.0),
        ("Destination", 1.0, 200.0),
    ];

    for (name, load, capacity) in &node_data {
        let result: serde_json::Value = client.query("CreateNode", &json!({
            "name": name,
            "load_factor": load,
            "capacity": capacity,
        })).await?;
        nodes.insert(name.to_string(), result["node"]["id"].as_str().unwrap().to_string());
    }

    // Create links
    let links = vec![
        ("Source", "Router A", 10.0),
        ("Source", "Router B", 15.0),
        ("Router A", "Destination", 20.0),
        ("Router B", "Router C", 12.0),
        ("Router C", "Destination", 18.0),
    ];

    for (from_node, to_node, cost) in &links {
        let _link: serde_json::Value = client.query("CreateLink", &json!({
            "from_id": nodes[*from_node],
            "to_id": nodes[*to_node],
            "cost": cost,
        })).await?;
    }

    // Find balanced route
    let result: serde_json::Value = client.query("FindBalancedRoute", &json!({
        "start_id": nodes["Source"],
        "end_id": nodes["Destination"],
    })).await?;

    println!("Balanced route: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create nodes with load and capacity
    nodes := make(map[string]string)
    nodeData := []struct {
        name       string
        load       float64
        capacity   float64
    }{
        {"Source", 1.0, 100.0},
        {"Router A", 2.5, 80.0},
        {"Router B", 1.2, 120.0},
        {"Router C", 1.5, 60.0},
        {"Destination", 1.0, 200.0},
    }

    for _, node := range nodeData {
        var result map[string]any
        if err := client.Query("CreateNode", helix.WithData(map[string]any{
            "name":        node.name,
            "load_factor": node.load,
            "capacity":    node.capacity,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateNode failed: %s", err)
        }
        nodes[node.name] = result["node"].(map[string]any)["id"].(string)
    }

    // Create links
    links := []struct {
        from, to string
        cost     float64
    }{
        {"Source", "Router A", 10.0},
        {"Source", "Router B", 15.0},
        {"Router A", "Destination", 20.0},
        {"Router B", "Router C", 12.0},
        {"Router C", "Destination", 18.0},
    }

    for _, link := range links {
        var l map[string]any
        if err := client.Query("CreateLink", helix.WithData(map[string]any{
            "from_id": nodes[link.from],
            "to_id":   nodes[link.to],
            "cost":    link.cost,
        })).Scan(&l); err != nil {
            log.Fatalf("CreateLink failed: %s", err)
        }
    }

    // Find balanced route
    var result map[string]any
    if err := client.Query("FindBalancedRoute", helix.WithData(map[string]any{
        "start_id": nodes["Source"],
        "end_id":   nodes["Destination"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindBalancedRoute failed: %s", err)
    }

    fmt.Printf("Balanced route: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create nodes with load and capacity
    const nodes: Record<string, string> = {};
    const nodeData = [
        { name: "Source", load_factor: 1.0, capacity: 100.0 },
        { name: "Router A", load_factor: 2.5, capacity: 80.0 },
        { name: "Router B", load_factor: 1.2, capacity: 120.0 },
        { name: "Router C", load_factor: 1.5, capacity: 60.0 },
        { name: "Destination", load_factor: 1.0, capacity: 200.0 },
    ];

    for (const node of nodeData) {
        const result = await client.query("CreateNode", node);
        nodes[node.name] = result.node.id;
    }

    // Create links
    const links = [
        { from: "Source", to: "Router A", cost: 10.0 },
        { from: "Source", to: "Router B", cost: 15.0 },
        { from: "Router A", to: "Destination", cost: 20.0 },
        { from: "Router B", to: "Router C", cost: 12.0 },
        { from: "Router C", to: "Destination", cost: 18.0 },
    ];

    for (const link of links) {
        await client.query("CreateLink", {
            from_id: nodes[link.from],
            to_id: nodes[link.to],
            cost: link.cost,
        });
    }

    // Find balanced route
    const result = await client.query("FindBalancedRoute", {
        start_id: nodes["Source"],
        end_id: nodes["Destination"],
    });

    console.log("Balanced route considering load and capacity:");
    console.log("Path:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Composite weight:", result.result.total_weight.toFixed(2));
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create nodes
SOURCE_ID=$(curl -X POST http://localhost:6969/CreateNode \
  -H 'Content-Type: application/json' \
  -d '{"name":"Source","load_factor":1.0,"capacity":100.0}' | jq -r '.node.id')

ROUTER_A_ID=$(curl -X POST http://localhost:6969/CreateNode \
  -H 'Content-Type: application/json' \
  -d '{"name":"Router A","load_factor":2.5,"capacity":80.0}' | jq -r '.node.id')

ROUTER_B_ID=$(curl -X POST http://localhost:6969/CreateNode \
  -H 'Content-Type: application/json' \
  -d '{"name":"Router B","load_factor":1.2,"capacity":120.0}' | jq -r '.node.id')

ROUTER_C_ID=$(curl -X POST http://localhost:6969/CreateNode \
  -H 'Content-Type: application/json' \
  -d '{"name":"Router C","load_factor":1.5,"capacity":60.0}' | jq -r '.node.id')

DEST_ID=$(curl -X POST http://localhost:6969/CreateNode \
  -H 'Content-Type: application/json' \
  -d '{"name":"Destination","load_factor":1.0,"capacity":200.0}' | jq -r '.node.id')

# Create links
curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SOURCE_ID\",\"to_id\":\"$ROUTER_A_ID\",\"cost\":10.0}"

curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$SOURCE_ID\",\"to_id\":\"$ROUTER_B_ID\",\"cost\":15.0}"

curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$ROUTER_A_ID\",\"to_id\":\"$DEST_ID\",\"cost\":20.0}"

curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$ROUTER_B_ID\",\"to_id\":\"$ROUTER_C_ID\",\"cost\":12.0}"

curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$ROUTER_C_ID\",\"to_id\":\"$DEST_ID\",\"cost\":18.0}"

# Find balanced route
curl -X POST http://localhost:6969/FindBalancedRoute \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$SOURCE_ID\",\"end_id\":\"$DEST_ID\"}"

Real-World Use Cases

Traffic-Aware Navigation

// Penalize routes starting from congested areas
::ShortestPathDijkstras<Road>(MUL(_::{distance}, _::FromN::{congestion}))

Cost Optimization

// Minimize cost considering both base rate and destination fees
::ShortestPathDijkstras<Route>(ADD(_::{base_cost}, _::ToN::{destination_fee}))

Load Balancing

// Distribute traffic away from heavily loaded servers
::ShortestPathDijkstras<Connection>(DIV(_::{latency}, SUB(1, _::ToN::{load_percent})))

Capacity Planning

// Prefer high-capacity destinations
::ShortestPathDijkstras<Link>(DIV(_::{distance}, _::ToN::{available_capacity}))

Best Practices

1. Property Normalization

Ensure properties are on similar scales:

// Good: Both factors are normalized 0-1
MUL(_::{distance}, ADD(_::FromN::{traffic}, _::ToN::{congestion}))

// Careful: Distance in miles, traffic as percentage
MUL(_::{distance}, _::FromN::{traffic_percent})  // May need scaling

2. Avoid Division by Zero

Protect against zero denominators:

// Add small epsilon or use MAX
DIV(_::{distance}, ADD(_::ToN::{popularity}, 0.01))

3. Index Key Properties

For best performance, index properties used in weight calculations:

// Index frequently-accessed properties
N::City {
    @index traffic_factor: F64,
    @index popularity: F64
}

4. Test Weight Distributions

Verify weights produce expected behavior:

  • Log sample weights during development
  • Ensure non-negative values
  • Check for reasonable ranges

Advanced Weight Expressions

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Advanced Weight Expressions  

Helix supports sophisticated mathematical expressions for calculating path weights. By combining mathematical functions with property contexts, you can model complex real-world routing scenarios with exponential decay, multi-factor scoring, conditional logic, and more.

ℹ️ Note

All mathematical functions available in Helix can be used in weight expressions. See the Mathematical Functions reference for the complete list.

Available Mathematical Functions

Arithmetic Operations

  • ADD(a, b) - Addition
  • SUB(a, b) - Subtraction
  • MUL(a, b) - Multiplication
  • DIV(a, b) - Division
  • MOD(a, b) - Modulo (remainder)

Power & Exponential

  • POW(base, exponent) - Power
  • SQRT(x) - Square root
  • EXP(x) - e^x (exponential)
  • LN(x) - Natural logarithm
  • LOG(x, base) - Logarithm with custom base

Rounding Functions

  • CEIL(x) - Round up
  • FLOOR(x) - Round down
  • ROUND(x) - Round to nearest
  • ABS(x) - Absolute value

Trigonometric Functions

  • SIN(x) - Sine
  • COS(x) - Cosine
  • TAN(x) - Tangent

Constants

  • PI() - π (3.14159…)

Example 1: Exponential time decay

QUERY FindFreshestPath(start_id: ID, end_id: ID) =>
    result <- N<DataCenter>(start_id)
        ::ShortestPathDijkstras<Link>(
            MUL(_::{distance}, POW(0.95, DIV(_::{days_since_update}, 30)))
        )
        ::To(end_id)
    RETURN result

QUERY CreateDataCenter(name: String) =>
    datacenter <- AddN<DataCenter>({ name: name })
    RETURN datacenter

QUERY CreateLink(from_id: ID, to_id: ID, distance: F64, days_old: I64) =>
    link <- AddE<Link>({ distance: distance, days_since_update: days_old })::From(from_id)::To(to_id)
    RETURN link
N::DataCenter {
    name: String
}

E::Link {
    distance: F64,
    days_since_update: I64  // Age of route information
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Weight = distance * 0.95^(days_since_update/30)
# Exponential decay: fresher routes get lower weights

# Create data centers
datacenters = {}
for name in ["DC1", "DC2", "DC3", "DC4", "Target"]:
    result = client.query("CreateDataCenter", {"name": name})
    datacenters[name] = result["datacenter"]["id"]

# Create links with varying freshness
# Format: (from, to, distance, days_old)
links = [
    ("DC1", "DC2", 100.0, 0),     # Fresh route
    ("DC1", "DC3", 90.0, 60),     # Stale route (2 months)
    ("DC2", "Target", 120.0, 10), # Recent route
    ("DC3", "DC4", 80.0, 90),     # Very stale (3 months)
    ("DC4", "Target", 110.0, 5),  # Fresh route
]

for from_dc, to_dc, distance, days_old in links:
    client.query("CreateLink", {
        "from_id": datacenters[from_dc],
        "to_id": datacenters[to_dc],
        "distance": distance,
        "days_old": days_old
    })

# Find path preferring fresh routes
result = client.query("FindFreshestPath", {
    "start_id": datacenters["DC1"],
    "end_id": datacenters["Target"]
})

print(f"Path using exponential time decay:")
print(f"Route: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Decay-adjusted weight: {result['result']['total_weight']:.2f}")
print("\nFresher routes are preferred over stale ones")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create data centers
    let mut datacenters: HashMap<String, String> = HashMap::new();
    for name in ["DC1", "DC2", "DC3", "DC4", "Target"] {
        let result: serde_json::Value = client.query("CreateDataCenter", &json!({
            "name": name,
        })).await?;
        datacenters.insert(name.to_string(), result["datacenter"]["id"].as_str().unwrap().to_string());
    }

    // Create links with varying freshness
    let links = vec![
        ("DC1", "DC2", 100.0, 0),
        ("DC1", "DC3", 90.0, 60),
        ("DC2", "Target", 120.0, 10),
        ("DC3", "DC4", 80.0, 90),
        ("DC4", "Target", 110.0, 5),
    ];

    for (from_dc, to_dc, distance, days_old) in &links {
        let _link: serde_json::Value = client.query("CreateLink", &json!({
            "from_id": datacenters[*from_dc],
            "to_id": datacenters[*to_dc],
            "distance": distance,
            "days_old": days_old,
        })).await?;
    }

    // Find path preferring fresh routes
    let result: serde_json::Value = client.query("FindFreshestPath", &json!({
        "start_id": datacenters["DC1"],
        "end_id": datacenters["Target"],
    })).await?;

    println!("Path using exponential time decay: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create data centers
    datacenters := make(map[string]string)
    dcNames := []string{"DC1", "DC2", "DC3", "DC4", "Target"}

    for _, name := range dcNames {
        var result map[string]any
        if err := client.Query("CreateDataCenter", helix.WithData(map[string]any{
            "name": name,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateDataCenter failed: %s", err)
        }
        datacenters[name] = result["datacenter"].(map[string]any)["id"].(string)
    }

    // Create links with varying freshness
    links := []struct {
        from, to string
        distance float64
        daysOld  int64
    }{
        {"DC1", "DC2", 100.0, 0},
        {"DC1", "DC3", 90.0, 60},
        {"DC2", "Target", 120.0, 10},
        {"DC3", "DC4", 80.0, 90},
        {"DC4", "Target", 110.0, 5},
    }

    for _, link := range links {
        var l map[string]any
        if err := client.Query("CreateLink", helix.WithData(map[string]any{
            "from_id":  datacenters[link.from],
            "to_id":    datacenters[link.to],
            "distance": link.distance,
            "days_old": link.daysOld,
        })).Scan(&l); err != nil {
            log.Fatalf("CreateLink failed: %s", err)
        }
    }

    // Find path preferring fresh routes
    var result map[string]any
    if err := client.Query("FindFreshestPath", helix.WithData(map[string]any{
        "start_id": datacenters["DC1"],
        "end_id":   datacenters["Target"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindFreshestPath failed: %s", err)
    }

    fmt.Printf("Path using exponential time decay: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create data centers
    const datacenters: Record<string, string> = {};
    const dcNames = ["DC1", "DC2", "DC3", "DC4", "Target"];

    for (const name of dcNames) {
        const result = await client.query("CreateDataCenter", { name });
        datacenters[name] = result.datacenter.id;
    }

    // Create links with varying freshness
    const links = [
        { from: "DC1", to: "DC2", distance: 100.0, days_old: 0 },
        { from: "DC1", to: "DC3", distance: 90.0, days_old: 60 },
        { from: "DC2", to: "Target", distance: 120.0, days_old: 10 },
        { from: "DC3", to: "DC4", distance: 80.0, days_old: 90 },
        { from: "DC4", to: "Target", distance: 110.0, days_old: 5 },
    ];

    for (const link of links) {
        await client.query("CreateLink", {
            from_id: datacenters[link.from],
            to_id: datacenters[link.to],
            distance: link.distance,
            days_old: link.days_old,
        });
    }

    // Find path preferring fresh routes
    const result = await client.query("FindFreshestPath", {
        start_id: datacenters["DC1"],
        end_id: datacenters["Target"],
    });

    console.log("Path using exponential time decay:");
    console.log("Route:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Decay-adjusted weight:", result.result.total_weight.toFixed(2));
    console.log("\nFresher routes are preferred over stale ones");
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create data centers
DC1_ID=$(curl -X POST http://localhost:6969/CreateDataCenter \
  -H 'Content-Type: application/json' \
  -d '{"name":"DC1"}' | jq -r '.datacenter.id')

DC2_ID=$(curl -X POST http://localhost:6969/CreateDataCenter \
  -H 'Content-Type: application/json' \
  -d '{"name":"DC2"}' | jq -r '.datacenter.id')

DC3_ID=$(curl -X POST http://localhost:6969/CreateDataCenter \
  -H 'Content-Type: application/json' \
  -d '{"name":"DC3"}' | jq -r '.datacenter.id')

DC4_ID=$(curl -X POST http://localhost:6969/CreateDataCenter \
  -H 'Content-Type: application/json' \
  -d '{"name":"DC4"}' | jq -r '.datacenter.id')

TARGET_ID=$(curl -X POST http://localhost:6969/CreateDataCenter \
  -H 'Content-Type: application/json' \
  -d '{"name":"Target"}' | jq -r '.datacenter.id')

# Create links with varying freshness
curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$DC1_ID\",\"to_id\":\"$DC2_ID\",\"distance\":100.0,\"days_old\":0}"

curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$DC1_ID\",\"to_id\":\"$DC3_ID\",\"distance\":90.0,\"days_old\":60}"

curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$DC2_ID\",\"to_id\":\"$TARGET_ID\",\"distance\":120.0,\"days_old\":10}"

curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$DC3_ID\",\"to_id\":\"$DC4_ID\",\"distance\":80.0,\"days_old\":90}"

curl -X POST http://localhost:6969/CreateLink \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$DC4_ID\",\"to_id\":\"$TARGET_ID\",\"distance\":110.0,\"days_old\":5}"

# Find path preferring fresh routes
curl -X POST http://localhost:6969/FindFreshestPath \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$DC1_ID\",\"end_id\":\"$TARGET_ID\"}"

How it works:

  • POW(0.95, DIV(days_since_update, 30)) creates exponential decay
  • At 0 days: multiplier = 1.0 (no penalty)
  • At 30 days: multiplier = 0.95 (5% increase)
  • At 60 days: multiplier = 0.90 (10% increase)
  • At 90 days: multiplier = 0.86 (14% increase)

Example 2: Multi-factor composite scoring

QUERY FindOptimalPath(start_id: ID, end_id: ID) =>
    result <- N<Server>(start_id)
        ::ShortestPathDijkstras<Connection>(
            ADD(
                MUL(_::{latency}, 0.4),
                ADD(
                    MUL(DIV(1, _::{bandwidth}), 0.3),
                    MUL(
                        SUB(1, _::{reliability}),
                        0.3
                    )
                )
            )
        )
        ::To(end_id)
    RETURN result

QUERY CreateServer(name: String) =>
    server <- AddN<Server>({ name: name })
    RETURN server

QUERY CreateConnection(from_id: ID, to_id: ID, latency: F64, bandwidth: F64, reliability: F64) =>
    connection <- AddE<Connection>({ latency: latency, bandwidth: bandwidth, reliability: reliability })::From(from_id)::To(to_id)
    RETURN connection
N::Server {
    name: String
}

E::Connection {
    latency: F64,        // Lower is better (ms)
    bandwidth: F64,      // Higher is better (Gbps)
    reliability: F64     // Higher is better (0.0-1.0)
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Composite weight: 40% latency + 30% reciprocal bandwidth + 30% unreliability
# Balances multiple competing factors

# Create servers
servers = {}
for name in ["S1", "S2", "S3", "S4", "S5"]:
    result = client.query("CreateServer", {"name": name})
    servers[name] = result["server"]["id"]

# Create connections with different characteristics
# Format: (from, to, latency_ms, bandwidth_gbps, reliability_0_to_1)
connections = [
    ("S1", "S2", 10.0, 10.0, 0.99),   # Low latency, high bandwidth, reliable
    ("S1", "S3", 5.0, 5.0, 0.85),     # Lowest latency, medium bandwidth, less reliable
    ("S2", "S5", 20.0, 20.0, 0.98),   # Higher latency, highest bandwidth
    ("S3", "S4", 15.0, 8.0, 0.90),    # Balanced
    ("S4", "S5", 8.0, 12.0, 0.95),    # Good all-around
]

for from_srv, to_srv, latency, bandwidth, reliability in connections:
    client.query("CreateConnection", {
        "from_id": servers[from_srv],
        "to_id": servers[to_srv],
        "latency": latency,
        "bandwidth": bandwidth,
        "reliability": reliability
    })

# Find optimal path balancing all factors
result = client.query("FindOptimalPath", {
    "start_id": servers["S1"],
    "end_id": servers["S5"]
})

print(f"Multi-factor optimized path:")
print(f"Route: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Composite score: {result['result']['total_weight']:.3f}")
print(f"(40% latency + 30% inv_bandwidth + 30% unreliability)")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create servers
    let mut servers: HashMap<String, String> = HashMap::new();
    for name in ["S1", "S2", "S3", "S4", "S5"] {
        let result: serde_json::Value = client.query("CreateServer", &json!({
            "name": name,
        })).await?;
        servers.insert(name.to_string(), result["server"]["id"].as_str().unwrap().to_string());
    }

    // Create connections with different characteristics
    let connections = vec![
        ("S1", "S2", 10.0, 10.0, 0.99),
        ("S1", "S3", 5.0, 5.0, 0.85),
        ("S2", "S5", 20.0, 20.0, 0.98),
        ("S3", "S4", 15.0, 8.0, 0.90),
        ("S4", "S5", 8.0, 12.0, 0.95),
    ];

    for (from_srv, to_srv, latency, bandwidth, reliability) in &connections {
        let _conn: serde_json::Value = client.query("CreateConnection", &json!({
            "from_id": servers[*from_srv],
            "to_id": servers[*to_srv],
            "latency": latency,
            "bandwidth": bandwidth,
            "reliability": reliability,
        })).await?;
    }

    // Find optimal path balancing all factors
    let result: serde_json::Value = client.query("FindOptimalPath", &json!({
        "start_id": servers["S1"],
        "end_id": servers["S5"],
    })).await?;

    println!("Multi-factor optimized path: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create servers
    servers := make(map[string]string)
    serverNames := []string{"S1", "S2", "S3", "S4", "S5"}

    for _, name := range serverNames {
        var result map[string]any
        if err := client.Query("CreateServer", helix.WithData(map[string]any{
            "name": name,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateServer failed: %s", err)
        }
        servers[name] = result["server"].(map[string]any)["id"].(string)
    }

    // Create connections with different characteristics
    connections := []struct {
        from, to    string
        latency     float64
        bandwidth   float64
        reliability float64
    }{
        {"S1", "S2", 10.0, 10.0, 0.99},
        {"S1", "S3", 5.0, 5.0, 0.85},
        {"S2", "S5", 20.0, 20.0, 0.98},
        {"S3", "S4", 15.0, 8.0, 0.90},
        {"S4", "S5", 8.0, 12.0, 0.95},
    }

    for _, conn := range connections {
        var c map[string]any
        if err := client.Query("CreateConnection", helix.WithData(map[string]any{
            "from_id":     servers[conn.from],
            "to_id":       servers[conn.to],
            "latency":     conn.latency,
            "bandwidth":   conn.bandwidth,
            "reliability": conn.reliability,
        })).Scan(&c); err != nil {
            log.Fatalf("CreateConnection failed: %s", err)
        }
    }

    // Find optimal path balancing all factors
    var result map[string]any
    if err := client.Query("FindOptimalPath", helix.WithData(map[string]any{
        "start_id": servers["S1"],
        "end_id":   servers["S5"],
    })).Scan(&result); err != nil {
        log.Fatalf("FindOptimalPath failed: %s", err)
    }

    fmt.Printf("Multi-factor optimized path: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create servers
    const servers: Record<string, string> = {};
    const serverNames = ["S1", "S2", "S3", "S4", "S5"];

    for (const name of serverNames) {
        const result = await client.query("CreateServer", { name });
        servers[name] = result.server.id;
    }

    // Create connections with different characteristics
    const connections = [
        { from: "S1", to: "S2", latency: 10.0, bandwidth: 10.0, reliability: 0.99 },
        { from: "S1", to: "S3", latency: 5.0, bandwidth: 5.0, reliability: 0.85 },
        { from: "S2", to: "S5", latency: 20.0, bandwidth: 20.0, reliability: 0.98 },
        { from: "S3", to: "S4", latency: 15.0, bandwidth: 8.0, reliability: 0.90 },
        { from: "S4", to: "S5", latency: 8.0, bandwidth: 12.0, reliability: 0.95 },
    ];

    for (const conn of connections) {
        await client.query("CreateConnection", {
            from_id: servers[conn.from],
            to_id: servers[conn.to],
            latency: conn.latency,
            bandwidth: conn.bandwidth,
            reliability: conn.reliability,
        });
    }

    // Find optimal path balancing all factors
    const result = await client.query("FindOptimalPath", {
        start_id: servers["S1"],
        end_id: servers["S5"],
    });

    console.log("Multi-factor optimized path:");
    console.log("Route:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Composite score:", result.result.total_weight.toFixed(3));
    console.log("(40% latency + 30% inv_bandwidth + 30% unreliability)");
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create servers
S1_ID=$(curl -X POST http://localhost:6969/CreateServer \
  -H 'Content-Type: application/json' \
  -d '{"name":"S1"}' | jq -r '.server.id')

S2_ID=$(curl -X POST http://localhost:6969/CreateServer \
  -H 'Content-Type: application/json' \
  -d '{"name":"S2"}' | jq -r '.server.id')

S3_ID=$(curl -X POST http://localhost:6969/CreateServer \
  -H 'Content-Type: application/json' \
  -d '{"name":"S3"}' | jq -r '.server.id')

S4_ID=$(curl -X POST http://localhost:6969/CreateServer \
  -H 'Content-Type: application/json' \
  -d '{"name":"S4"}' | jq -r '.server.id')

S5_ID=$(curl -X POST http://localhost:6969/CreateServer \
  -H 'Content-Type: application/json' \
  -d '{"name":"S5"}' | jq -r '.server.id')

# Create connections with different characteristics
curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$S1_ID\",\"to_id\":\"$S2_ID\",\"latency\":10.0,\"bandwidth\":10.0,\"reliability\":0.99}"

curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$S1_ID\",\"to_id\":\"$S3_ID\",\"latency\":5.0,\"bandwidth\":5.0,\"reliability\":0.85}"

curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$S2_ID\",\"to_id\":\"$S5_ID\",\"latency\":20.0,\"bandwidth\":20.0,\"reliability\":0.98}"

curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$S3_ID\",\"to_id\":\"$S4_ID\",\"latency\":15.0,\"bandwidth\":8.0,\"reliability\":0.90}"

curl -X POST http://localhost:6969/CreateConnection \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$S4_ID\",\"to_id\":\"$S5_ID\",\"latency\":8.0,\"bandwidth\":12.0,\"reliability\":0.95}"

# Find optimal path balancing all factors
curl -X POST http://localhost:6969/FindOptimalPath \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$S1_ID\",\"end_id\":\"$S5_ID\"}"

Weight breakdown:

  • 40% latency: Direct contribution (lower is better)
  • 30% reciprocal bandwidth: 1/bandwidth (lower bandwidth → higher cost)
  • 30% unreliability: (1 - reliability) (less reliable → higher cost)

Example 3: Conditional weights with thresholds

QUERY FindConditionalPath(start_id: ID, end_id: ID, threshold: F64) =>
    result <- N<Router>(start_id)
        ::ShortestPathDijkstras<Cable>(
            MUL(
                _::{length},
                ADD(
                    1,
                    MUL(CEIL(DIV(SUB(_::{capacity_used}, threshold), 10)), 0.5)
                )
            )
        )
        ::To(end_id)
    RETURN result

QUERY CreateRouter(name: String) =>
    router <- AddN<Router>({ name: name })
    RETURN router

QUERY CreateCable(from_id: ID, to_id: ID, length: F64, capacity_used: F64) =>
    cable <- AddE<Cable>({ length: length, capacity_used: capacity_used })::From(from_id)::To(to_id)
    RETURN cable
N::Router {
    name: String
}

E::Cable {
    length: F64,
    capacity_used: F64  // Percentage of capacity used (0-100)
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Conditional weight: Adds penalty when capacity exceeds threshold
# Weight = length * (1 + ceil((capacity_used - threshold) / 10) * 0.5)
# Penalizes overcapacity cables progressively

# Create routers
routers = {}
for name in ["R1", "R2", "R3", "R4", "R5"]:
    result = client.query("CreateRouter", {"name": name})
    routers[name] = result["router"]["id"]

# Create cables with varying capacity usage
# Format: (from, to, length_km, capacity_percent)
cables = [
    ("R1", "R2", 100.0, 45.0),   # Below threshold
    ("R1", "R3", 90.0, 85.0),    # Above threshold
    ("R2", "R4", 110.0, 50.0),   # Below threshold
    ("R3", "R4", 95.0, 95.0),    # Way above threshold
    ("R4", "R5", 105.0, 60.0),   # Slightly above threshold
]

for from_r, to_r, length, capacity in cables:
    client.query("CreateCable", {
        "from_id": routers[from_r],
        "to_id": routers[to_r],
        "length": length,
        "capacity_used": capacity
    })

# Find path with 70% capacity threshold
# Cables above 70% get progressive penalties
result = client.query("FindConditionalPath", {
    "start_id": routers["R1"],
    "end_id": routers["R5"],
    "threshold": 70.0
})

print(f"Path avoiding overcapacity cables (>70%):")
print(f"Route: {' -> '.join([node['name'] for node in result['result']['path']])}")
print(f"Penalty-adjusted weight: {result['result']['total_weight']:.2f}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use std::collections::HashMap;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create routers
    let mut routers: HashMap<String, String> = HashMap::new();
    for name in ["R1", "R2", "R3", "R4", "R5"] {
        let result: serde_json::Value = client.query("CreateRouter", &json!({
            "name": name,
        })).await?;
        routers.insert(name.to_string(), result["router"]["id"].as_str().unwrap().to_string());
    }

    // Create cables with varying capacity usage
    let cables = vec![
        ("R1", "R2", 100.0, 45.0),
        ("R1", "R3", 90.0, 85.0),
        ("R2", "R4", 110.0, 50.0),
        ("R3", "R4", 95.0, 95.0),
        ("R4", "R5", 105.0, 60.0),
    ];

    for (from_r, to_r, length, capacity) in &cables {
        let _cable: serde_json::Value = client.query("CreateCable", &json!({
            "from_id": routers[*from_r],
            "to_id": routers[*to_r],
            "length": length,
            "capacity_used": capacity,
        })).await?;
    }

    // Find path with 70% capacity threshold
    let result: serde_json::Value = client.query("FindConditionalPath", &json!({
        "start_id": routers["R1"],
        "end_id": routers["R5"],
        "threshold": 70.0,
    })).await?;

    println!("Path avoiding overcapacity cables: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create routers
    routers := make(map[string]string)
    routerNames := []string{"R1", "R2", "R3", "R4", "R5"}

    for _, name := range routerNames {
        var result map[string]any
        if err := client.Query("CreateRouter", helix.WithData(map[string]any{
            "name": name,
        })).Scan(&result); err != nil {
            log.Fatalf("CreateRouter failed: %s", err)
        }
        routers[name] = result["router"].(map[string]any)["id"].(string)
    }

    // Create cables with varying capacity usage
    cables := []struct {
        from, to     string
        length       float64
        capacityUsed float64
    }{
        {"R1", "R2", 100.0, 45.0},
        {"R1", "R3", 90.0, 85.0},
        {"R2", "R4", 110.0, 50.0},
        {"R3", "R4", 95.0, 95.0},
        {"R4", "R5", 105.0, 60.0},
    }

    for _, cable := range cables {
        var c map[string]any
        if err := client.Query("CreateCable", helix.WithData(map[string]any{
            "from_id":       routers[cable.from],
            "to_id":         routers[cable.to],
            "length":        cable.length,
            "capacity_used": cable.capacityUsed,
        })).Scan(&c); err != nil {
            log.Fatalf("CreateCable failed: %s", err)
        }
    }

    // Find path with 70% capacity threshold
    var result map[string]any
    if err := client.Query("FindConditionalPath", helix.WithData(map[string]any{
        "start_id": routers["R1"],
        "end_id":   routers["R5"],
        "threshold": 70.0,
    })).Scan(&result); err != nil {
        log.Fatalf("FindConditionalPath failed: %s", err)
    }

    fmt.Printf("Path avoiding overcapacity cables: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create routers
    const routers: Record<string, string> = {};
    const routerNames = ["R1", "R2", "R3", "R4", "R5"];

    for (const name of routerNames) {
        const result = await client.query("CreateRouter", { name });
        routers[name] = result.router.id;
    }

    // Create cables with varying capacity usage
    const cables = [
        { from: "R1", to: "R2", length: 100.0, capacity_used: 45.0 },
        { from: "R1", to: "R3", length: 90.0, capacity_used: 85.0 },
        { from: "R2", to: "R4", length: 110.0, capacity_used: 50.0 },
        { from: "R3", to: "R4", length: 95.0, capacity_used: 95.0 },
        { from: "R4", to: "R5", length: 105.0, capacity_used: 60.0 },
    ];

    for (const cable of cables) {
        await client.query("CreateCable", {
            from_id: routers[cable.from],
            to_id: routers[cable.to],
            length: cable.length,
            capacity_used: cable.capacity_used,
        });
    }

    // Find path with 70% capacity threshold
    const result = await client.query("FindConditionalPath", {
        start_id: routers["R1"],
        end_id: routers["R5"],
        threshold: 70.0,
    });

    console.log("Path avoiding overcapacity cables (>70%):");
    console.log("Route:", result.result.path.map((n: any) => n.name).join(" -> "));
    console.log("Penalty-adjusted weight:", result.result.total_weight.toFixed(2));
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create routers
R1_ID=$(curl -X POST http://localhost:6969/CreateRouter \
  -H 'Content-Type: application/json' \
  -d '{"name":"R1"}' | jq -r '.router.id')

R2_ID=$(curl -X POST http://localhost:6969/CreateRouter \
  -H 'Content-Type: application/json' \
  -d '{"name":"R2"}' | jq -r '.router.id')

R3_ID=$(curl -X POST http://localhost:6969/CreateRouter \
  -H 'Content-Type: application/json' \
  -d '{"name":"R3"}' | jq -r '.router.id')

R4_ID=$(curl -X POST http://localhost:6969/CreateRouter \
  -H 'Content-Type: application/json' \
  -d '{"name":"R4"}' | jq -r '.router.id')

R5_ID=$(curl -X POST http://localhost:6969/CreateRouter \
  -H 'Content-Type: application/json' \
  -d '{"name":"R5"}' | jq -r '.router.id')

# Create cables with varying capacity usage
curl -X POST http://localhost:6969/CreateCable \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$R1_ID\",\"to_id\":\"$R2_ID\",\"length\":100.0,\"capacity_used\":45.0}"

curl -X POST http://localhost:6969/CreateCable \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$R1_ID\",\"to_id\":\"$R3_ID\",\"length\":90.0,\"capacity_used\":85.0}"

curl -X POST http://localhost:6969/CreateCable \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$R2_ID\",\"to_id\":\"$R4_ID\",\"length\":110.0,\"capacity_used\":50.0}"

curl -X POST http://localhost:6969/CreateCable \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$R3_ID\",\"to_id\":\"$R4_ID\",\"length\":95.0,\"capacity_used\":95.0}"

curl -X POST http://localhost:6969/CreateCable \
  -H 'Content-Type: application/json' \
  -d "{\"from_id\":\"$R4_ID\",\"to_id\":\"$R5_ID\",\"length\":105.0,\"capacity_used\":60.0}"

# Find path with 70% capacity threshold
curl -X POST http://localhost:6969/FindConditionalPath \
  -H 'Content-Type: application/json' \
  -d "{\"start_id\":\"$R1_ID\",\"end_id\":\"$R5_ID\",\"threshold\":70.0}"

How conditional weighting works:

  • Below threshold: No penalty (multiplier = 1)
  • Above threshold: Progressive penalty based on excess
  • Uses CEIL to create step-function penalties
  • Every 10% over threshold adds 0.5x multiplier

Complex Expression Patterns

Vector Distance with Property Weighting

// Euclidean-style distance combining two properties
SQRT(ADD(POW(_::{distance}, 2), POW(MUL(1000, SUB(1, _::{reliability})), 2)))

Logarithmic Scaling for Large Values

// Compress large bandwidth values logarithmically
MUL(_::{distance}, LN(ADD(_::{bandwidth}, 1)))

Periodic Patterns with Trigonometry

// Prefer routes updated on certain days (weekly cycle)
MUL(_::{distance}, ADD(1, MUL(SIN(DIV(_::{days_since_update}, 7)), 0.2)))

Quantized Weights

// Round to nearest 10 for simplified routing
MUL(ROUND(DIV(_::{distance}, 10)), 10)

Performance Considerations

Expression Complexity Impact

ComplexityOperationsPerformance Impact
SimpleSingle propertyMinimal (1% overhead)
Moderate2-5 operationsLow (1-5% overhead)
Complex6-15 operationsModerate (5-15% overhead)
Very Complex15+ operationsHigher (15-30% overhead)

Optimization Tips

  1. Pre-calculate when possible: Store derived values as properties
// Instead of: POW(0.95, DIV(days, 30))
// Pre-calculate: decay_factor property
  1. Simplify expressions: Combine constants
// Instead of: MUL(MUL(_::{x}, 0.3), 0.4)
// Use: MUL(_::{x}, 0.12)
  1. Avoid expensive operations in hot paths:

    • Trigonometric functions (SIN, COS, TAN) are slower
    • Multiple POW operations add up
    • Consider lookup tables for complex functions
  2. Profile your queries: Test with realistic data volumes

Common Patterns Library

Time-based Decay

// Exponential: POW(0.95, DIV(age, period))
// Linear: MUL(distance, ADD(1, DIV(age, 100)))
// Step: MUL(distance, ADD(1, FLOOR(DIV(age, 30))))

Multi-factor Scoring

// Weighted sum: ADD(MUL(factor1, w1), MUL(factor2, w2))
// Geometric mean: SQRT(MUL(factor1, factor2))
// Harmonic mean: DIV(2, ADD(DIV(1, f1), DIV(1, f2)))

Threshold-based Penalties

// Hard threshold: IF(GT(value, thresh), penalty, 1)
// Soft threshold: ADD(1, MUL(MAX(0, SUB(value, thresh)), rate))
// Step function: CEIL(DIV(MAX(0, SUB(value, thresh)), step))

Normalization

// Min-max: DIV(SUB(value, min), SUB(max, min))
// Z-score: DIV(SUB(value, mean), stddev)
// Log scale: LN(ADD(value, 1))

Aggregation

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Group by properties  

Group elements by specific properties to get count summaries.

::GROUP_BY(property1, property2, ...)

Returns count summaries:

[
    {'property': 25, 'count': 3}, 
    {'property': 30, 'count': 2},
    {'property': 35, 'count': 1},
    ...
]

ℹ️ Note

GROUP_BY returns count summaries for each unique combination of the specified properties, useful for analytics and data distribution analysis.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Group users by age

QUERY GroupUsersByAge () =>
    users <- N<User>
    RETURN users::GROUP_BY(age)

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 25, "email": "charlie@example.com"},
    {"name": "Diana", "age": 30, "email": "diana@example.com"},
    {"name": "Eve", "age": 35, "email": "eve@example.com"},
    {"name": "Frank", "age": 25, "email": "frank@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GroupUsersByAge", {})
print("Users grouped by age:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 25, "charlie@example.com"),
        ("Diana", 30, "diana@example.com"),
        ("Eve", 35, "eve@example.com"),
        ("Frank", 25, "frank@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GroupUsersByAge", &json!({})).await?;
    println!("Users grouped by age: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(25), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(30), "email": "diana@example.com"},
        {"name": "Eve", "age": uint8(35), "email": "eve@example.com"},
        {"name": "Frank", "age": uint8(25), "email": "frank@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GroupUsersByAge", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GroupUsersByAge failed: %s", err)
    }

    fmt.Printf("Users grouped by age: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 25, email: "charlie@example.com" },
        { name: "Diana", age: 30, email: "diana@example.com" },
        { name: "Eve", age: 35, email: "eve@example.com" },
        { name: "Frank", age: 25, email: "frank@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GroupUsersByAge", {});
    console.log("Users grouped by age:", result);
}

main().catch((err) => {
    console.error("GroupUsersByAge query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":25,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":30,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","age":35,"email":"eve@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Frank","age":25,"email":"frank@example.com"}'

curl -X POST \
  http://localhost:6969/GroupUsersByAge \
  -H 'Content-Type: application/json' \
  -d '{}'

Aggregate by properties  

Aggregate elements by specific properties to get detailed data grouped by those properties.

::AGGREGATE_BY(property1, property2, ...)

Returns grouped data with full objects:

#![allow(unused)]
fn main() {
[
    {
        'count': 3,
        'data': [
            {"property1": 25, "property2": "Alice", ...},
            {"property1": 25, "property2": "Charlie", ...},
            {"property1": 25, "property2": "Frank", ...}
        ]
    },
    {
        'count': 2,
        'data': [
            {"property1": 30, "property2": "Bob", ...},
            {"property1": 30, "property2": "Diana", ...}
        ]
    },
    ...
]
}

ℹ️ Note

AGGREGATE_BY returns the full data objects grouped by the specified properties, providing both count and the actual grouped elements for detailed analysis.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Aggregate users by age and email domain

QUERY AggregateUsersByAge () =>
    users <- N<User>
    RETURN users::AGGREGATE_BY(age)

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice Johnson", "age": 25, "email": "alice@company.com"},
    {"name": "Bob Smith", "age": 30, "email": "bob@startup.io"},
    {"name": "Charlie Brown", "age": 25, "email": "charlie@company.com"},
    {"name": "Diana Prince", "age": 30, "email": "diana@enterprise.org"},
    {"name": "Eve Wilson", "age": 35, "email": "eve@freelance.net"},
    {"name": "Frank Miller", "age": 25, "email": "frank@startup.io"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("AggregateUsersByAge", {})
print("Users aggregated by age:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice Johnson", 25, "alice@company.com"),
        ("Bob Smith", 30, "bob@startup.io"),
        ("Charlie Brown", 25, "charlie@company.com"),
        ("Diana Prince", 30, "diana@enterprise.org"),
        ("Eve Wilson", 35, "eve@freelance.net"),
        ("Frank Miller", 25, "frank@startup.io"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("AggregateUsersByAge", &json!({})).await?;
    println!("Users aggregated by age: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice Johnson", "age": uint8(25), "email": "alice@company.com"},
        {"name": "Bob Smith", "age": uint8(30), "email": "bob@startup.io"},
        {"name": "Charlie Brown", "age": uint8(25), "email": "charlie@company.com"},
        {"name": "Diana Prince", "age": uint8(30), "email": "diana@enterprise.org"},
        {"name": "Eve Wilson", "age": uint8(35), "email": "eve@freelance.net"},
        {"name": "Frank Miller", "age": uint8(25), "email": "frank@startup.io"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("AggregateUsersByAge", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("AggregateUsersByAge failed: %s", err)
    }

    fmt.Printf("Users aggregated by age: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice Johnson", age: 25, email: "alice@company.com" },
        { name: "Bob Smith", age: 30, email: "bob@startup.io" },
        { name: "Charlie Brown", age: 25, email: "charlie@company.com" },
        { name: "Diana Prince", age: 30, email: "diana@enterprise.org" },
        { name: "Eve Wilson", age: 35, email: "eve@freelance.net" },
        { name: "Frank Miller", age: 25, email: "frank@startup.io" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("AggregateUsersByAge", {});
    console.log("Users aggregated by age:", result);
}

main().catch((err) => {
    console.error("AggregateUsersByAge query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice Johnson","age":25,"email":"alice@company.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob Smith","age":30,"email":"bob@startup.io"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie Brown","age":25,"email":"charlie@company.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana Prince","age":30,"email":"diana@enterprise.org"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve Wilson","age":35,"email":"eve@freelance.net"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Frank Miller","age":25,"email":"frank@startup.io"}'

curl -X POST \
  http://localhost:6969/AggregateUsersByAge \
  -H 'Content-Type: application/json' \
  -d '{}'

Coming Soon

  • DEDUP
  • Pattern matching

Result Operations

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Get first element of a list with FIRST  

Get the first element from a traversal result.

⚠️ Warning

Note this does not return the first/oldest node in the graph, but the first node that the traversal returns.

::FIRST

ℹ️ Note

FIRST returns a single element (object) rather than a collection (array). If the traversal has no results, the binding will be empty.

Example 1: Getting the first node returned by a traversal

QUERY GetOneUser () =>
    user <- N<User>::FIRST
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Example 2: Getting the first node returned by an edge traversal

QUERY GetOneFollower (user_id: ID) =>
    user <- N<User>(user_id)
    first_follower <- user::In<Follows>::FIRST
    RETURN first_follower
N::User {
    name: String,
    age: U8,
    email: String
}

E::Follows {
    From: User,
    To: User
}

Count elements with COUNT  

Count the number of elements in a traversal.

::COUNT

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Basic element counting

QUERY GetUserCount () =>
    user_count <- N<User>::COUNT
    RETURN user_count

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
    {"name": "Diana", "age": 22, "email": "diana@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetUserCount", {})
print(f"Total users: {result}")
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 30, "bob@example.com"),
        ("Charlie", 28, "charlie@example.com"),
        ("Diana", 22, "diana@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUserCount", &json!({})).await?;
    println!("Total users: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUserCount", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUserCount failed: %s", err)
    }

    fmt.Printf("Total users: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
        { name: "Diana", age: 22, email: "diana@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetUserCount", {});
    console.log("Total users:", result);
}

main().catch((err) => {
    console.error("GetUserCount query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/GetUserCount \
  -H 'Content-Type: application/json' \
  -d '{}'

Scope results with RANGE  

Get a specific range of elements from a traversal.

::RANGE(start, end)

ℹ️ Note

RANGE is inclusive of the start but exclusive of the end. For example, RANGE(0, 10) returns elements 0 through 9 (10 total elements). Both start and end and required and must be positive integers.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Basic pagination

QUERY GetUsersPaginated (start: U32, end: U32) =>
    users <- N<User>::RANGE(start, end)
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

for i in range(20):
    client.query("CreateUser", {
        "name": f"User{i}",
        "age": 20 + (i % 40),
        "email": f"user{i}@example.com"
    })

page1 = client.query("GetUsersPaginated", {"start": 0, "end": 5})
print("Page 1:", page1)

page2 = client.query("GetUsersPaginated", {"start": 5, "end": 10})
print("Page 2:", page2)

page3 = client.query("GetUsersPaginated", {"start": 10, "end": 15})
print("Page 3:", page3)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    for i in 0..20 {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": format!("User{}", i),
            "age": 20 + (i % 40),
            "email": format!("user{}@example.com", i),
        })).await?;
    }

    let page1: serde_json::Value = client.query("GetUsersPaginated", &json!({
        "start": 0,
        "end": 5
    })).await?;
    println!("Page 1: {page1:#?}");

    let page2: serde_json::Value = client.query("GetUsersPaginated", &json!({
        "start": 5,
        "end": 10
    })).await?;
    println!("Page 2: {page2:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    for i := 0; i < 20; i++ {
        user := map[string]any{
            "name":  fmt.Sprintf("User%d", i),
            "age":   uint8(20 + (i % 40)),
            "email": fmt.Sprintf("user%d@example.com", i),
        }

        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    page1Payload := map[string]any{"start": uint32(0), "end": uint32(5)}
    var page1 map[string]any
    if err := client.Query("GetUsersPaginated", helix.WithData(page1Payload)).Scan(&page1); err != nil {
        log.Fatalf("GetUsersPaginated failed: %s", err)
    }
    fmt.Printf("Page 1: %#v\n", page1)

    page2Payload := map[string]any{"start": uint32(5), "end": uint32(10)}
    var page2 map[string]any
    if err := client.Query("GetUsersPaginated", helix.WithData(page2Payload)).Scan(&page2); err != nil {
        log.Fatalf("GetUsersPaginated failed: %s", err)
    }
    fmt.Printf("Page 2: %#v\n", page2)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    for (let i = 0; i < 20; i++) {
        await client.query("CreateUser", {
            name: `User${i}`,
            age: 20 + (i % 40),
            email: `user${i}@example.com`
        });
    }

    const page1 = await client.query("GetUsersPaginated", {
        start: 0,
        end: 5
    });
    console.log("Page 1:", page1);

    const page2 = await client.query("GetUsersPaginated", {
        start: 5,
        end: 10
    });
    console.log("Page 2:", page2);

    const page3 = await client.query("GetUsersPaginated", {
        start: 10,
        end: 15
    });
    console.log("Page 3:", page3);
}

main().catch((err) => {
    console.error("GetUsersPaginated query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"User0","age":20,"email":"user0@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"User1","age":21,"email":"user1@example.com"}'

curl -X POST \
  http://localhost:6969/GetUsersPaginated \
  -H 'Content-Type: application/json' \
  -d '{"start":0,"end":5}'

curl -X POST \
  http://localhost:6969/GetUsersPaginated \
  -H 'Content-Type: application/json' \
  -d '{"start":5,"end":10}'

Sort results with ORDER  

Sort the results of a traversal by a property in ascending or descending order.

::ORDER<Asc>(_::{property})
::ORDER<Desc>(_::{property})

ℹ️ Note

Use Asc for ascending order and Desc for descending order.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Sorting by age (oldest first)

QUERY GetUsersOldestFirst () =>
    users <- N<User>::ORDER<Desc>(_::{age})
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 45, "email": "bob@example.com"},
    {"name": "Charlie", "age": 19, "email": "charlie@example.com"},
    {"name": "Diana", "age": 32, "email": "diana@example.com"},
    {"name": "Eve", "age": 28, "email": "eve@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetUsersOldestFirst", {})
print("Users (oldest first):", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 45, "bob@example.com"),
        ("Charlie", 19, "charlie@example.com"),
        ("Diana", 32, "diana@example.com"),
        ("Eve", 28, "eve@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUsersOldestFirst", &json!({})).await?;
    println!("Users (oldest first): {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(45), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(19), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(32), "email": "diana@example.com"},
        {"name": "Eve", "age": uint8(28), "email": "eve@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUsersOldestFirst", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUsersOldestFirst failed: %s", err)
    }

    fmt.Printf("Users (oldest first): %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 45, email: "bob@example.com" },
        { name: "Charlie", age: 19, email: "charlie@example.com" },
        { name: "Diana", age: 32, email: "diana@example.com" },
        { name: "Eve", age: 28, email: "eve@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetUsersOldestFirst", {});
    console.log("Users (oldest first):", result);
}

main().catch((err) => {
    console.error("GetUsersOldestFirst query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":45,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":19,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":32,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","age":28,"email":"eve@example.com"}'

curl -X POST \
  http://localhost:6969/GetUsersOldestFirst \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Sorting by age (youngest first)

QUERY GetUsersYoungestFirst () =>
    users <- N<User>::ORDER<Asc>(_::{age})
    RETURN users

QUERY CreateUser (name: String, age: U8, email: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 45, "email": "bob@example.com"},
    {"name": "Charlie", "age": 19, "email": "charlie@example.com"},
    {"name": "Diana", "age": 32, "email": "diana@example.com"},
    {"name": "Eve", "age": 28, "email": "eve@example.com"},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GetUsersYoungestFirst", {})
print("Users (youngest first):", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", 25, "alice@example.com"),
        ("Bob", 45, "bob@example.com"),
        ("Charlie", 19, "charlie@example.com"),
        ("Diana", 32, "diana@example.com"),
        ("Eve", 28, "eve@example.com"),
    ];

    for (name, age, email) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
        })).await?;
    }

    let result: serde_json::Value = client.query("GetUsersYoungestFirst", &json!({})).await?;
    println!("Users (youngest first): {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(45), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(19), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(32), "email": "diana@example.com"},
        {"name": "Eve", "age": uint8(28), "email": "eve@example.com"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GetUsersYoungestFirst", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GetUsersYoungestFirst failed: %s", err)
    }

    fmt.Printf("Users (youngest first): %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 45, email: "bob@example.com" },
        { name: "Charlie", age: 19, email: "charlie@example.com" },
        { name: "Diana", age: 32, email: "diana@example.com" },
        { name: "Eve", age: 28, email: "eve@example.com" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GetUsersYoungestFirst", {});
    console.log("Users (youngest first):", result);
}

main().catch((err) => {
    console.error("GetUsersYoungestFirst query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":45,"email":"bob@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":19,"email":"charlie@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":32,"email":"diana@example.com"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","age":28,"email":"eve@example.com"}'

curl -X POST \
  http://localhost:6969/GetUsersYoungestFirst \
  -H 'Content-Type: application/json' \
  -d '{}'

Group By

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Group Results by Property Values  

GROUP_BY organizes query results into groups based on one or more property values, returning count summaries for each unique combination.

::GROUP_BY(property)
::GROUP_BY(property1, property2)

Returns count summaries:

[
    {'property': value1, 'count': 3},
    {'property': value2, 'count': 2},
    ...
]

ℹ️ Note

GROUP_BY returns only the count summaries for each unique combination of the specified properties. This is ideal for analytics, distribution analysis, and understanding data patterns without returning full objects.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Group users by country

QUERY GroupUsersByCountry () =>
    users <- N<User>
    RETURN users::GROUP_BY(country)

QUERY CreateUser (name: String, country: String, city: String, age: U8) =>
    user <- AddN<User>({
        name: name,
        country: country,
        city: city,
        age: age
    })
    RETURN user
N::User {
    name: String,
    country: String,
    city: String,
    age: U8
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "country": "USA", "city": "New York", "age": 28},
    {"name": "Bob", "country": "Canada", "city": "Toronto", "age": 32},
    {"name": "Charlie", "country": "USA", "city": "Los Angeles", "age": 25},
    {"name": "Diana", "country": "UK", "city": "London", "age": 30},
    {"name": "Eve", "country": "Canada", "city": "Vancouver", "age": 27},
    {"name": "Frank", "country": "USA", "city": "Chicago", "age": 35},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GroupUsersByCountry", {})
print("Users grouped by country:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", "USA", "New York", 28),
        ("Bob", "Canada", "Toronto", 32),
        ("Charlie", "USA", "Los Angeles", 25),
        ("Diana", "UK", "London", 30),
        ("Eve", "Canada", "Vancouver", 27),
        ("Frank", "USA", "Chicago", 35),
    ];

    for (name, country, city, age) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "country": country,
            "city": city,
            "age": age,
        })).await?;
    }

    let result: serde_json::Value = client.query("GroupUsersByCountry", &json!({})).await?;
    println!("Users grouped by country: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "country": "USA", "city": "New York", "age": uint8(28)},
        {"name": "Bob", "country": "Canada", "city": "Toronto", "age": uint8(32)},
        {"name": "Charlie", "country": "USA", "city": "Los Angeles", "age": uint8(25)},
        {"name": "Diana", "country": "UK", "city": "London", "age": uint8(30)},
        {"name": "Eve", "country": "Canada", "city": "Vancouver", "age": uint8(27)},
        {"name": "Frank", "country": "USA", "city": "Chicago", "age": uint8(35)},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GroupUsersByCountry", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GroupUsersByCountry failed: %s", err)
    }

    fmt.Printf("Users grouped by country: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", country: "USA", city: "New York", age: 28 },
        { name: "Bob", country: "Canada", city: "Toronto", age: 32 },
        { name: "Charlie", country: "USA", city: "Los Angeles", age: 25 },
        { name: "Diana", country: "UK", city: "London", age: 30 },
        { name: "Eve", country: "Canada", city: "Vancouver", age: 27 },
        { name: "Frank", country: "USA", city: "Chicago", age: 35 },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GroupUsersByCountry", {});
    console.log("Users grouped by country:", result);
}

main().catch((err) => {
    console.error("GroupUsersByCountry query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","country":"USA","city":"New York","age":28}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","country":"Canada","city":"Toronto","age":32}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","country":"USA","city":"Los Angeles","age":25}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","country":"UK","city":"London","age":30}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","country":"Canada","city":"Vancouver","age":27}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Frank","country":"USA","city":"Chicago","age":35}'

curl -X POST \
  http://localhost:6969/GroupUsersByCountry \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Group users by multiple properties (country and city)

QUERY GroupUsersByCountryAndCity () =>
    users <- N<User>
    RETURN users::GROUP_BY(country, city)

QUERY CreateUser (name: String, country: String, city: String, age: U8) =>
    user <- AddN<User>({
        name: name,
        country: country,
        city: city,
        age: age
    })
    RETURN user
N::User {
    name: String,
    country: String,
    city: String,
    age: U8
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "country": "USA", "city": "New York", "age": 28},
    {"name": "Bob", "country": "USA", "city": "New York", "age": 32},
    {"name": "Charlie", "country": "USA", "city": "Los Angeles", "age": 25},
    {"name": "Diana", "country": "UK", "city": "London", "age": 30},
    {"name": "Eve", "country": "Canada", "city": "Toronto", "age": 27},
    {"name": "Frank", "country": "USA", "city": "Los Angeles", "age": 35},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("GroupUsersByCountryAndCity", {})
print("Users grouped by country and city:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", "USA", "New York", 28),
        ("Bob", "USA", "New York", 32),
        ("Charlie", "USA", "Los Angeles", 25),
        ("Diana", "UK", "London", 30),
        ("Eve", "Canada", "Toronto", 27),
        ("Frank", "USA", "Los Angeles", 35),
    ];

    for (name, country, city, age) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "country": country,
            "city": city,
            "age": age,
        })).await?;
    }

    let result: serde_json::Value = client.query("GroupUsersByCountryAndCity", &json!({})).await?;
    println!("Users grouped by country and city: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "country": "USA", "city": "New York", "age": uint8(28)},
        {"name": "Bob", "country": "USA", "city": "New York", "age": uint8(32)},
        {"name": "Charlie", "country": "USA", "city": "Los Angeles", "age": uint8(25)},
        {"name": "Diana", "country": "UK", "city": "London", "age": uint8(30)},
        {"name": "Eve", "country": "Canada", "city": "Toronto", "age": uint8(27)},
        {"name": "Frank", "country": "USA", "city": "Los Angeles", "age": uint8(35)},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("GroupUsersByCountryAndCity", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("GroupUsersByCountryAndCity failed: %s", err)
    }

    fmt.Printf("Users grouped by country and city: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", country: "USA", city: "New York", age: 28 },
        { name: "Bob", country: "USA", city: "New York", age: 32 },
        { name: "Charlie", country: "USA", city: "Los Angeles", age: 25 },
        { name: "Diana", country: "UK", city: "London", age: 30 },
        { name: "Eve", country: "Canada", city: "Toronto", age: 27 },
        { name: "Frank", country: "USA", city: "Los Angeles", age: 35 },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("GroupUsersByCountryAndCity", {});
    console.log("Users grouped by country and city:", result);
}

main().catch((err) => {
    console.error("GroupUsersByCountryAndCity query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","country":"USA","city":"New York","age":28}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","country":"USA","city":"New York","age":32}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","country":"USA","city":"Los Angeles","age":25}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","country":"UK","city":"London","age":30}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","country":"Canada","city":"Toronto","age":27}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Frank","country":"USA","city":"Los Angeles","age":35}'

curl -X POST \
  http://localhost:6969/GroupUsersByCountryAndCity \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 3: Count users per country using COUNT with GROUP_BY

QUERY CountUsersByCountry () =>
    users <- N<User>
    RETURN users::COUNT::GROUP_BY(country)

QUERY CreateUser (name: String, country: String, city: String, age: U8) =>
    user <- AddN<User>({
        name: name,
        country: country,
        city: city,
        age: age
    })
    RETURN user
N::User {
    name: String,
    country: String,
    city: String,
    age: U8
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "country": "USA", "city": "New York", "age": 28},
    {"name": "Bob", "country": "Canada", "city": "Toronto", "age": 32},
    {"name": "Charlie", "country": "USA", "city": "Los Angeles", "age": 25},
    {"name": "Diana", "country": "UK", "city": "London", "age": 30},
    {"name": "Eve", "country": "Canada", "city": "Vancouver", "age": 27},
    {"name": "Frank", "country": "USA", "city": "Chicago", "age": 35},
    {"name": "Grace", "country": "USA", "city": "Seattle", "age": 29},
]

for user in users:
    client.query("CreateUser", user)

result = client.query("CountUsersByCountry", {})
print("Count of users by country:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", "USA", "New York", 28),
        ("Bob", "Canada", "Toronto", 32),
        ("Charlie", "USA", "Los Angeles", 25),
        ("Diana", "UK", "London", 30),
        ("Eve", "Canada", "Vancouver", 27),
        ("Frank", "USA", "Chicago", 35),
        ("Grace", "USA", "Seattle", 29),
    ];

    for (name, country, city, age) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "country": country,
            "city": city,
            "age": age,
        })).await?;
    }

    let result: serde_json::Value = client.query("CountUsersByCountry", &json!({})).await?;
    println!("Count of users by country: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "country": "USA", "city": "New York", "age": uint8(28)},
        {"name": "Bob", "country": "Canada", "city": "Toronto", "age": uint8(32)},
        {"name": "Charlie", "country": "USA", "city": "Los Angeles", "age": uint8(25)},
        {"name": "Diana", "country": "UK", "city": "London", "age": uint8(30)},
        {"name": "Eve", "country": "Canada", "city": "Vancouver", "age": uint8(27)},
        {"name": "Frank", "country": "USA", "city": "Chicago", "age": uint8(35)},
        {"name": "Grace", "country": "USA", "city": "Seattle", "age": uint8(29)},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CountUsersByCountry", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("CountUsersByCountry failed: %s", err)
    }

    fmt.Printf("Count of users by country: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", country: "USA", city: "New York", age: 28 },
        { name: "Bob", country: "Canada", city: "Toronto", age: 32 },
        { name: "Charlie", country: "USA", city: "Los Angeles", age: 25 },
        { name: "Diana", country: "UK", city: "London", age: 30 },
        { name: "Eve", country: "Canada", city: "Vancouver", age: 27 },
        { name: "Frank", country: "USA", city: "Chicago", age: 35 },
        { name: "Grace", country: "USA", city: "Seattle", age: 29 },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    const result = await client.query("CountUsersByCountry", {});
    console.log("Count of users by country:", result);
}

main().catch((err) => {
    console.error("CountUsersByCountry query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","country":"USA","city":"New York","age":28}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","country":"Canada","city":"Toronto","age":32}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","country":"USA","city":"Los Angeles","age":25}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","country":"UK","city":"London","age":30}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","country":"Canada","city":"Vancouver","age":27}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Frank","country":"USA","city":"Chicago","age":35}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Grace","country":"USA","city":"Seattle","age":29}'

curl -X POST \
  http://localhost:6969/CountUsersByCountry \
  -H 'Content-Type: application/json' \
  -d '{}'

Aggregations

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Aggregate Results with Full Data Objects  

AGGREGATE_BY organizes query results into groups based on one or more property values, returning both count summaries and the full data objects for each group.

::AGGREGATE_BY(property)
::AGGREGATE_BY(property1, property2)

Returns grouped data with full objects:

#![allow(unused)]
fn main() {
[
    {
        'count': 3,
        'data': [
            {"property1": value1, "property2": "Alice", ...},
            {"property1": value1, "property2": "Charlie", ...},
            {"property1": value1, "property2": "Frank", ...}
        ]
    },
    {
        'count': 2,
        'data': [
            {"property1": value2, "property2": "Bob", ...},
            {"property1": value2, "property2": "Diana", ...}
        ]
    },
    ...
]
}

ℹ️ Note

AGGREGATE_BY returns the full data objects grouped by the specified properties, providing both count and the actual grouped elements for detailed analysis. This is useful when you need to work with the actual data in each group, not just counts.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Aggregate orders by status

QUERY AggregateOrdersByStatus () =>
    orders <- N<Order>
    RETURN orders::AGGREGATE_BY(status)

QUERY CreateOrder (customer_name: String, status: String, total: F64) =>
    order <- AddN<Order>({
        customer_name: customer_name,
        status: status,
        total: total
    })
    RETURN order
N::Order {
    customer_name: String,
    status: String,
    total: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

orders = [
    {"customer_name": "Alice", "status": "pending", "total": 99.99},
    {"customer_name": "Bob", "status": "shipped", "total": 149.99},
    {"customer_name": "Charlie", "status": "pending", "total": 75.50},
    {"customer_name": "Diana", "status": "delivered", "total": 200.00},
    {"customer_name": "Eve", "status": "shipped", "total": 89.99},
    {"customer_name": "Frank", "status": "pending", "total": 125.00},
]

for order in orders:
    client.query("CreateOrder", order)

result = client.query("AggregateOrdersByStatus", {})
print("Orders aggregated by status:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let orders = vec![
        ("Alice", "pending", 99.99),
        ("Bob", "shipped", 149.99),
        ("Charlie", "pending", 75.50),
        ("Diana", "delivered", 200.00),
        ("Eve", "shipped", 89.99),
        ("Frank", "pending", 125.00),
    ];

    for (customer_name, status, total) in &orders {
        let _created: serde_json::Value = client.query("CreateOrder", &json!({
            "customer_name": customer_name,
            "status": status,
            "total": total,
        })).await?;
    }

    let result: serde_json::Value = client.query("AggregateOrdersByStatus", &json!({})).await?;
    println!("Orders aggregated by status: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    orders := []map[string]any{
        {"customer_name": "Alice", "status": "pending", "total": 99.99},
        {"customer_name": "Bob", "status": "shipped", "total": 149.99},
        {"customer_name": "Charlie", "status": "pending", "total": 75.50},
        {"customer_name": "Diana", "status": "delivered", "total": 200.00},
        {"customer_name": "Eve", "status": "shipped", "total": 89.99},
        {"customer_name": "Frank", "status": "pending", "total": 125.00},
    }

    for _, order := range orders {
        var created map[string]any
        if err := client.Query("CreateOrder", helix.WithData(order)).Scan(&created); err != nil {
            log.Fatalf("CreateOrder failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("AggregateOrdersByStatus", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("AggregateOrdersByStatus failed: %s", err)
    }

    fmt.Printf("Orders aggregated by status: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const orders = [
        { customer_name: "Alice", status: "pending", total: 99.99 },
        { customer_name: "Bob", status: "shipped", total: 149.99 },
        { customer_name: "Charlie", status: "pending", total: 75.50 },
        { customer_name: "Diana", status: "delivered", total: 200.00 },
        { customer_name: "Eve", status: "shipped", total: 89.99 },
        { customer_name: "Frank", status: "pending", total: 125.00 },
    ];

    for (const order of orders) {
        await client.query("CreateOrder", order);
    }

    const result = await client.query("AggregateOrdersByStatus", {});
    console.log("Orders aggregated by status:", result);
}

main().catch((err) => {
    console.error("AggregateOrdersByStatus query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Alice","status":"pending","total":99.99}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Bob","status":"shipped","total":149.99}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Charlie","status":"pending","total":75.50}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Diana","status":"delivered","total":200.00}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Eve","status":"shipped","total":89.99}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Frank","status":"pending","total":125.00}'

curl -X POST \
  http://localhost:6969/AggregateOrdersByStatus \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Aggregate products by category with COUNT

QUERY CountProductsByCategory () =>
    products <- N<Product>
    RETURN products::COUNT::AGGREGATE_BY(category)

QUERY CreateProduct (name: String, category: String, price: F64, stock: I32) =>
    product <- AddN<Product>({
        name: name,
        category: category,
        price: price,
        stock: stock
    })
    RETURN product
N::Product {
    name: String,
    category: String,
    price: F64,
    stock: I32
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

products = [
    {"name": "Laptop", "category": "Electronics", "price": 999.99, "stock": 15},
    {"name": "Mouse", "category": "Electronics", "price": 29.99, "stock": 50},
    {"name": "Desk", "category": "Furniture", "price": 299.99, "stock": 10},
    {"name": "Chair", "category": "Furniture", "price": 199.99, "stock": 20},
    {"name": "Monitor", "category": "Electronics", "price": 399.99, "stock": 25},
    {"name": "Bookshelf", "category": "Furniture", "price": 149.99, "stock": 12},
]

for product in products:
    client.query("CreateProduct", product)

result = client.query("CountProductsByCategory", {})
print("Product count aggregated by category:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let products = vec![
        ("Laptop", "Electronics", 999.99, 15),
        ("Mouse", "Electronics", 29.99, 50),
        ("Desk", "Furniture", 299.99, 10),
        ("Chair", "Furniture", 199.99, 20),
        ("Monitor", "Electronics", 399.99, 25),
        ("Bookshelf", "Furniture", 149.99, 12),
    ];

    for (name, category, price, stock) in &products {
        let _created: serde_json::Value = client.query("CreateProduct", &json!({
            "name": name,
            "category": category,
            "price": price,
            "stock": stock,
        })).await?;
    }

    let result: serde_json::Value = client.query("CountProductsByCategory", &json!({})).await?;
    println!("Product count aggregated by category: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    products := []map[string]any{
        {"name": "Laptop", "category": "Electronics", "price": 999.99, "stock": int32(15)},
        {"name": "Mouse", "category": "Electronics", "price": 29.99, "stock": int32(50)},
        {"name": "Desk", "category": "Furniture", "price": 299.99, "stock": int32(10)},
        {"name": "Chair", "category": "Furniture", "price": 199.99, "stock": int32(20)},
        {"name": "Monitor", "category": "Electronics", "price": 399.99, "stock": int32(25)},
        {"name": "Bookshelf", "category": "Furniture", "price": 149.99, "stock": int32(12)},
    }

    for _, product := range products {
        var created map[string]any
        if err := client.Query("CreateProduct", helix.WithData(product)).Scan(&created); err != nil {
            log.Fatalf("CreateProduct failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CountProductsByCategory", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("CountProductsByCategory failed: %s", err)
    }

    fmt.Printf("Product count aggregated by category: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const products = [
        { name: "Laptop", category: "Electronics", price: 999.99, stock: 15 },
        { name: "Mouse", category: "Electronics", price: 29.99, stock: 50 },
        { name: "Desk", category: "Furniture", price: 299.99, stock: 10 },
        { name: "Chair", category: "Furniture", price: 199.99, stock: 20 },
        { name: "Monitor", category: "Electronics", price: 399.99, stock: 25 },
        { name: "Bookshelf", category: "Furniture", price: 149.99, stock: 12 },
    ];

    for (const product of products) {
        await client.query("CreateProduct", product);
    }

    const result = await client.query("CountProductsByCategory", {});
    console.log("Product count aggregated by category:", result);
}

main().catch((err) => {
    console.error("CountProductsByCategory query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Laptop","category":"Electronics","price":999.99,"stock":15}'

curl -X POST \
  http://localhost:6969/CreateProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Mouse","category":"Electronics","price":29.99,"stock":50}'

curl -X POST \
  http://localhost:6969/CreateProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Desk","category":"Furniture","price":299.99,"stock":10}'

curl -X POST \
  http://localhost:6969/CreateProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Chair","category":"Furniture","price":199.99,"stock":20}'

curl -X POST \
  http://localhost:6969/CreateProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Monitor","category":"Electronics","price":399.99,"stock":25}'

curl -X POST \
  http://localhost:6969/CreateProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bookshelf","category":"Furniture","price":149.99,"stock":12}'

curl -X POST \
  http://localhost:6969/CountProductsByCategory \
  -H 'Content-Type: application/json' \
  -d '{}'

When to Use AGGREGATE_BY vs GROUP_BY

Choose AGGREGATE_BY when:

  • You need the actual data objects in each group
  • You want to perform further processing on grouped items
  • You need to display or return the full records
  • You’re building reports that show detailed breakdowns

Choose GROUP_BY when:

  • You only need counts and summaries
  • Memory efficiency is a priority
  • You’re doing analytics or dashboards showing distributions
  • You don’t need the actual data, just statistics

ℹ️ Note

AGGREGATE_BY returns more data than GROUP_BY, so it uses more memory and bandwidth. Use GROUP_BY when you only need counts.

Grouping and Aggregation Guide

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Understanding Grouping Operations  

HelixDB provides two powerful operations for organizing and summarizing data: GROUP_BY and AGGREGATE_BY. While they may seem similar, they serve different purposes and return different results.

Key Differences

FeatureGROUP_BYAGGREGATE_BY
ReturnsCount summaries onlyFull data objects + counts
Memory UsageLow - only stores countsHigher - stores all objects
Use CaseAnalytics, distributionsDetailed reports, processing
Output SizeSmall, compactLarge, comprehensive
Best ForDashboards, statisticsData analysis, transformations

Syntax Comparison

Both operations support single or multiple properties:

// GROUP_BY - Returns counts only
::GROUP_BY(property)
::GROUP_BY(property1, property2, ...)

// AGGREGATE_BY - Returns full data
::AGGREGATE_BY(property)
::AGGREGATE_BY(property1, property2, ...)

Output Format Comparison

GROUP_BY Output

[
    {'country': 'USA', 'count': 3},
    {'country': 'Canada', 'count': 2},
    {'country': 'UK', 'count': 1}
]

AGGREGATE_BY Output

#![allow(unused)]
fn main() {
[
    {
        'count': 3,
        'data': [
            {'name': 'Alice', 'country': 'USA', 'age': 28},
            {'name': 'Charlie', 'country': 'USA', 'age': 25},
            {'name': 'Frank', 'country': 'USA', 'age': 35}
        ]
    },
    {
        'count': 2,
        'data': [
            {'name': 'Bob', 'country': 'Canada', 'age': 32},
            {'name': 'Eve', 'country': 'Canada', 'age': 27}
        ]
    }
]
}

Performance Characteristics

GROUP_BY Performance

  • Memory: O(n) where n = number of unique groups
  • Speed: Fast - only counts are stored
  • Bandwidth: Minimal - small response size
  • Scalability: Excellent for large datasets

AGGREGATE_BY Performance

  • Memory: O(m) where m = total number of items
  • Speed: Moderate - full objects stored
  • Bandwidth: Higher - complete data returned
  • Scalability: Good for moderate datasets

ℹ️ Note

For large datasets where you only need counts, GROUP_BY can be orders of magnitude more efficient in terms of memory and bandwidth usage.

Use Case Decision Tree

Do you need the actual data objects?
│
├─ YES → Do you need to process/transform them?
│         │
│         ├─ YES → Use AGGREGATE_BY
│         │        (You need the full objects)
│         │
│         └─ NO  → Do you need to display them?
│                  │
│                  ├─ YES → Use AGGREGATE_BY
│                  │        (You need to show details)
│                  │
│                  └─ NO  → Use GROUP_BY
│                           (You only need counts)
│
└─ NO  → Use GROUP_BY
          (Counts are sufficient)

Best Practices

Use GROUP_BY When:

  1. Building analytics dashboards
  2. Showing data distributions
  3. Generating summary reports
  4. Optimizing for memory/bandwidth
  5. Working with large datasets (millions of records)
  6. Creating charts or graphs

Use AGGREGATE_BY When:

  1. Need to process grouped data further
  2. Building detailed reports with examples
  3. Need to display sample records per group
  4. Performing transformations on grouped items
  5. Working with moderate datasets (thousands of records)
  6. Building data exploration interfaces

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Side-by-Side Comparison - User Distribution

QUERY GroupUsersByCountry () =>
    users <- N<User>
    RETURN users::GROUP_BY(country)

QUERY AggregateUsersByCountry () =>
    users <- N<User>
    RETURN users::AGGREGATE_BY(country)

QUERY CreateUser (name: String, country: String, age: U8) =>
    user <- AddN<User>({
        name: name,
        country: country,
        age: age
    })
    RETURN user
N::User {
    name: String,
    country: String,
    age: U8
}

Here’s how to run both queries using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

users = [
    {"name": "Alice", "country": "USA", "age": 28},
    {"name": "Bob", "country": "Canada", "age": 32},
    {"name": "Charlie", "country": "USA", "age": 25},
    {"name": "Diana", "country": "UK", "age": 30},
    {"name": "Eve", "country": "Canada", "age": 27},
    {"name": "Frank", "country": "USA", "age": 35},
]

for user in users:
    client.query("CreateUser", user)

# GROUP_BY - Returns only counts
group_result = client.query("GroupUsersByCountry", {})
print("GROUP_BY result:", group_result)
# Output: [{'country': 'USA', 'count': 3}, {'country': 'Canada', 'count': 2}, ...]

# AGGREGATE_BY - Returns full data
aggregate_result = client.query("AggregateUsersByCountry", {})
print("AGGREGATE_BY result:", aggregate_result)
# Output: [{'count': 3, 'data': [{'name': 'Alice', ...}, ...]}, ...]
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let users = vec![
        ("Alice", "USA", 28),
        ("Bob", "Canada", 32),
        ("Charlie", "USA", 25),
        ("Diana", "UK", 30),
        ("Eve", "Canada", 27),
        ("Frank", "USA", 35),
    ];

    for (name, country, age) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "country": country,
            "age": age,
        })).await?;
    }

    // GROUP_BY - Returns only counts
    let group_result: serde_json::Value = client.query("GroupUsersByCountry", &json!({})).await?;
    println!("GROUP_BY result: {group_result:#?}");

    // AGGREGATE_BY - Returns full data
    let aggregate_result: serde_json::Value = client.query("AggregateUsersByCountry", &json!({})).await?;
    println!("AGGREGATE_BY result: {aggregate_result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    users := []map[string]any{
        {"name": "Alice", "country": "USA", "age": uint8(28)},
        {"name": "Bob", "country": "Canada", "age": uint8(32)},
        {"name": "Charlie", "country": "USA", "age": uint8(25)},
        {"name": "Diana", "country": "UK", "age": uint8(30)},
        {"name": "Eve", "country": "Canada", "age": uint8(27)},
        {"name": "Frank", "country": "USA", "age": uint8(35)},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    // GROUP_BY - Returns only counts
    var groupResult map[string]any
    if err := client.Query("GroupUsersByCountry", helix.WithData(map[string]any{})).Scan(&groupResult); err != nil {
        log.Fatalf("GroupUsersByCountry failed: %s", err)
    }
    fmt.Printf("GROUP_BY result: %#v\n", groupResult)

    // AGGREGATE_BY - Returns full data
    var aggregateResult map[string]any
    if err := client.Query("AggregateUsersByCountry", helix.WithData(map[string]any{})).Scan(&aggregateResult); err != nil {
        log.Fatalf("AggregateUsersByCountry failed: %s", err)
    }
    fmt.Printf("AGGREGATE_BY result: %#v\n", aggregateResult)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const users = [
        { name: "Alice", country: "USA", age: 28 },
        { name: "Bob", country: "Canada", age: 32 },
        { name: "Charlie", country: "USA", age: 25 },
        { name: "Diana", country: "UK", age: 30 },
        { name: "Eve", country: "Canada", age: 27 },
        { name: "Frank", country: "USA", age: 35 },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    // GROUP_BY - Returns only counts
    const groupResult = await client.query("GroupUsersByCountry", {});
    console.log("GROUP_BY result:", groupResult);

    // AGGREGATE_BY - Returns full data
    const aggregateResult = await client.query("AggregateUsersByCountry", {});
    console.log("AGGREGATE_BY result:", aggregateResult);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create users
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","country":"USA","age":28}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","country":"Canada","age":32}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","country":"USA","age":25}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","country":"UK","age":30}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Eve","country":"Canada","age":27}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Frank","country":"USA","age":35}'

# GROUP_BY - Returns only counts
curl -X POST \
  http://localhost:6969/GroupUsersByCountry \
  -H 'Content-Type: application/json' \
  -d '{}'

# AGGREGATE_BY - Returns full data
curl -X POST \
  http://localhost:6969/AggregateUsersByCountry \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Using COUNT with Both Operations

QUERY CountWithGroupBy () =>
    orders <- N<Order>
    RETURN orders::COUNT::GROUP_BY(status)

QUERY CountWithAggregateBy () =>
    orders <- N<Order>
    RETURN orders::COUNT::AGGREGATE_BY(status)

QUERY CreateOrder (customer_name: String, status: String, total: F64) =>
    order <- AddN<Order>({
        customer_name: customer_name,
        status: status,
        total: total
    })
    RETURN order
N::Order {
    customer_name: String,
    status: String,
    total: F64
}

Here’s how to run both queries using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

orders = [
    {"customer_name": "Alice", "status": "pending", "total": 99.99},
    {"customer_name": "Bob", "status": "shipped", "total": 149.99},
    {"customer_name": "Charlie", "status": "pending", "total": 75.50},
    {"customer_name": "Diana", "status": "delivered", "total": 200.00},
    {"customer_name": "Eve", "status": "shipped", "total": 89.99},
]

for order in orders:
    client.query("CreateOrder", order)

# COUNT with GROUP_BY - Compact counts
group_count = client.query("CountWithGroupBy", {})
print("COUNT::GROUP_BY result:", group_count)
# Output: [{'status': 'pending', 'count': 2}, {'status': 'shipped', 'count': 2}, ...]

# COUNT with AGGREGATE_BY - Counts with data
aggregate_count = client.query("CountWithAggregateBy", {})
print("COUNT::AGGREGATE_BY result:", aggregate_count)
# Output: [{'count': 2, 'data': [{'customer_name': 'Alice', ...}, ...]}, ...]
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let orders = vec![
        ("Alice", "pending", 99.99),
        ("Bob", "shipped", 149.99),
        ("Charlie", "pending", 75.50),
        ("Diana", "delivered", 200.00),
        ("Eve", "shipped", 89.99),
    ];

    for (customer_name, status, total) in &orders {
        let _created: serde_json::Value = client.query("CreateOrder", &json!({
            "customer_name": customer_name,
            "status": status,
            "total": total,
        })).await?;
    }

    // COUNT with GROUP_BY - Compact counts
    let group_count: serde_json::Value = client.query("CountWithGroupBy", &json!({})).await?;
    println!("COUNT::GROUP_BY result: {group_count:#?}");

    // COUNT with AGGREGATE_BY - Counts with data
    let aggregate_count: serde_json::Value = client.query("CountWithAggregateBy", &json!({})).await?;
    println!("COUNT::AGGREGATE_BY result: {aggregate_count:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    orders := []map[string]any{
        {"customer_name": "Alice", "status": "pending", "total": 99.99},
        {"customer_name": "Bob", "status": "shipped", "total": 149.99},
        {"customer_name": "Charlie", "status": "pending", "total": 75.50},
        {"customer_name": "Diana", "status": "delivered", "total": 200.00},
        {"customer_name": "Eve", "status": "shipped", "total": 89.99},
    }

    for _, order := range orders {
        var created map[string]any
        if err := client.Query("CreateOrder", helix.WithData(order)).Scan(&created); err != nil {
            log.Fatalf("CreateOrder failed: %s", err)
        }
    }

    // COUNT with GROUP_BY - Compact counts
    var groupCount map[string]any
    if err := client.Query("CountWithGroupBy", helix.WithData(map[string]any{})).Scan(&groupCount); err != nil {
        log.Fatalf("CountWithGroupBy failed: %s", err)
    }
    fmt.Printf("COUNT::GROUP_BY result: %#v\n", groupCount)

    // COUNT with AGGREGATE_BY - Counts with data
    var aggregateCount map[string]any
    if err := client.Query("CountWithAggregateBy", helix.WithData(map[string]any{})).Scan(&aggregateCount); err != nil {
        log.Fatalf("CountWithAggregateBy failed: %s", err)
    }
    fmt.Printf("COUNT::AGGREGATE_BY result: %#v\n", aggregateCount)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const orders = [
        { customer_name: "Alice", status: "pending", total: 99.99 },
        { customer_name: "Bob", status: "shipped", total: 149.99 },
        { customer_name: "Charlie", status: "pending", total: 75.50 },
        { customer_name: "Diana", status: "delivered", total: 200.00 },
        { customer_name: "Eve", status: "shipped", total: 89.99 },
    ];

    for (const order of orders) {
        await client.query("CreateOrder", order);
    }

    // COUNT with GROUP_BY - Compact counts
    const groupCount = await client.query("CountWithGroupBy", {});
    console.log("COUNT::GROUP_BY result:", groupCount);

    // COUNT with AGGREGATE_BY - Counts with data
    const aggregateCount = await client.query("CountWithAggregateBy", {});
    console.log("COUNT::AGGREGATE_BY result:", aggregateCount);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create orders
curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Alice","status":"pending","total":99.99}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Bob","status":"shipped","total":149.99}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Charlie","status":"pending","total":75.50}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Diana","status":"delivered","total":200.00}'

curl -X POST \
  http://localhost:6969/CreateOrder \
  -H 'Content-Type: application/json' \
  -d '{"customer_name":"Eve","status":"shipped","total":89.99}'

# COUNT with GROUP_BY - Compact counts
curl -X POST \
  http://localhost:6969/CountWithGroupBy \
  -H 'Content-Type: application/json' \
  -d '{}'

# COUNT with AGGREGATE_BY - Counts with data
curl -X POST \
  http://localhost:6969/CountWithAggregateBy \
  -H 'Content-Type: application/json' \
  -d '{}'

Common Pitfalls

Memory Issues with AGGREGATE_BY

// ❌ Bad: AGGREGATE_BY on millions of records
users <- N<User>
RETURN users::AGGREGATE_BY(country)

// ✅ Good: Use GROUP_BY for large datasets
users <- N<User>
RETURN users::GROUP_BY(country)

Using GROUP_BY When You Need Data

// ❌ Bad: GROUP_BY when you need to process items
items <- N<Item>
grouped <- items::GROUP_BY(category)
// Can't access individual items here!

// ✅ Good: Use AGGREGATE_BY to access data
items <- N<Item>
grouped <- items::AGGREGATE_BY(category)
// Now you have access to full item data

Summary

Choose the right operation for your use case:

  • GROUP_BY: Lightweight, fast, perfect for counts and distributions
  • AGGREGATE_BY: Comprehensive, detailed, ideal for data processing

Both operations are powerful tools in your HelixDB toolkit. Understanding when to use each will help you build efficient and effective queries.

FOR Loops

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Overview

FOR loops enable you to iterate over collections and perform operations on each element. They are essential for batch processing, data transformation, and building complex graph structures.

FOR variable IN collection {
    // Operations on each element
}

FOR {field1, field2} IN collection {
    // Operations with destructured fields
}

ℹ️ Note

FOR loops are executed sequentially, making them ideal for operations that need to maintain order or have dependencies between iterations.

When to use FOR loops

  • Batch data loading: Insert multiple nodes or edges from a collection
  • Data transformation: Process and update multiple records
  • Graph construction: Build relationships between collections of nodes
  • Nested iterations: Create connections between all pairs of elements

Quick examples

Basic iteration

FOR user IN users {
    AddN<User>({ name: user.name, age: user.age })
}

Destructuring

FOR {name, age, email} IN user_data {
    AddN<User>({ name: name, age: age, email: email })
}

Nested loops

FOR user1 IN users {
    FOR user2 IN users {
        AddE<Knows>::From(user1)::To(user2)
    }
}

Detailed guides

Basic FOR Loops

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Iterate over collections with FOR loops

Use FOR loops to perform operations on each element in a collection. The loop variable can reference the entire element or specific properties.

FOR variable IN collection {
    // Access element properties: variable.property
    // Perform operations on each element
}

ℹ️ Note

FOR loops process elements sequentially in the order they appear in the collection. This guarantees consistent execution order for dependent operations.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Iterating over nodes to perform updates

QUERY UpdateUserStatus (user_updates: [{id: ID, status: String}]) =>
    FOR {id, status} IN user_updates {
        user <- N<User>(id)::UPDATE({
            status: status,
            last_updated: "2024-11-04"
        })
    }
    RETURN "Updated all users"

QUERY CreateUser (name: String, age: U8, email: String, status: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email,
        status: status,
        last_updated: "2024-11-01"
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
    status: String,
    last_updated: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create initial users
users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com", "status": "active"},
    {"name": "Bob", "age": 30, "email": "bob@example.com", "status": "active"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com", "status": "active"},
]

user_ids = []
for user in users:
    result = client.query("CreateUser", user)
    user_ids.append(result["id"])

# Update their statuses using FOR loop
user_updates = [
    {"id": user_ids[0], "status": "inactive"},
    {"id": user_ids[1], "status": "pending"},
    {"id": user_ids[2], "status": "active"},
]

result = client.query("UpdateUserStatus", {"user_updates": user_updates})
print("Update result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create initial users
    let users = vec![
        ("Alice", 25, "alice@example.com", "active"),
        ("Bob", 30, "bob@example.com", "active"),
        ("Charlie", 28, "charlie@example.com", "active"),
    ];

    let mut user_ids = Vec::new();
    for (name, age, email, status) in &users {
        let result: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
            "status": status,
        })).await?;
        user_ids.push(result["id"].clone());
    }

    // Update their statuses using FOR loop
    let user_updates = vec![
        json!({"id": user_ids[0], "status": "inactive"}),
        json!({"id": user_ids[1], "status": "pending"}),
        json!({"id": user_ids[2], "status": "active"}),
    ];

    let result: serde_json::Value = client.query("UpdateUserStatus", &json!({
        "user_updates": user_updates
    })).await?;
    println!("Update result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create initial users
    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com", "status": "active"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com", "status": "active"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com", "status": "active"},
    }

    var userIDs []any
    for _, user := range users {
        var result map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&result); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
        userIDs = append(userIDs, result["id"])
    }

    // Update their statuses using FOR loop
    userUpdates := []map[string]any{
        {"id": userIDs[0], "status": "inactive"},
        {"id": userIDs[1], "status": "pending"},
        {"id": userIDs[2], "status": "active"},
    }

    var updateResult map[string]any
    if err := client.Query("UpdateUserStatus", helix.WithData(map[string]any{
        "user_updates": userUpdates,
    })).Scan(&updateResult); err != nil {
        log.Fatalf("UpdateUserStatus failed: %s", err)
    }

    fmt.Printf("Update result: %#v\n", updateResult)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create initial users
    const users = [
        { name: "Alice", age: 25, email: "alice@example.com", status: "active" },
        { name: "Bob", age: 30, email: "bob@example.com", status: "active" },
        { name: "Charlie", age: 28, email: "charlie@example.com", status: "active" },
    ];

    const userIds = [];
    for (const user of users) {
        const result = await client.query("CreateUser", user);
        userIds.push(result.id);
    }

    // Update their statuses using FOR loop
    const userUpdates = [
        { id: userIds[0], status: "inactive" },
        { id: userIds[1], status: "pending" },
        { id: userIds[2], status: "active" },
    ];

    const result = await client.query("UpdateUserStatus", { user_updates: userUpdates });
    console.log("Update result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Create initial users and capture their IDs
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com","status":"active"}'

# Update their statuses using FOR loop (replace IDs with actual values from above)
curl -X POST \
  http://localhost:6969/UpdateUserStatus \
  -H 'Content-Type: application/json' \
  -d '{
    "user_updates": [
      {"id": 1, "status": "inactive"},
      {"id": 2, "status": "pending"},
      {"id": 3, "status": "active"}
    ]
  }'

Example 2: Processing query results

QUERY ArchiveInactiveUsers () =>
    inactive_users <- N<User>::WHERE(_::{status}::EQ("inactive"))
    FOR user IN inactive_users {
        user::UPDATE({ archived: true, archived_at: "2024-11-04" })
    }
    RETURN "Archived inactive users"

QUERY CreateUser (name: String, age: U8, email: String, status: String) =>
    user <- AddN<User>({
        name: name,
        age: age,
        email: email,
        status: status,
        archived: false,
        archived_at: ""
    })
    RETURN user
N::User {
    name: String,
    age: U8,
    email: String,
    status: String,
    archived: Bool,
    archived_at: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create users with different statuses
users = [
    {"name": "Alice", "age": 25, "email": "alice@example.com", "status": "inactive"},
    {"name": "Bob", "age": 30, "email": "bob@example.com", "status": "active"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com", "status": "inactive"},
    {"name": "Diana", "age": 22, "email": "diana@example.com", "status": "active"},
]

for user in users:
    client.query("CreateUser", user)

# Archive all inactive users
result = client.query("ArchiveInactiveUsers", {})
print("Archive result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create users with different statuses
    let users = vec![
        ("Alice", 25, "alice@example.com", "inactive"),
        ("Bob", 30, "bob@example.com", "active"),
        ("Charlie", 28, "charlie@example.com", "inactive"),
        ("Diana", 22, "diana@example.com", "active"),
    ];

    for (name, age, email, status) in &users {
        let _created: serde_json::Value = client.query("CreateUser", &json!({
            "name": name,
            "age": age,
            "email": email,
            "status": status,
        })).await?;
    }

    // Archive all inactive users
    let result: serde_json::Value = client.query("ArchiveInactiveUsers", &json!({})).await?;
    println!("Archive result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create users with different statuses
    users := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com", "status": "inactive"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com", "status": "active"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com", "status": "inactive"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com", "status": "active"},
    }

    for _, user := range users {
        var created map[string]any
        if err := client.Query("CreateUser", helix.WithData(user)).Scan(&created); err != nil {
            log.Fatalf("CreateUser failed: %s", err)
        }
    }

    // Archive all inactive users
    var result map[string]any
    if err := client.Query("ArchiveInactiveUsers", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("ArchiveInactiveUsers failed: %s", err)
    }

    fmt.Printf("Archive result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create users with different statuses
    const users = [
        { name: "Alice", age: 25, email: "alice@example.com", status: "inactive" },
        { name: "Bob", age: 30, email: "bob@example.com", status: "active" },
        { name: "Charlie", age: 28, email: "charlie@example.com", status: "inactive" },
        { name: "Diana", age: 22, email: "diana@example.com", status: "active" },
    ];

    for (const user of users) {
        await client.query("CreateUser", user);
    }

    // Archive all inactive users
    const result = await client.query("ArchiveInactiveUsers", {});
    console.log("Archive result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","age":25,"email":"alice@example.com","status":"inactive"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","age":30,"email":"bob@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","age":28,"email":"charlie@example.com","status":"inactive"}'

curl -X POST \
  http://localhost:6969/CreateUser \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","age":22,"email":"diana@example.com","status":"active"}'

curl -X POST \
  http://localhost:6969/ArchiveInactiveUsers \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 3: Batch operations on collections

QUERY LoadProducts (products: [{name: String, price: F64, category: String}]) =>
    FOR product IN products {
        new_product <- AddN<Product>({
            name: product.name,
            price: product.price,
            category: product.category,
            stock: 100
        })
    }
    RETURN "Products loaded successfully"
N::Product {
    name: String,
    price: F64,
    category: String,
    stock: I32
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Load multiple products in batch
products = [
    {"name": "Laptop", "price": 999.99, "category": "Electronics"},
    {"name": "Mouse", "price": 29.99, "category": "Electronics"},
    {"name": "Keyboard", "price": 79.99, "category": "Electronics"},
    {"name": "Monitor", "price": 299.99, "category": "Electronics"},
    {"name": "Desk Chair", "price": 199.99, "category": "Furniture"},
]

result = client.query("LoadProducts", {"products": products})
print("Load result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Load multiple products in batch
    let products = vec![
        json!({"name": "Laptop", "price": 999.99, "category": "Electronics"}),
        json!({"name": "Mouse", "price": 29.99, "category": "Electronics"}),
        json!({"name": "Keyboard", "price": 79.99, "category": "Electronics"}),
        json!({"name": "Monitor", "price": 299.99, "category": "Electronics"}),
        json!({"name": "Desk Chair", "price": 199.99, "category": "Furniture"}),
    ];

    let result: serde_json::Value = client.query("LoadProducts", &json!({
        "products": products
    })).await?;
    println!("Load result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Load multiple products in batch
    products := []map[string]any{
        {"name": "Laptop", "price": 999.99, "category": "Electronics"},
        {"name": "Mouse", "price": 29.99, "category": "Electronics"},
        {"name": "Keyboard", "price": 79.99, "category": "Electronics"},
        {"name": "Monitor", "price": 299.99, "category": "Electronics"},
        {"name": "Desk Chair", "price": 199.99, "category": "Furniture"},
    }

    var result map[string]any
    if err := client.Query("LoadProducts", helix.WithData(map[string]any{
        "products": products,
    })).Scan(&result); err != nil {
        log.Fatalf("LoadProducts failed: %s", err)
    }

    fmt.Printf("Load result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Load multiple products in batch
    const products = [
        { name: "Laptop", price: 999.99, category: "Electronics" },
        { name: "Mouse", price: 29.99, category: "Electronics" },
        { name: "Keyboard", price: 79.99, category: "Electronics" },
        { name: "Monitor", price: 299.99, category: "Electronics" },
        { name: "Desk Chair", price: 199.99, category: "Furniture" },
    ];

    const result = await client.query("LoadProducts", { products });
    console.log("Load result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
curl -X POST \
  http://localhost:6969/LoadProducts \
  -H 'Content-Type: application/json' \
  -d '{
    "products": [
      {"name": "Laptop", "price": 999.99, "category": "Electronics"},
      {"name": "Mouse", "price": 29.99, "category": "Electronics"},
      {"name": "Keyboard", "price": 79.99, "category": "Electronics"},
      {"name": "Monitor", "price": 299.99, "category": "Electronics"},
      {"name": "Desk Chair", "price": 199.99, "category": "Furniture"}
    ]
  }'

FOR Loop Destructuring

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Destructure collection elements in FOR loops

Use destructuring syntax to extract specific properties from collection elements directly in the loop variable declaration. This eliminates the need for property access syntax within the loop body.

FOR {field1, field2, field3} IN collection {
    // Use field1, field2, field3 directly
}

FOR {object.field1, object.field2} IN collection {
    // Access nested properties
}

ℹ️ Note

Destructuring makes your queries more readable and explicit about which properties are being used in the loop body.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Basic destructuring

QUERY CreateUsersFromData (user_data: [{name: String, age: U8, email: String}]) =>
    FOR {name, age, email} IN user_data {
        user <- AddN<User>({
            name: name,
            age: age,
            email: email
        })
    }
    RETURN "Users created successfully"
N::User {
    name: String,
    age: U8,
    email: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create users using destructuring
user_data = [
    {"name": "Alice", "age": 25, "email": "alice@example.com"},
    {"name": "Bob", "age": 30, "email": "bob@example.com"},
    {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
    {"name": "Diana", "age": 22, "email": "diana@example.com"},
]

result = client.query("CreateUsersFromData", {"user_data": user_data})
print("Create result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create users using destructuring
    let user_data = vec![
        json!({"name": "Alice", "age": 25, "email": "alice@example.com"}),
        json!({"name": "Bob", "age": 30, "email": "bob@example.com"}),
        json!({"name": "Charlie", "age": 28, "email": "charlie@example.com"}),
        json!({"name": "Diana", "age": 22, "email": "diana@example.com"}),
    ];

    let result: serde_json::Value = client.query("CreateUsersFromData", &json!({
        "user_data": user_data
    })).await?;
    println!("Create result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create users using destructuring
    userData := []map[string]any{
        {"name": "Alice", "age": uint8(25), "email": "alice@example.com"},
        {"name": "Bob", "age": uint8(30), "email": "bob@example.com"},
        {"name": "Charlie", "age": uint8(28), "email": "charlie@example.com"},
        {"name": "Diana", "age": uint8(22), "email": "diana@example.com"},
    }

    var result map[string]any
    if err := client.Query("CreateUsersFromData", helix.WithData(map[string]any{
        "user_data": userData,
    })).Scan(&result); err != nil {
        log.Fatalf("CreateUsersFromData failed: %s", err)
    }

    fmt.Printf("Create result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create users using destructuring
    const userData = [
        { name: "Alice", age: 25, email: "alice@example.com" },
        { name: "Bob", age: 30, email: "bob@example.com" },
        { name: "Charlie", age: 28, email: "charlie@example.com" },
        { name: "Diana", age: 22, email: "diana@example.com" },
    ];

    const result = await client.query("CreateUsersFromData", { user_data: userData });
    console.log("Create result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUsersFromData \
  -H 'Content-Type: application/json' \
  -d '{
    "user_data": [
      {"name": "Alice", "age": 25, "email": "alice@example.com"},
      {"name": "Bob", "age": 30, "email": "bob@example.com"},
      {"name": "Charlie", "age": 28, "email": "charlie@example.com"},
      {"name": "Diana", "age": 22, "email": "diana@example.com"}
    ]
  }'

Example 2: Nested property access with destructuring

QUERY LoadOrdersWithItems (
    order_data: [{
        customer: {name: String, email: String},
        items: [{product: String, quantity: I32, price: F64}]
    }]
) =>
    FOR {customer, items} IN order_data {
        order_node <- AddN<Order>({
            customer_name: customer.name,
            customer_email: customer.email,
            total_items: 0
        })
        FOR {product, quantity, price} IN items {
            item_node <- AddN<OrderItem>({
                product_name: product,
                quantity: quantity,
                unit_price: price
            })
            AddE<Contains>::From(order_node)::To(item_node)
        }
    }
    RETURN "Orders loaded successfully"
N::Order {
    customer_name: String,
    customer_email: String,
    total_items: I32
}

N::OrderItem {
    product_name: String,
    quantity: I32,
    unit_price: F64
}

E::Contains {
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Load orders with nested destructuring
order_data = [
    {
        "customer": {"name": "Alice", "email": "alice@example.com"},
        "items": [
            {"product": "Laptop", "quantity": 1, "price": 999.99},
            {"product": "Mouse", "quantity": 2, "price": 29.99},
        ]
    },
    {
        "customer": {"name": "Bob", "email": "bob@example.com"},
        "items": [
            {"product": "Keyboard", "quantity": 1, "price": 79.99},
            {"product": "Monitor", "quantity": 2, "price": 299.99},
        ]
    },
]

result = client.query("LoadOrdersWithItems", {"order_data": order_data})
print("Load result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Load orders with nested destructuring
    let order_data = vec![
        json!({
            "customer": {"name": "Alice", "email": "alice@example.com"},
            "items": [
                {"product": "Laptop", "quantity": 1, "price": 999.99},
                {"product": "Mouse", "quantity": 2, "price": 29.99},
            ]
        }),
        json!({
            "customer": {"name": "Bob", "email": "bob@example.com"},
            "items": [
                {"product": "Keyboard", "quantity": 1, "price": 79.99},
                {"product": "Monitor", "quantity": 2, "price": 299.99},
            ]
        }),
    ];

    let result: serde_json::Value = client.query("LoadOrdersWithItems", &json!({
        "order_data": order_data
    })).await?;
    println!("Load result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Load orders with nested destructuring
    orderData := []map[string]any{
        {
            "customer": map[string]any{"name": "Alice", "email": "alice@example.com"},
            "items": []map[string]any{
                {"product": "Laptop", "quantity": 1, "price": 999.99},
                {"product": "Mouse", "quantity": 2, "price": 29.99},
            },
        },
        {
            "customer": map[string]any{"name": "Bob", "email": "bob@example.com"},
            "items": []map[string]any{
                {"product": "Keyboard", "quantity": 1, "price": 79.99},
                {"product": "Monitor", "quantity": 2, "price": 299.99},
            },
        },
    }

    var result map[string]any
    if err := client.Query("LoadOrdersWithItems", helix.WithData(map[string]any{
        "order_data": orderData,
    })).Scan(&result); err != nil {
        log.Fatalf("LoadOrdersWithItems failed: %s", err)
    }

    fmt.Printf("Load result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Load orders with nested destructuring
    const orderData = [
        {
            customer: { name: "Alice", email: "alice@example.com" },
            items: [
                { product: "Laptop", quantity: 1, price: 999.99 },
                { product: "Mouse", quantity: 2, price: 29.99 },
            ]
        },
        {
            customer: { name: "Bob", email: "bob@example.com" },
            items: [
                { product: "Keyboard", quantity: 1, price: 79.99 },
                { product: "Monitor", quantity: 2, price: 299.99 },
            ]
        },
    ];

    const result = await client.query("LoadOrdersWithItems", { order_data: orderData });
    console.log("Load result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
curl -X POST \
  http://localhost:6969/LoadOrdersWithItems \
  -H 'Content-Type: application/json' \
  -d '{
    "order_data": [
      {
        "customer": {"name": "Alice", "email": "alice@example.com"},
        "items": [
          {"product": "Laptop", "quantity": 1, "price": 999.99},
          {"product": "Mouse", "quantity": 2, "price": 29.99}
        ]
      },
      {
        "customer": {"name": "Bob", "email": "bob@example.com"},
        "items": [
          {"product": "Keyboard", "quantity": 1, "price": 79.99},
          {"product": "Monitor", "quantity": 2, "price": 299.99}
        ]
      }
    ]
  }'

Example 3: Selective field extraction

QUERY CreateProductsFromCatalog (
    catalog: [{
        id: String,
        name: String,
        price: F64,
        internal_code: String,
        warehouse_location: String
    }]
) =>
    FOR {name, price} IN catalog {
        product <- AddN<Product>({
            name: name,
            price: price,
            stock: 100
        })
    }
    RETURN "Products created from catalog"
N::Product {
    name: String,
    price: F64,
    stock: I32
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create products extracting only needed fields
catalog = [
    {
        "id": "P001",
        "name": "Laptop",
        "price": 999.99,
        "internal_code": "WH-A-001",
        "warehouse_location": "Building A, Aisle 3"
    },
    {
        "id": "P002",
        "name": "Mouse",
        "price": 29.99,
        "internal_code": "WH-B-042",
        "warehouse_location": "Building B, Aisle 1"
    },
    {
        "id": "P003",
        "name": "Keyboard",
        "price": 79.99,
        "internal_code": "WH-A-015",
        "warehouse_location": "Building A, Aisle 5"
    },
]

result = client.query("CreateProductsFromCatalog", {"catalog": catalog})
print("Create result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create products extracting only needed fields
    let catalog = vec![
        json!({
            "id": "P001",
            "name": "Laptop",
            "price": 999.99,
            "internal_code": "WH-A-001",
            "warehouse_location": "Building A, Aisle 3"
        }),
        json!({
            "id": "P002",
            "name": "Mouse",
            "price": 29.99,
            "internal_code": "WH-B-042",
            "warehouse_location": "Building B, Aisle 1"
        }),
        json!({
            "id": "P003",
            "name": "Keyboard",
            "price": 79.99,
            "internal_code": "WH-A-015",
            "warehouse_location": "Building A, Aisle 5"
        }),
    ];

    let result: serde_json::Value = client.query("CreateProductsFromCatalog", &json!({
        "catalog": catalog
    })).await?;
    println!("Create result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create products extracting only needed fields
    catalog := []map[string]any{
        {
            "id":                 "P001",
            "name":               "Laptop",
            "price":              999.99,
            "internal_code":      "WH-A-001",
            "warehouse_location": "Building A, Aisle 3",
        },
        {
            "id":                 "P002",
            "name":               "Mouse",
            "price":              29.99,
            "internal_code":      "WH-B-042",
            "warehouse_location": "Building B, Aisle 1",
        },
        {
            "id":                 "P003",
            "name":               "Keyboard",
            "price":              79.99,
            "internal_code":      "WH-A-015",
            "warehouse_location": "Building A, Aisle 5",
        },
    }

    var result map[string]any
    if err := client.Query("CreateProductsFromCatalog", helix.WithData(map[string]any{
        "catalog": catalog,
    })).Scan(&result); err != nil {
        log.Fatalf("CreateProductsFromCatalog failed: %s", err)
    }

    fmt.Printf("Create result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create products extracting only needed fields
    const catalog = [
        {
            id: "P001",
            name: "Laptop",
            price: 999.99,
            internal_code: "WH-A-001",
            warehouse_location: "Building A, Aisle 3"
        },
        {
            id: "P002",
            name: "Mouse",
            price: 29.99,
            internal_code: "WH-B-042",
            warehouse_location: "Building B, Aisle 1"
        },
        {
            id: "P003",
            name: "Keyboard",
            price: 79.99,
            internal_code: "WH-A-015",
            warehouse_location: "Building A, Aisle 5"
        },
    ];

    const result = await client.query("CreateProductsFromCatalog", { catalog });
    console.log("Create result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateProductsFromCatalog \
  -H 'Content-Type: application/json' \
  -d '{
    "catalog": [
      {
        "id": "P001",
        "name": "Laptop",
        "price": 999.99,
        "internal_code": "WH-A-001",
        "warehouse_location": "Building A, Aisle 3"
      },
      {
        "id": "P002",
        "name": "Mouse",
        "price": 29.99,
        "internal_code": "WH-B-042",
        "warehouse_location": "Building B, Aisle 1"
      },
      {
        "id": "P003",
        "name": "Keyboard",
        "price": 79.99,
        "internal_code": "WH-A-015",
        "warehouse_location": "Building A, Aisle 5"
      }
    ]
  }'

Nested FOR Loops

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Use nested FOR loops for multi-level iteration

Nest FOR loops to perform operations across multiple collections simultaneously. This is particularly useful for creating relationships between all pairs of elements or processing hierarchical data structures.

FOR outer_var IN outer_collection {
    FOR inner_var IN inner_collection {
        // Operations using both outer_var and inner_var
    }
}

ℹ️ Note

Nested loops are executed for every combination of elements from the outer and inner collections, resulting in a cartesian product of operations.

⚠️ Warning

Be mindful of performance when using nested loops with large collections. The number of operations grows multiplicatively with collection sizes (N × M operations for two collections).

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Creating connections between all user pairs

QUERY CreateUserNetwork (user_data: [{name: String, age: U8, interests: String}]) =>
    // First, create all users
    FOR {name, age, interests} IN user_data {
        user <- AddN<User>({
            name: name,
            age: age,
            interests: interests
        })
    }

    // Then create connections between all users
    all_users <- N<User>
    FOR user1 IN all_users {
        FOR user2 IN all_users {
            // Don't create self-connections
            IF user1::ID != user2::ID THEN {
                AddE<Knows>::From(user1)::To(user2)
            }
        }
    }
    RETURN "User network created"
N::User {
    name: String,
    age: U8,
    interests: String
}

E::Knows {
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create a fully connected user network
user_data = [
    {"name": "Alice", "age": 25, "interests": "Technology, Reading"},
    {"name": "Bob", "age": 30, "interests": "Sports, Music"},
    {"name": "Charlie", "age": 28, "interests": "Art, Photography"},
    {"name": "Diana", "age": 22, "interests": "Travel, Cooking"},
]

result = client.query("CreateUserNetwork", {"user_data": user_data})
print("Network creation result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create a fully connected user network
    let user_data = vec![
        json!({"name": "Alice", "age": 25, "interests": "Technology, Reading"}),
        json!({"name": "Bob", "age": 30, "interests": "Sports, Music"}),
        json!({"name": "Charlie", "age": 28, "interests": "Art, Photography"}),
        json!({"name": "Diana", "age": 22, "interests": "Travel, Cooking"}),
    ];

    let result: serde_json::Value = client.query("CreateUserNetwork", &json!({
        "user_data": user_data
    })).await?;
    println!("Network creation result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create a fully connected user network
    userData := []map[string]any{
        {"name": "Alice", "age": uint8(25), "interests": "Technology, Reading"},
        {"name": "Bob", "age": uint8(30), "interests": "Sports, Music"},
        {"name": "Charlie", "age": uint8(28), "interests": "Art, Photography"},
        {"name": "Diana", "age": uint8(22), "interests": "Travel, Cooking"},
    }

    var result map[string]any
    if err := client.Query("CreateUserNetwork", helix.WithData(map[string]any{
        "user_data": userData,
    })).Scan(&result); err != nil {
        log.Fatalf("CreateUserNetwork failed: %s", err)
    }

    fmt.Printf("Network creation result: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create a fully connected user network
    const userData = [
        { name: "Alice", age: 25, interests: "Technology, Reading" },
        { name: "Bob", age: 30, interests: "Sports, Music" },
        { name: "Charlie", age: 28, interests: "Art, Photography" },
        { name: "Diana", age: 22, interests: "Travel, Cooking" },
    ];

    const result = await client.query("CreateUserNetwork", { user_data: userData });
    console.log("Network creation result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUserNetwork \
  -H 'Content-Type: application/json' \
  -d '{
    "user_data": [
      {"name": "Alice", "age": 25, "interests": "Technology, Reading"},
      {"name": "Bob", "age": 30, "interests": "Sports, Music"},
      {"name": "Charlie", "age": 28, "interests": "Art, Photography"},
      {"name": "Diana", "age": 22, "interests": "Travel, Cooking"}
    ]
  }'

Example 2: Cross-product operations with hierarchical data

QUERY LoadDocumentStructure (
    chapters: [{
        id: I64,
        title: String,
        subchapters: [{
            title: String,
            content: String,
            chunks: [{chunk: String, vector: [F64]}]
        }]
    }]
) =>
    FOR {id, title, subchapters} IN chapters {
        chapter_node <- AddN<Chapter>({
            chapter_index: id,
            title: title
        })

        FOR {title, content, chunks} IN subchapters {
            subchapter_node <- AddN<SubChapter>({
                title: title,
                content: content
            })
            AddE<Contains>::From(chapter_node)::To(subchapter_node)

            FOR {chunk, vector} IN chunks {
                vec <- AddV<Embedding>(vector)
                AddE<EmbeddingOf>({chunk: chunk})::From(subchapter_node)::To(vec)
            }
        }
    }
    RETURN "Document structure loaded"
N::Chapter {
    chapter_index: I64,
    title: String
}

N::SubChapter {
    title: String,
    content: String
}

V::Embedding {
    dimension: 384
}

E::Contains {
}

E::EmbeddingOf {
    chunk: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client
import random

client = Client(local=True, port=6969)

# Helper to generate sample embeddings
def generate_embedding(dim=384):
    return [random.random() for _ in range(dim)]

# Load hierarchical document structure
chapters = [
    {
        "id": 1,
        "title": "Introduction",
        "subchapters": [
            {
                "title": "Overview",
                "content": "This chapter provides an overview of the topic.",
                "chunks": [
                    {"chunk": "First chunk of text", "vector": generate_embedding()},
                    {"chunk": "Second chunk of text", "vector": generate_embedding()},
                ]
            },
            {
                "title": "Background",
                "content": "Historical context and background information.",
                "chunks": [
                    {"chunk": "Background chunk 1", "vector": generate_embedding()},
                    {"chunk": "Background chunk 2", "vector": generate_embedding()},
                ]
            }
        ]
    },
    {
        "id": 2,
        "title": "Main Content",
        "subchapters": [
            {
                "title": "Key Concepts",
                "content": "Detailed explanation of key concepts.",
                "chunks": [
                    {"chunk": "Concept explanation 1", "vector": generate_embedding()},
                    {"chunk": "Concept explanation 2", "vector": generate_embedding()},
                ]
            }
        ]
    }
]

result = client.query("LoadDocumentStructure", {"chapters": chapters})
print("Document structure result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use rand::Rng;

fn generate_embedding(dim: usize) -> Vec<f64> {
    let mut rng = rand::thread_rng();
    (0..dim).map(|_| rng.gen::<f64>()).collect()
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Load hierarchical document structure
    let chapters = vec![
        json!({
            "id": 1,
            "title": "Introduction",
            "subchapters": [
                {
                    "title": "Overview",
                    "content": "This chapter provides an overview of the topic.",
                    "chunks": [
                        {"chunk": "First chunk of text", "vector": generate_embedding(384)},
                        {"chunk": "Second chunk of text", "vector": generate_embedding(384)},
                    ]
                },
                {
                    "title": "Background",
                    "content": "Historical context and background information.",
                    "chunks": [
                        {"chunk": "Background chunk 1", "vector": generate_embedding(384)},
                        {"chunk": "Background chunk 2", "vector": generate_embedding(384)},
                    ]
                }
            ]
        }),
        json!({
            "id": 2,
            "title": "Main Content",
            "subchapters": [
                {
                    "title": "Key Concepts",
                    "content": "Detailed explanation of key concepts.",
                    "chunks": [
                        {"chunk": "Concept explanation 1", "vector": generate_embedding(384)},
                        {"chunk": "Concept explanation 2", "vector": generate_embedding(384)},
                    ]
                }
            ]
        })
    ];

    let result: serde_json::Value = client.query("LoadDocumentStructure", &json!({
        "chapters": chapters
    })).await?;
    println!("Document structure result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "math/rand"

    "github.com/HelixDB/helix-go"
)

func generateEmbedding(dim int) []float64 {
    embedding := make([]float64, dim)
    for i := range embedding {
        embedding[i] = rand.Float64()
    }
    return embedding
}

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Load hierarchical document structure
    chapters := []map[string]any{
        {
            "id":    int64(1),
            "title": "Introduction",
            "subchapters": []map[string]any{
                {
                    "title":   "Overview",
                    "content": "This chapter provides an overview of the topic.",
                    "chunks": []map[string]any{
                        {"chunk": "First chunk of text", "vector": generateEmbedding(384)},
                        {"chunk": "Second chunk of text", "vector": generateEmbedding(384)},
                    },
                },
                {
                    "title":   "Background",
                    "content": "Historical context and background information.",
                    "chunks": []map[string]any{
                        {"chunk": "Background chunk 1", "vector": generateEmbedding(384)},
                        {"chunk": "Background chunk 2", "vector": generateEmbedding(384)},
                    },
                },
            },
        },
        {
            "id":    int64(2),
            "title": "Main Content",
            "subchapters": []map[string]any{
                {
                    "title":   "Key Concepts",
                    "content": "Detailed explanation of key concepts.",
                    "chunks": []map[string]any{
                        {"chunk": "Concept explanation 1", "vector": generateEmbedding(384)},
                        {"chunk": "Concept explanation 2", "vector": generateEmbedding(384)},
                    },
                },
            },
        },
    }

    var result map[string]any
    if err := client.Query("LoadDocumentStructure", helix.WithData(map[string]any{
        "chapters": chapters,
    })).Scan(&result); err != nil {
        log.Fatalf("LoadDocumentStructure failed: %s", err)
    }

    fmt.Printf("Document structure result: %#v\n", result)
}
import HelixDB from "helix-ts";

function generateEmbedding(dim: number = 384): number[] {
    return Array.from({ length: dim }, () => Math.random());
}

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Load hierarchical document structure
    const chapters = [
        {
            id: 1,
            title: "Introduction",
            subchapters: [
                {
                    title: "Overview",
                    content: "This chapter provides an overview of the topic.",
                    chunks: [
                        { chunk: "First chunk of text", vector: generateEmbedding() },
                        { chunk: "Second chunk of text", vector: generateEmbedding() },
                    ]
                },
                {
                    title: "Background",
                    content: "Historical context and background information.",
                    chunks: [
                        { chunk: "Background chunk 1", vector: generateEmbedding() },
                        { chunk: "Background chunk 2", vector: generateEmbedding() },
                    ]
                }
            ]
        },
        {
            id: 2,
            title: "Main Content",
            subchapters: [
                {
                    title: "Key Concepts",
                    content: "Detailed explanation of key concepts.",
                    chunks: [
                        { chunk: "Concept explanation 1", vector: generateEmbedding() },
                        { chunk: "Concept explanation 2", vector: generateEmbedding() },
                    ]
                }
            ]
        }
    ];

    const result = await client.query("LoadDocumentStructure", { chapters });
    console.log("Document structure result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Note: This example shows the structure. In practice, you'd generate actual embedding vectors.
curl -X POST \
  http://localhost:6969/LoadDocumentStructure \
  -H 'Content-Type: application/json' \
  -d '{
    "chapters": [
      {
        "id": 1,
        "title": "Introduction",
        "subchapters": [
          {
            "title": "Overview",
            "content": "This chapter provides an overview of the topic.",
            "chunks": [
              {"chunk": "First chunk of text", "vector": [0.1, 0.2, 0.3]},
              {"chunk": "Second chunk of text", "vector": [0.4, 0.5, 0.6]}
            ]
          }
        ]
      }
    ]
  }'

Performance considerations

⚠️ Warning

Nested loops can significantly impact performance:

  • Two loops over collections of size N and M result in N × M operations
  • Three nested loops result in N × M × P operations
  • Consider using WHERE clauses to filter collections before looping
  • For large datasets, evaluate whether nested loops are the most efficient approach

Tips for optimizing nested loops:

  1. Filter early: Apply WHERE clauses before entering loops to reduce iteration count
  2. Consider alternatives: Sometimes traversals or joins can be more efficient than nested loops
  3. Batch operations: Group related operations to minimize database calls
  4. Monitor performance: Test with realistic data volumes to identify bottlenecks

Math Functions

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Mathematical Functions in HelixQL

HelixQL provides a comprehensive set of mathematical functions for performing calculations, transformations, and aggregations within your queries. These functions can be used anywhere expressions are allowed, including custom weight calculations for shortest paths, property transformations, and conditional logic.

Function Categories

  • Arithmetic — Basic math operations: ADD, SUB, MUL, DIV, POW, MOD

  • Unary Math — Single-argument functions: ABS, SQRT, LN, LOG, EXP, CEIL, FLOOR, ROUND

  • Trigonometry — Trig functions: SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2

  • Constants — Mathematical constants: PI, E

  • Aggregates — Collection operations: MIN, MAX, SUM, AVG, COUNT

Common Use Cases

1. Custom Weight Calculations

Use math functions to calculate dynamic weights for shortest path algorithms:

::ShortestPathDijkstras<Route>(
    MUL(_::{distance}, POW(0.95, DIV(_::{days_old}, 30)))
)

2. Property Transformations

Transform property values during queries:

QUERY NormalizeScores(threshold: F64) =>
    items <- N::Item
        ::{
            raw_score,
            normalized: DIV(_::{raw_score}, 100.0),
            above_threshold: _::{raw_score}::GT(threshold)
        }
    RETURN items

3. Distance Calculations

Calculate distances using mathematical formulas:

QUERY CalculateDistance(x1: F64, y1: F64, x2: F64, y2: F64) =>
    dx <- SUB(x2, x1)
    dy <- SUB(y2, y1)
    distance <- SQRT(ADD(POW(dx, 2.0), POW(dy, 2.0)))
    RETURN distance

4. Aggregation and Statistics

Perform statistical calculations on collections:

QUERY GetProductStats() =>
    products <- N::Product
    stats <- {
        total: COUNT(products),
        min_price: MIN(products::{price}),
        max_price: MAX(products::{price}),
        avg_price: AVG(products::{price}),
        total_revenue: SUM(products::{revenue})
    }
    RETURN stats

Function Composition

Math functions can be nested and composed to create complex expressions:

// Exponential decay with normalization
MUL(
    DIV(_::{score}, 100.0),
    EXP(MUL(-0.1, _::{age_days}))
)

// Weighted scoring with multiple factors
ADD(
    MUL(_::{relevance}, 0.6),
    MUL(_::{popularity}, 0.3),
    MUL(_::{recency}, 0.1)
)

Type Handling

Mathematical functions in HelixQL handle numeric types appropriately:

  • Integer types: I8, I16, I32, I64, U8, U16, U32, U64
  • Floating-point types: F32, F64

ℹ️ Note

Functions that produce fractional results (like DIV, SQRT) will return floating-point values. Ensure your type annotations match the expected output types.

Performance Considerations

  • Simple operations (ADD, SUB, MUL) are highly optimized and add negligible overhead
  • Complex functions (trigonometry, logarithms) have more computational cost
  • Aggregate functions process entire collections and scale with collection size
  • Use math functions in weight calculations for shortest paths to enable dynamic routing

Arithmetic Functions

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Arithmetic Operations

HelixQL provides six fundamental arithmetic functions for performing basic mathematical calculations in your queries.

Available Functions

ADD - Addition

ADD(a, b)  // Returns a + b

SUB - Subtraction

SUB(a, b)  // Returns a - b

MUL - Multiplication

MUL(a, b)  // Returns a * b

DIV - Division

DIV(a, b)  // Returns a / b

POW - Power

POW(base, exponent)  // Returns base^exponent

MOD - Modulo

MOD(a, b)  // Returns a % b (remainder)

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Basic arithmetic in property calculations

Calculate discounted prices and tax for products:

QUERY CalculateProductPricing(discount_percent: F64, tax_rate: F64) =>
    products <- N::Product
        ::{
            name,
            original_price: price,
            discount: MUL(_::{price}, DIV(discount_percent, 100.0)),
            final_price: SUB(_::{price}, MUL(_::{price}, DIV(discount_percent, 100.0))),
            with_tax: MUL(SUB(_::{price}, MUL(_::{price}, DIV(discount_percent, 100.0))), ADD(1.0, tax_rate))
        }
    RETURN products

QUERY InsertProduct(name: String, price: F64) =>
    product <- AddN<Product>({ name: name, price: price })
    RETURN product
N::Product {
    name: String,
    price: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Insert sample products
products = [
    {"name": "Laptop", "price": 999.99},
    {"name": "Mouse", "price": 29.99},
    {"name": "Keyboard", "price": 79.99},
]

for product in products:
    client.query("InsertProduct", product)

# Calculate pricing with 15% discount and 8.5% tax
result = client.query("CalculateProductPricing", {
    "discount_percent": 15.0,
    "tax_rate": 0.085
})

print("Product pricing:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let products = vec![
        ("Laptop", 999.99),
        ("Mouse", 29.99),
        ("Keyboard", 79.99),
    ];

    for (name, price) in &products {
        let _inserted: serde_json::Value = client.query("InsertProduct", &json!({
            "name": name,
            "price": price,
        })).await?;
    }

    let result: serde_json::Value = client.query("CalculateProductPricing", &json!({
        "discount_percent": 15.0,
        "tax_rate": 0.085,
    })).await?;

    println!("Product pricing: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    products := []map[string]any{
        {"name": "Laptop", "price": 999.99},
        {"name": "Mouse", "price": 29.99},
        {"name": "Keyboard", "price": 79.99},
    }

    for _, product := range products {
        var inserted map[string]any
        if err := client.Query("InsertProduct", helix.WithData(product)).Scan(&inserted); err != nil {
            log.Fatalf("InsertProduct failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CalculateProductPricing", helix.WithData(map[string]any{
        "discount_percent": 15.0,
        "tax_rate":         0.085,
    })).Scan(&result); err != nil {
        log.Fatalf("CalculateProductPricing failed: %s", err)
    }

    fmt.Printf("Product pricing: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const products = [
        { name: "Laptop", price: 999.99 },
        { name: "Mouse", price: 29.99 },
        { name: "Keyboard", price: 79.99 },
    ];

    for (const product of products) {
        await client.query("InsertProduct", product);
    }

    const result = await client.query("CalculateProductPricing", {
        discount_percent: 15.0,
        tax_rate: 0.085,
    });

    console.log("Product pricing:", result);
}

main().catch((err) => {
    console.error("CalculateProductPricing query failed:", err);
});
curl -X POST \
  http://localhost:6969/InsertProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Laptop","price":999.99}'

curl -X POST \
  http://localhost:6969/InsertProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Mouse","price":29.99}'

curl -X POST \
  http://localhost:6969/InsertProduct \
  -H 'Content-Type: application/json' \
  -d '{"name":"Keyboard","price":79.99}'

curl -X POST \
  http://localhost:6969/CalculateProductPricing \
  -H 'Content-Type: application/json' \
  -d '{"discount_percent":15.0,"tax_rate":0.085}'

Example 2: Using POW for exponential calculations

Calculate compound interest over time:

QUERY CalculateInvestmentGrowth(years: I32, rate: F64) =>
    accounts <- N::Account
        ::{
            account_id,
            initial_amount,
            final_amount: MUL(_::{initial_amount}, POW(ADD(1.0, rate), years))
        }
    RETURN accounts

QUERY CreateAccount(account_id: String, initial_amount: F64) =>
    account <- AddN<Account>({ account_id: account_id, initial_amount: initial_amount })
    RETURN account
N::Account {
    account_id: String,
    initial_amount: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create accounts
accounts = [
    {"account_id": "ACC001", "initial_amount": 10000.0},
    {"account_id": "ACC002", "initial_amount": 25000.0},
    {"account_id": "ACC003", "initial_amount": 50000.0},
]

for account in accounts:
    client.query("CreateAccount", account)

# Calculate growth over 10 years at 7% annual rate
result = client.query("CalculateInvestmentGrowth", {
    "years": 10,
    "rate": 0.07
})

print("Investment growth:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let accounts = vec![
        ("ACC001", 10000.0),
        ("ACC002", 25000.0),
        ("ACC003", 50000.0),
    ];

    for (account_id, initial_amount) in &accounts {
        let _inserted: serde_json::Value = client.query("CreateAccount", &json!({
            "account_id": account_id,
            "initial_amount": initial_amount,
        })).await?;
    }

    let result: serde_json::Value = client.query("CalculateInvestmentGrowth", &json!({
        "years": 10,
        "rate": 0.07,
    })).await?;

    println!("Investment growth: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    accounts := []map[string]any{
        {"account_id": "ACC001", "initial_amount": 10000.0},
        {"account_id": "ACC002", "initial_amount": 25000.0},
        {"account_id": "ACC003", "initial_amount": 50000.0},
    }

    for _, account := range accounts {
        var inserted map[string]any
        if err := client.Query("CreateAccount", helix.WithData(account)).Scan(&inserted); err != nil {
            log.Fatalf("CreateAccount failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CalculateInvestmentGrowth", helix.WithData(map[string]any{
        "years": int32(10),
        "rate":  0.07,
    })).Scan(&result); err != nil {
        log.Fatalf("CalculateInvestmentGrowth failed: %s", err)
    }

    fmt.Printf("Investment growth: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const accounts = [
        { account_id: "ACC001", initial_amount: 10000.0 },
        { account_id: "ACC002", initial_amount: 25000.0 },
        { account_id: "ACC003", initial_amount: 50000.0 },
    ];

    for (const account of accounts) {
        await client.query("CreateAccount", account);
    }

    const result = await client.query("CalculateInvestmentGrowth", {
        years: 10,
        rate: 0.07,
    });

    console.log("Investment growth:", result);
}

main().catch((err) => {
    console.error("CalculateInvestmentGrowth query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateAccount \
  -H 'Content-Type: application/json' \
  -d '{"account_id":"ACC001","initial_amount":10000.0}'

curl -X POST \
  http://localhost:6969/CreateAccount \
  -H 'Content-Type: application/json' \
  -d '{"account_id":"ACC002","initial_amount":25000.0}'

curl -X POST \
  http://localhost:6969/CreateAccount \
  -H 'Content-Type: application/json' \
  -d '{"account_id":"ACC003","initial_amount":50000.0}'

curl -X POST \
  http://localhost:6969/CalculateInvestmentGrowth \
  -H 'Content-Type: application/json' \
  -d '{"years":10,"rate":0.07}'

Example 3: Using MOD for cyclic patterns

Use modulo to determine recurring patterns or groupings:

QUERY CategorizeByRotation(group_size: I64) =>
    items <- N::Item
        ::{
            item_id,
            position,
            group: MOD(_::{position}, group_size),
            is_first_in_group: MOD(_::{position}, group_size)::EQ(0)
        }
    RETURN items

QUERY CreateItem(item_id: String, position: I64) =>
    item <- AddN<Item>({ item_id: item_id, position: position })
    RETURN item
N::Item {
    item_id: String,
    position: I64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create items with positions
for i in range(15):
    client.query("CreateItem", {
        "item_id": f"ITEM{i:03d}",
        "position": i
    })

# Categorize into groups of 5
result = client.query("CategorizeByRotation", {
    "group_size": 5
})

print("Categorized items:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Create items with positions
    for i in 0..15 {
        let _inserted: serde_json::Value = client.query("CreateItem", &json!({
            "item_id": format!("ITEM{:03}", i),
            "position": i,
        })).await?;
    }

    let result: serde_json::Value = client.query("CategorizeByRotation", &json!({
        "group_size": 5,
    })).await?;

    println!("Categorized items: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Create items with positions
    for i := 0; i < 15; i++ {
        var inserted map[string]any
        if err := client.Query("CreateItem", helix.WithData(map[string]any{
            "item_id":  fmt.Sprintf("ITEM%03d", i),
            "position": int64(i),
        })).Scan(&inserted); err != nil {
            log.Fatalf("CreateItem failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CategorizeByRotation", helix.WithData(map[string]any{
        "group_size": int64(5),
    })).Scan(&result); err != nil {
        log.Fatalf("CategorizeByRotation failed: %s", err)
    }

    fmt.Printf("Categorized items: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Create items with positions
    for (let i = 0; i < 15; i++) {
        await client.query("CreateItem", {
            item_id: `ITEM${i.toString().padStart(3, '0')}`,
            position: i,
        });
    }

    const result = await client.query("CategorizeByRotation", {
        group_size: 5,
    });

    console.log("Categorized items:", result);
}

main().catch((err) => {
    console.error("CategorizeByRotation query failed:", err);
});
# Create items
for i in {0..14}; do
  curl -X POST \
    http://localhost:6969/CreateItem \
    -H 'Content-Type: application/json' \
    -d "{\"item_id\":\"ITEM$(printf %03d $i)\",\"position\":$i}"
done

curl -X POST \
  http://localhost:6969/CategorizeByRotation \
  -H 'Content-Type: application/json' \
  -d '{"group_size":5}'

Function Composition

Arithmetic functions can be nested to create complex calculations:

// Multi-factor scoring
ADD(
    MUL(_::{base_score}, 0.6),
    MUL(_::{bonus_score}, 0.4)
)

// Percentage calculation
MUL(DIV(_::{partial}, _::{total}), 100.0)

// Exponential decay
MUL(_::{initial_value}, POW(0.9, _::{time_elapsed}))

Use in Shortest Path Weights

Arithmetic functions are commonly used in custom weight calculations:

// Distance-based weight with decay
::ShortestPathDijkstras<Route>(
    MUL(_::{distance}, POW(0.95, DIV(_::{days_old}, 30)))
)

// Multi-factor routing cost
::ShortestPathDijkstras<Road>(
    ADD(
        MUL(_::{distance}, 0.7),
        MUL(_::{toll_cost}, 0.3)
    )
)

Unary Math Functions

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Unary Mathematical Functions

HelixQL provides a rich set of single-argument mathematical functions for transforming numeric values, including absolute values, roots, logarithms, exponentials, and rounding operations.

Available Functions

ABS - Absolute Value

ABS(x)  // Returns |x|

Returns the absolute value of a number.

SQRT - Square Root

SQRT(x)  // Returns √x

Returns the square root of a non-negative number.

LN - Natural Logarithm

LN(x)  // Returns ln(x)

Returns the natural logarithm (base e) of a positive number.

LOG10 - Base-10 Logarithm

LOG10(x)  // Returns log₁₀(x)

Returns the base-10 logarithm of a positive number.

LOG - Custom Base Logarithm

LOG(x, base)  // Returns log_base(x)

Returns the logarithm of x with a custom base.

EXP - Exponential

EXP(x)  // Returns e^x

Returns e raised to the power of x.

CEIL - Ceiling

CEIL(x)  // Returns ⌈x⌉

Rounds up to the nearest integer.

FLOOR - Floor

FLOOR(x)  // Returns ⌊x⌋

Rounds down to the nearest integer.

ROUND - Round

ROUND(x)  // Returns round(x)

Rounds to the nearest integer.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Distance calculations with SQRT

Calculate Euclidean distances between points:

QUERY CalculateDistances() =>
    points <- N::Point
        ::{
            x, y,
            distance_from_origin: SQRT(ADD(POW(_::{x}, 2.0), POW(_::{y}, 2.0))),
            rounded_distance: ROUND(SQRT(ADD(POW(_::{x}, 2.0), POW(_::{y}, 2.0))))
        }
    RETURN points

QUERY CreatePoint(x: F64, y: F64) =>
    point <- AddN<Point>({ x: x, y: y })
    RETURN point
N::Point {
    x: F64,
    y: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create points
points = [
    {"x": 3.0, "y": 4.0},
    {"x": 5.0, "y": 12.0},
    {"x": 8.0, "y": 15.0},
]

for point in points:
    client.query("CreatePoint", point)

result = client.query("CalculateDistances", {})
print("Point distances:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let points = vec![(3.0, 4.0), (5.0, 12.0), (8.0, 15.0)];

    for (x, y) in &points {
        let _inserted: serde_json::Value = client.query("CreatePoint", &json!({
            "x": x,
            "y": y,
        })).await?;
    }

    let result: serde_json::Value = client.query("CalculateDistances", &json!({})).await?;

    println!("Point distances: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    points := []map[string]any{
        {"x": 3.0, "y": 4.0},
        {"x": 5.0, "y": 12.0},
        {"x": 8.0, "y": 15.0},
    }

    for _, point := range points {
        var inserted map[string]any
        if err := client.Query("CreatePoint", helix.WithData(point)).Scan(&inserted); err != nil {
            log.Fatalf("CreatePoint failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CalculateDistances", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("CalculateDistances failed: %s", err)
    }

    fmt.Printf("Point distances: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const points = [
        { x: 3.0, y: 4.0 },
        { x: 5.0, y: 12.0 },
        { x: 8.0, y: 15.0 },
    ];

    for (const point of points) {
        await client.query("CreatePoint", point);
    }

    const result = await client.query("CalculateDistances", {});

    console.log("Point distances:", result);
}

main().catch((err) => {
    console.error("CalculateDistances query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreatePoint \
  -H 'Content-Type: application/json' \
  -d '{"x":3.0,"y":4.0}'

curl -X POST \
  http://localhost:6969/CreatePoint \
  -H 'Content-Type: application/json' \
  -d '{"x":5.0,"y":12.0}'

curl -X POST \
  http://localhost:6969/CreatePoint \
  -H 'Content-Type: application/json' \
  -d '{"x":8.0,"y":15.0}'

curl -X POST \
  http://localhost:6969/CalculateDistances \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Logarithmic scaling with LN and LOG10

Use logarithms for normalization and scaling:

QUERY NormalizeMetrics() =>
    metrics <- N::Metric
        ::{
            value,
            log_scale: LN(_::{value}),
            log10_scale: LOG10(_::{value}),
            custom_log: LOG(_::{value}, 2.0)
        }
    RETURN metrics

QUERY CreateMetric(value: F64) =>
    metric <- AddN<Metric>({ value: value })
    RETURN metric
N::Metric {
    value: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create metrics with various scales
values = [1.0, 10.0, 100.0, 1000.0, 10000.0]

for value in values:
    client.query("CreateMetric", {"value": value})

result = client.query("NormalizeMetrics", {})
print("Normalized metrics:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let values = vec![1.0, 10.0, 100.0, 1000.0, 10000.0];

    for value in &values {
        let _inserted: serde_json::Value = client.query("CreateMetric", &json!({
            "value": value,
        })).await?;
    }

    let result: serde_json::Value = client.query("NormalizeMetrics", &json!({})).await?;

    println!("Normalized metrics: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    values := []float64{1.0, 10.0, 100.0, 1000.0, 10000.0}

    for _, value := range values {
        var inserted map[string]any
        if err := client.Query("CreateMetric", helix.WithData(map[string]any{
            "value": value,
        })).Scan(&inserted); err != nil {
            log.Fatalf("CreateMetric failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("NormalizeMetrics", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("NormalizeMetrics failed: %s", err)
    }

    fmt.Printf("Normalized metrics: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const values = [1.0, 10.0, 100.0, 1000.0, 10000.0];

    for (const value of values) {
        await client.query("CreateMetric", { value });
    }

    const result = await client.query("NormalizeMetrics", {});

    console.log("Normalized metrics:", result);
}

main().catch((err) => {
    console.error("NormalizeMetrics query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateMetric \
  -H 'Content-Type: application/json' \
  -d '{"value":1.0}'

curl -X POST \
  http://localhost:6969/CreateMetric \
  -H 'Content-Type: application/json' \
  -d '{"value":10.0}'

curl -X POST \
  http://localhost:6969/CreateMetric \
  -H 'Content-Type: application/json' \
  -d '{"value":100.0}'

curl -X POST \
  http://localhost:6969/NormalizeMetrics \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 3: Exponential decay with EXP

Model time-based decay using exponential functions:

QUERY CalculateDecayFactors(decay_rate: F64) =>
    items <- N::Item
        ::{
            name, age_days,
            decay_factor: EXP(MUL(decay_rate, _::{age_days})),
            relevance_score: MUL(_::{base_score}, EXP(MUL(decay_rate, _::{age_days})))
        }
    RETURN items

QUERY CreateItem(name: String, age_days: F64, base_score: F64) =>
    item <- AddN<Item>({ name: name, age_days: age_days, base_score: base_score })
    RETURN item
N::Item {
    name: String,
    age_days: F64,
    base_score: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create items with different ages
items = [
    {"name": "Recent Post", "age_days": 1.0, "base_score": 100.0},
    {"name": "Week Old Post", "age_days": 7.0, "base_score": 100.0},
    {"name": "Month Old Post", "age_days": 30.0, "base_score": 100.0},
]

for item in items:
    client.query("CreateItem", item)

# Apply decay rate of -0.1 per day
result = client.query("CalculateDecayFactors", {
    "decay_rate": -0.1
})

print("Decay factors:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let items = vec![
        ("Recent Post", 1.0, 100.0),
        ("Week Old Post", 7.0, 100.0),
        ("Month Old Post", 30.0, 100.0),
    ];

    for (name, age_days, base_score) in &items {
        let _inserted: serde_json::Value = client.query("CreateItem", &json!({
            "name": name,
            "age_days": age_days,
            "base_score": base_score,
        })).await?;
    }

    let result: serde_json::Value = client.query("CalculateDecayFactors", &json!({
        "decay_rate": -0.1,
    })).await?;

    println!("Decay factors: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    items := []map[string]any{
        {"name": "Recent Post", "age_days": 1.0, "base_score": 100.0},
        {"name": "Week Old Post", "age_days": 7.0, "base_score": 100.0},
        {"name": "Month Old Post", "age_days": 30.0, "base_score": 100.0},
    }

    for _, item := range items {
        var inserted map[string]any
        if err := client.Query("CreateItem", helix.WithData(item)).Scan(&inserted); err != nil {
            log.Fatalf("CreateItem failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CalculateDecayFactors", helix.WithData(map[string]any{
        "decay_rate": -0.1,
    })).Scan(&result); err != nil {
        log.Fatalf("CalculateDecayFactors failed: %s", err)
    }

    fmt.Printf("Decay factors: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const items = [
        { name: "Recent Post", age_days: 1.0, base_score: 100.0 },
        { name: "Week Old Post", age_days: 7.0, base_score: 100.0 },
        { name: "Month Old Post", age_days: 30.0, base_score: 100.0 },
    ];

    for (const item of items) {
        await client.query("CreateItem", item);
    }

    const result = await client.query("CalculateDecayFactors", {
        decay_rate: -0.1,
    });

    console.log("Decay factors:", result);
}

main().catch((err) => {
    console.error("CalculateDecayFactors query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateItem \
  -H 'Content-Type: application/json' \
  -d '{"name":"Recent Post","age_days":1.0,"base_score":100.0}'

curl -X POST \
  http://localhost:6969/CreateItem \
  -H 'Content-Type: application/json' \
  -d '{"name":"Week Old Post","age_days":7.0,"base_score":100.0}'

curl -X POST \
  http://localhost:6969/CreateItem \
  -H 'Content-Type: application/json' \
  -d '{"name":"Month Old Post","age_days":30.0,"base_score":100.0}'

curl -X POST \
  http://localhost:6969/CalculateDecayFactors \
  -H 'Content-Type: application/json' \
  -d '{"decay_rate":-0.1}'

Rounding Functions

CEIL, FLOOR, and ROUND provide different rounding behaviors:

CEIL(3.2)   // Returns 4.0
FLOOR(3.8)  // Returns 3.0
ROUND(3.5)  // Returns 4.0
ROUND(3.4)  // Returns 3.0

Use rounding for display formatting or bucketing:

QUERY BucketScores() =>
    items <- N::Item
        ::{
            raw_score,
            bucket: MUL(FLOOR(DIV(_::{raw_score}, 10.0)), 10.0)
        }
    RETURN items

Domain Restrictions

ℹ️ Note

Some functions have domain restrictions:

  • SQRT(x): x must be non-negative
  • LN(x), LOG10(x), LOG(x, base): x must be positive
  • LOG(x, base): base must be positive and not equal to 1

Use in Weight Calculations

Unary math functions are powerful in shortest path weight calculations:

// Exponential time decay
::ShortestPathDijkstras<Route>(
    MUL(_::{distance}, EXP(MUL(-0.05, _::{days_old})))
)

// Logarithmic scaling for large values
::ShortestPathDijkstras<Connection>(
    LOG10(ADD(_::{traffic}, 1.0))
)

Trigonometric Functions

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Trigonometric Functions

HelixQL provides a comprehensive set of trigonometric functions for angular calculations, including standard trigonometric functions (sine, cosine, tangent) and their inverse counterparts, essential for geospatial calculations, physics simulations, and angular measurements.

Available Functions

SIN - Sine

SIN(x)  // Returns sin(x)

Returns the sine of an angle in radians.

COS - Cosine

COS(x)  // Returns cos(x)

Returns the cosine of an angle in radians.

TAN - Tangent

TAN(x)  // Returns tan(x)

Returns the tangent of an angle in radians.

ASIN - Arcsine

ASIN(x)  // Returns arcsin(x)

Returns the arcsine (inverse sine) of x in radians. Domain: [-1, 1].

ACOS - Arccosine

ACOS(x)  // Returns arccos(x)

Returns the arccosine (inverse cosine) of x in radians. Domain: [-1, 1].

ATAN - Arctangent

ATAN(x)  // Returns arctan(x)

Returns the arctangent (inverse tangent) of x in radians.

ATAN2 - Two-Argument Arctangent

ATAN2(y, x)  // Returns atan2(y, x)

Returns the angle in radians between the positive x-axis and the point (x, y). This function handles all quadrants correctly and is commonly used for angle calculations.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Geospatial bearing calculations

Calculate the bearing (direction) between geographic coordinates:

QUERY CalculateBearings() =>
    locations <- N::Location
        ::{
            name, latitude, longitude,
            radians_lat: MUL(_::{latitude}, DIV(PI(), 180.0)),
            radians_lon: MUL(_::{longitude}, DIV(PI(), 180.0)),
            sin_lat: SIN(MUL(_::{latitude}, DIV(PI(), 180.0))),
            cos_lat: COS(MUL(_::{latitude}, DIV(PI(), 180.0))),
            tan_lat: TAN(MUL(_::{latitude}, DIV(PI(), 180.0)))
        }
    RETURN locations

QUERY CreateLocation(name: String, latitude: F64, longitude: F64) =>
    location <- AddN<Location>({ name: name, latitude: latitude, longitude: longitude })
    RETURN location
N::Location {
    name: String,
    latitude: F64,
    longitude: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create locations with geographic coordinates
locations = [
    {"name": "New York", "latitude": 40.7128, "longitude": -74.0060},
    {"name": "London", "latitude": 51.5074, "longitude": -0.1278},
    {"name": "Tokyo", "latitude": 35.6762, "longitude": 139.6503},
]

for location in locations:
    client.query("CreateLocation", location)

result = client.query("CalculateBearings", {})
print("Location bearings:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let locations = vec![
        ("New York", 40.7128, -74.0060),
        ("London", 51.5074, -0.1278),
        ("Tokyo", 35.6762, 139.6503),
    ];

    for (name, latitude, longitude) in &locations {
        let _inserted: serde_json::Value = client.query("CreateLocation", &json!({
            "name": name,
            "latitude": latitude,
            "longitude": longitude,
        })).await?;
    }

    let result: serde_json::Value = client.query("CalculateBearings", &json!({})).await?;

    println!("Location bearings: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    locations := []map[string]any{
        {"name": "New York", "latitude": 40.7128, "longitude": -74.0060},
        {"name": "London", "latitude": 51.5074, "longitude": -0.1278},
        {"name": "Tokyo", "latitude": 35.6762, "longitude": 139.6503},
    }

    for _, location := range locations {
        var inserted map[string]any
        if err := client.Query("CreateLocation", helix.WithData(location)).Scan(&inserted); err != nil {
            log.Fatalf("CreateLocation failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CalculateBearings", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("CalculateBearings failed: %s", err)
    }

    fmt.Printf("Location bearings: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const locations = [
        { name: "New York", latitude: 40.7128, longitude: -74.0060 },
        { name: "London", latitude: 51.5074, longitude: -0.1278 },
        { name: "Tokyo", latitude: 35.6762, longitude: 139.6503 },
    ];

    for (const location of locations) {
        await client.query("CreateLocation", location);
    }

    const result = await client.query("CalculateBearings", {});

    console.log("Location bearings:", result);
}

main().catch((err) => {
    console.error("CalculateBearings query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateLocation \
  -H 'Content-Type: application/json' \
  -d '{"name":"New York","latitude":40.7128,"longitude":-74.0060}'

curl -X POST \
  http://localhost:6969/CreateLocation \
  -H 'Content-Type: application/json' \
  -d '{"name":"London","latitude":51.5074,"longitude":-0.1278}'

curl -X POST \
  http://localhost:6969/CreateLocation \
  -H 'Content-Type: application/json' \
  -d '{"name":"Tokyo","latitude":35.6762,"longitude":139.6503}'

curl -X POST \
  http://localhost:6969/CalculateBearings \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Angle calculations with ATAN2

Use ATAN2 to calculate angles between vectors and points:

QUERY CalculateAngles() =>
    vectors <- N::Vector
        ::{
            x, y,
            angle_radians: ATAN2(_::{y}, _::{x}),
            angle_degrees: MUL(ATAN2(_::{y}, _::{x}), DIV(180.0, PI()))
        }
    RETURN vectors

QUERY CreateVector(x: F64, y: F64) =>
    vector <- AddN<Vector>({ x: x, y: y })
    RETURN vector
N::Vector {
    x: F64,
    y: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create vectors in different quadrants
vectors = [
    {"x": 1.0, "y": 1.0},      # 45 degrees
    {"x": -1.0, "y": 1.0},     # 135 degrees
    {"x": -1.0, "y": -1.0},    # -135 degrees
    {"x": 1.0, "y": -1.0},     # -45 degrees
]

for vector in vectors:
    client.query("CreateVector", vector)

result = client.query("CalculateAngles", {})
print("Vector angles:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let vectors = vec![
        (1.0, 1.0),      // 45 degrees
        (-1.0, 1.0),     // 135 degrees
        (-1.0, -1.0),    // -135 degrees
        (1.0, -1.0),     // -45 degrees
    ];

    for (x, y) in &vectors {
        let _inserted: serde_json::Value = client.query("CreateVector", &json!({
            "x": x,
            "y": y,
        })).await?;
    }

    let result: serde_json::Value = client.query("CalculateAngles", &json!({})).await?;

    println!("Vector angles: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    vectors := []map[string]any{
        {"x": 1.0, "y": 1.0},      // 45 degrees
        {"x": -1.0, "y": 1.0},     // 135 degrees
        {"x": -1.0, "y": -1.0},    // -135 degrees
        {"x": 1.0, "y": -1.0},     // -45 degrees
    }

    for _, vector := range vectors {
        var inserted map[string]any
        if err := client.Query("CreateVector", helix.WithData(vector)).Scan(&inserted); err != nil {
            log.Fatalf("CreateVector failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CalculateAngles", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("CalculateAngles failed: %s", err)
    }

    fmt.Printf("Vector angles: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const vectors = [
        { x: 1.0, y: 1.0 },      // 45 degrees
        { x: -1.0, y: 1.0 },     // 135 degrees
        { x: -1.0, y: -1.0 },    // -135 degrees
        { x: 1.0, y: -1.0 },     // -45 degrees
    ];

    for (const vector of vectors) {
        await client.query("CreateVector", vector);
    }

    const result = await client.query("CalculateAngles", {});

    console.log("Vector angles:", result);
}

main().catch((err) => {
    console.error("CalculateAngles query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateVector \
  -H 'Content-Type: application/json' \
  -d '{"x":1.0,"y":1.0}'

curl -X POST \
  http://localhost:6969/CreateVector \
  -H 'Content-Type: application/json' \
  -d '{"x":-1.0,"y":1.0}'

curl -X POST \
  http://localhost:6969/CreateVector \
  -H 'Content-Type: application/json' \
  -d '{"x":-1.0,"y":-1.0}'

curl -X POST \
  http://localhost:6969/CreateVector \
  -H 'Content-Type: application/json' \
  -d '{"x":1.0,"y":-1.0}'

curl -X POST \
  http://localhost:6969/CalculateAngles \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 3: Inverse trigonometric functions

Use inverse trigonometric functions to recover angles from ratios:

QUERY RecoverAngles() =>
    measurements <- N::Measurement
        ::{
            ratio,
            angle_from_sin: ASIN(_::{ratio}),
            angle_from_cos: ACOS(_::{ratio}),
            angle_from_tan: ATAN(_::{ratio})
        }
    RETURN measurements

QUERY CreateMeasurement(ratio: F64) =>
    measurement <- AddN<Measurement>({ ratio: ratio })
    RETURN measurement
N::Measurement {
    ratio: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create measurements with various ratios
ratios = [0.0, 0.5, 0.707, 0.866, 1.0]

for ratio in ratios:
    client.query("CreateMeasurement", {"ratio": ratio})

result = client.query("RecoverAngles", {})
print("Recovered angles:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let ratios = vec![0.0, 0.5, 0.707, 0.866, 1.0];

    for ratio in &ratios {
        let _inserted: serde_json::Value = client.query("CreateMeasurement", &json!({
            "ratio": ratio,
        })).await?;
    }

    let result: serde_json::Value = client.query("RecoverAngles", &json!({})).await?;

    println!("Recovered angles: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    ratios := []float64{0.0, 0.5, 0.707, 0.866, 1.0}

    for _, ratio := range ratios {
        var inserted map[string]any
        if err := client.Query("CreateMeasurement", helix.WithData(map[string]any{
            "ratio": ratio,
        })).Scan(&inserted); err != nil {
            log.Fatalf("CreateMeasurement failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("RecoverAngles", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("RecoverAngles failed: %s", err)
    }

    fmt.Printf("Recovered angles: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const ratios = [0.0, 0.5, 0.707, 0.866, 1.0];

    for (const ratio of ratios) {
        await client.query("CreateMeasurement", { ratio });
    }

    const result = await client.query("RecoverAngles", {});

    console.log("Recovered angles:", result);
}

main().catch((err) => {
    console.error("RecoverAngles query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateMeasurement \
  -H 'Content-Type: application/json' \
  -d '{"ratio":0.0}'

curl -X POST \
  http://localhost:6969/CreateMeasurement \
  -H 'Content-Type: application/json' \
  -d '{"ratio":0.5}'

curl -X POST \
  http://localhost:6969/CreateMeasurement \
  -H 'Content-Type: application/json' \
  -d '{"ratio":0.707}'

curl -X POST \
  http://localhost:6969/CreateMeasurement \
  -H 'Content-Type: application/json' \
  -d '{"ratio":0.866}'

curl -X POST \
  http://localhost:6969/CreateMeasurement \
  -H 'Content-Type: application/json' \
  -d '{"ratio":1.0}'

curl -X POST \
  http://localhost:6969/RecoverAngles \
  -H 'Content-Type: application/json' \
  -d '{}'

Radians vs Degrees

All trigonometric functions in HelixQL work with radians. To convert between degrees and radians:

// Degrees to radians
radians = MUL(degrees, DIV(PI(), 180.0))

// Radians to degrees
degrees = MUL(radians, DIV(180.0, PI()))

💡 Tip

Use the PI() constant function for accurate conversion between degrees and radians.

Domain Restrictions

ℹ️ Note

Inverse trigonometric functions have domain restrictions:

  • ASIN(x), ACOS(x): x must be in [-1, 1]
  • ATAN(x): accepts all real numbers
  • ATAN2(y, x): accepts all real numbers for both arguments

ATAN2 vs ATAN

ATAN2 is preferred over ATAN for angle calculations because:

  • It handles all four quadrants correctly
  • It avoids division by zero when x = 0
  • It returns values in the full range [-π, π]
// ATAN2 correctly handles all quadrants
ATAN2(1.0, 1.0)    // π/4 (first quadrant)
ATAN2(1.0, -1.0)   // 3π/4 (second quadrant)
ATAN2(-1.0, -1.0)  // -3π/4 (third quadrant)
ATAN2(-1.0, 1.0)   // -π/4 (fourth quadrant)

Use in Geospatial Calculations

Trigonometric functions are essential for geospatial calculations:

// Calculate great circle distance (Haversine formula)
QUERY CalculateDistance(lat1: F64, lon1: F64, lat2: F64, lon2: F64) =>
    dlat <- SUB(lat2, lat1)
    dlon <- SUB(lon2, lon1)
    a <- ADD(
        POW(SIN(DIV(dlat, 2.0)), 2.0),
        MUL(MUL(COS(lat1), COS(lat2)), POW(SIN(DIV(dlon, 2.0)), 2.0))
    )
    c <- MUL(2.0, ATAN2(SQRT(a), SQRT(SUB(1.0, a))))
    distance <- MUL(6371.0, c)  // Earth radius in km
    RETURN distance

Mathematical Constants

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Mathematical Constants

HelixQL provides built-in mathematical constants PI and E for use in calculations. These constants are provided as functions that return their respective values with high precision.

Available Constants

PI - Pi Constant

PI()  // Returns π ≈ 3.14159265358979323846

Returns the mathematical constant π (pi), the ratio of a circle’s circumference to its diameter.

E - Euler’s Number

E()  // Returns e ≈ 2.71828182845904523536

Returns the mathematical constant e (Euler’s number), the base of natural logarithms.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Circle calculations with PI

Calculate circle properties using the PI constant:

QUERY CalculateCircleProperties() =>
    circles <- N::Circle
        ::{
            radius,
            circumference: MUL(MUL(2.0, PI()), _::{radius}),
            area: MUL(PI(), POW(_::{radius}, 2.0))
        }
    RETURN circles

QUERY CreateCircle(radius: F64) =>
    circle <- AddN<Circle>({ radius: radius })
    RETURN circle
N::Circle {
    radius: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create circles with different radii
radii = [1.0, 5.0, 10.0, 15.0, 20.0]

for radius in radii:
    client.query("CreateCircle", {"radius": radius})

result = client.query("CalculateCircleProperties", {})
print("Circle properties:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let radii = vec![1.0, 5.0, 10.0, 15.0, 20.0];

    for radius in &radii {
        let _inserted: serde_json::Value = client.query("CreateCircle", &json!({
            "radius": radius,
        })).await?;
    }

    let result: serde_json::Value = client.query("CalculateCircleProperties", &json!({})).await?;

    println!("Circle properties: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    radii := []float64{1.0, 5.0, 10.0, 15.0, 20.0}

    for _, radius := range radii {
        var inserted map[string]any
        if err := client.Query("CreateCircle", helix.WithData(map[string]any{
            "radius": radius,
        })).Scan(&inserted); err != nil {
            log.Fatalf("CreateCircle failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CalculateCircleProperties", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("CalculateCircleProperties failed: %s", err)
    }

    fmt.Printf("Circle properties: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const radii = [1.0, 5.0, 10.0, 15.0, 20.0];

    for (const radius of radii) {
        await client.query("CreateCircle", { radius });
    }

    const result = await client.query("CalculateCircleProperties", {});

    console.log("Circle properties:", result);
}

main().catch((err) => {
    console.error("CalculateCircleProperties query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateCircle \
  -H 'Content-Type: application/json' \
  -d '{"radius":1.0}'

curl -X POST \
  http://localhost:6969/CreateCircle \
  -H 'Content-Type: application/json' \
  -d '{"radius":5.0}'

curl -X POST \
  http://localhost:6969/CreateCircle \
  -H 'Content-Type: application/json' \
  -d '{"radius":10.0}'

curl -X POST \
  http://localhost:6969/CreateCircle \
  -H 'Content-Type: application/json' \
  -d '{"radius":15.0}'

curl -X POST \
  http://localhost:6969/CreateCircle \
  -H 'Content-Type: application/json' \
  -d '{"radius":20.0}'

curl -X POST \
  http://localhost:6969/CalculateCircleProperties \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Exponential growth with E

Model exponential growth and decay using Euler’s number:

QUERY CalculateExponentialGrowth(time: F64, rate: F64) =>
    populations <- N::Population
        ::{
            initial_size,
            time_elapsed: time,
            final_size: MUL(_::{initial_size}, POW(E(), MUL(rate, time)))
        }
    RETURN populations

QUERY CreatePopulation(initial_size: F64) =>
    population <- AddN<Population>({ initial_size: initial_size })
    RETURN population
N::Population {
    initial_size: F64
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create populations with different initial sizes
initial_sizes = [100.0, 500.0, 1000.0, 5000.0]

for size in initial_sizes:
    client.query("CreatePopulation", {"initial_size": size})

# Calculate growth after 10 time units with 5% growth rate
result = client.query("CalculateExponentialGrowth", {
    "time": 10.0,
    "rate": 0.05
})

print("Population growth:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let initial_sizes = vec![100.0, 500.0, 1000.0, 5000.0];

    for size in &initial_sizes {
        let _inserted: serde_json::Value = client.query("CreatePopulation", &json!({
            "initial_size": size,
        })).await?;
    }

    let result: serde_json::Value = client.query("CalculateExponentialGrowth", &json!({
        "time": 10.0,
        "rate": 0.05,
    })).await?;

    println!("Population growth: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    initialSizes := []float64{100.0, 500.0, 1000.0, 5000.0}

    for _, size := range initialSizes {
        var inserted map[string]any
        if err := client.Query("CreatePopulation", helix.WithData(map[string]any{
            "initial_size": size,
        })).Scan(&inserted); err != nil {
            log.Fatalf("CreatePopulation failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("CalculateExponentialGrowth", helix.WithData(map[string]any{
        "time": 10.0,
        "rate": 0.05,
    })).Scan(&result); err != nil {
        log.Fatalf("CalculateExponentialGrowth failed: %s", err)
    }

    fmt.Printf("Population growth: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const initialSizes = [100.0, 500.0, 1000.0, 5000.0];

    for (const size of initialSizes) {
        await client.query("CreatePopulation", { initial_size: size });
    }

    const result = await client.query("CalculateExponentialGrowth", {
        time: 10.0,
        rate: 0.05,
    });

    console.log("Population growth:", result);
}

main().catch((err) => {
    console.error("CalculateExponentialGrowth query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreatePopulation \
  -H 'Content-Type: application/json' \
  -d '{"initial_size":100.0}'

curl -X POST \
  http://localhost:6969/CreatePopulation \
  -H 'Content-Type: application/json' \
  -d '{"initial_size":500.0}'

curl -X POST \
  http://localhost:6969/CreatePopulation \
  -H 'Content-Type: application/json' \
  -d '{"initial_size":1000.0}'

curl -X POST \
  http://localhost:6969/CreatePopulation \
  -H 'Content-Type: application/json' \
  -d '{"initial_size":5000.0}'

curl -X POST \
  http://localhost:6969/CalculateExponentialGrowth \
  -H 'Content-Type: application/json' \
  -d '{"time":10.0,"rate":0.05}'

Common Use Cases

Degree-Radian Conversion

Use PI for converting between degrees and radians:

// Degrees to radians
radians = MUL(degrees, DIV(PI(), 180.0))

// Radians to degrees
degrees = MUL(radians, DIV(180.0, PI()))

Circular Motion

Calculate properties of circular motion:

QUERY CalculateAngularVelocity() =>
    objects <- N::RotatingObject
        ::{
            rpm,
            angular_velocity: MUL(MUL(2.0, PI()), DIV(_::{rpm}, 60.0))
        }
    RETURN objects

Compound Interest

Use E for continuous compound interest calculations:

QUERY CalculateCompoundInterest(principal: F64, rate: F64, time: F64) =>
    amount <- MUL(principal, POW(E(), MUL(rate, time)))
    RETURN amount

Natural Decay

Model radioactive decay or other natural decay processes:

QUERY CalculateDecay() =>
    samples <- N::Sample
        ::{
            initial_amount,
            half_life,
            time_elapsed,
            remaining: MUL(
                _::{initial_amount},
                POW(E(), MUL(DIV(LN(0.5), _::{half_life}), _::{time_elapsed}))
            )
        }
    RETURN samples

💡 Tip

Constants are particularly useful when combined with trigonometric functions (SIN, COS, TAN) and exponential functions (EXP, LN).

Precision

Both PI() and E() return high-precision values suitable for scientific and engineering calculations:

  • PI() returns π to approximately 20 decimal places
  • E() returns e to approximately 20 decimal places

ℹ️ Note

The constants are implemented as functions rather than literals to maintain consistency with HelixQL’s function-based syntax.

Use in Complex Formulas

Constants are often used in complex mathematical formulas:

// Gaussian distribution
QUERY CalculateGaussian(x: F64, mean: F64, std_dev: F64) =>
    coefficient <- DIV(1.0, MUL(std_dev, SQRT(MUL(2.0, PI()))))
    exponent <- DIV(POW(SUB(x, mean), 2.0), MUL(2.0, POW(std_dev, 2.0)))
    probability <- MUL(coefficient, POW(E(), MUL(-1.0, exponent)))
    RETURN probability

// Euler's formula: e^(iθ) = cos(θ) + i*sin(θ)
QUERY EulerFormula(theta: F64) =>
    result <- N::ComplexNumber
        ::{
            real: COS(theta),
            imaginary: SIN(theta),
            magnitude: POW(E(), 0.0)  // Always 1 for pure imaginary exponent
        }
    RETURN result

Aggregate Functions

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Aggregate Functions

HelixQL provides a comprehensive set of aggregate functions for performing statistical operations and summarizing collections of values. These functions operate on arrays or collections to produce single summary values.

Available Functions

MIN - Minimum Value

MIN(collection)  // Returns the smallest value

Returns the minimum value from a collection of numbers.

MAX - Maximum Value

MAX(collection)  // Returns the largest value

Returns the maximum value from a collection of numbers.

SUM - Sum of Values

SUM(collection)  // Returns the total sum

Returns the sum of all values in a collection.

AVG - Average Value

AVG(collection)  // Returns the mean

Returns the arithmetic mean (average) of all values in a collection.

COUNT - Count Elements

COUNT(collection)  // Returns the number of elements

Returns the number of elements in a collection.

⚠️ Warning

When using the SDKs or curling the endpoint, the query name must match what is defined in the queries.hx file exactly.

Example 1: Statistical analysis of sales data

Analyze sales performance using aggregate functions:

QUERY AnalyzeSalesStatistics() =>
    stats <- {
        total_sales: SUM(N::Sale::{amount}),
        average_sale: AVG(N::Sale::{amount}),
        max_sale: MAX(N::Sale::{amount}),
        min_sale: MIN(N::Sale::{amount}),
        sale_count: COUNT(N::Sale::{amount})
    }
    RETURN stats

QUERY CreateSale(amount: F64, product: String) =>
    sale <- AddN<Sale>({ amount: amount, product: product })
    RETURN sale
N::Sale {
    amount: F64,
    product: String
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create sales records
sales = [
    {"amount": 150.0, "product": "Laptop"},
    {"amount": 75.0, "product": "Mouse"},
    {"amount": 300.0, "product": "Monitor"},
    {"amount": 50.0, "product": "Keyboard"},
    {"amount": 200.0, "product": "Headphones"},
    {"amount": 125.0, "product": "Webcam"},
]

for sale in sales:
    client.query("CreateSale", sale)

result = client.query("AnalyzeSalesStatistics", {})
print("Sales statistics:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let sales = vec![
        (150.0, "Laptop"),
        (75.0, "Mouse"),
        (300.0, "Monitor"),
        (50.0, "Keyboard"),
        (200.0, "Headphones"),
        (125.0, "Webcam"),
    ];

    for (amount, product) in &sales {
        let _inserted: serde_json::Value = client.query("CreateSale", &json!({
            "amount": amount,
            "product": product,
        })).await?;
    }

    let result: serde_json::Value = client.query("AnalyzeSalesStatistics", &json!({})).await?;

    println!("Sales statistics: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    sales := []map[string]any{
        {"amount": 150.0, "product": "Laptop"},
        {"amount": 75.0, "product": "Mouse"},
        {"amount": 300.0, "product": "Monitor"},
        {"amount": 50.0, "product": "Keyboard"},
        {"amount": 200.0, "product": "Headphones"},
        {"amount": 125.0, "product": "Webcam"},
    }

    for _, sale := range sales {
        var inserted map[string]any
        if err := client.Query("CreateSale", helix.WithData(sale)).Scan(&inserted); err != nil {
            log.Fatalf("CreateSale failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("AnalyzeSalesStatistics", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("AnalyzeSalesStatistics failed: %s", err)
    }

    fmt.Printf("Sales statistics: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const sales = [
        { amount: 150.0, product: "Laptop" },
        { amount: 75.0, product: "Mouse" },
        { amount: 300.0, product: "Monitor" },
        { amount: 50.0, product: "Keyboard" },
        { amount: 200.0, product: "Headphones" },
        { amount: 125.0, product: "Webcam" },
    ];

    for (const sale of sales) {
        await client.query("CreateSale", sale);
    }

    const result = await client.query("AnalyzeSalesStatistics", {});

    console.log("Sales statistics:", result);
}

main().catch((err) => {
    console.error("AnalyzeSalesStatistics query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateSale \
  -H 'Content-Type: application/json' \
  -d '{"amount":150.0,"product":"Laptop"}'

curl -X POST \
  http://localhost:6969/CreateSale \
  -H 'Content-Type: application/json' \
  -d '{"amount":75.0,"product":"Mouse"}'

curl -X POST \
  http://localhost:6969/CreateSale \
  -H 'Content-Type: application/json' \
  -d '{"amount":300.0,"product":"Monitor"}'

curl -X POST \
  http://localhost:6969/CreateSale \
  -H 'Content-Type: application/json' \
  -d '{"amount":50.0,"product":"Keyboard"}'

curl -X POST \
  http://localhost:6969/CreateSale \
  -H 'Content-Type: application/json' \
  -d '{"amount":200.0,"product":"Headphones"}'

curl -X POST \
  http://localhost:6969/CreateSale \
  -H 'Content-Type: application/json' \
  -d '{"amount":125.0,"product":"Webcam"}'

curl -X POST \
  http://localhost:6969/AnalyzeSalesStatistics \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 2: Student grade analysis

Calculate grade statistics for students:

QUERY AnalyzeStudentGrades() =>
    students <- N::Student
        ::{
            name,
            grades,
            highest_grade: MAX(_::{grades}),
            lowest_grade: MIN(_::{grades}),
            average_grade: AVG(_::{grades}),
            total_assessments: COUNT(_::{grades})
        }
    RETURN students

QUERY CreateStudent(name: String, grades: [F64]) =>
    student <- AddN<Student>({ name: name, grades: grades })
    RETURN student
N::Student {
    name: String,
    grades: [F64]
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create student records with their grades
students = [
    {"name": "Alice", "grades": [85.0, 92.0, 88.0, 95.0, 90.0]},
    {"name": "Bob", "grades": [78.0, 82.0, 75.0, 88.0, 80.0]},
    {"name": "Charlie", "grades": [95.0, 98.0, 92.0, 96.0, 94.0]},
    {"name": "Diana", "grades": [70.0, 75.0, 72.0, 78.0, 74.0]},
]

for student in students:
    client.query("CreateStudent", student)

result = client.query("AnalyzeStudentGrades", {})
print("Student grade analysis:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let students = vec![
        ("Alice", vec![85.0, 92.0, 88.0, 95.0, 90.0]),
        ("Bob", vec![78.0, 82.0, 75.0, 88.0, 80.0]),
        ("Charlie", vec![95.0, 98.0, 92.0, 96.0, 94.0]),
        ("Diana", vec![70.0, 75.0, 72.0, 78.0, 74.0]),
    ];

    for (name, grades) in &students {
        let _inserted: serde_json::Value = client.query("CreateStudent", &json!({
            "name": name,
            "grades": grades,
        })).await?;
    }

    let result: serde_json::Value = client.query("AnalyzeStudentGrades", &json!({})).await?;

    println!("Student grade analysis: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    students := []map[string]any{
        {"name": "Alice", "grades": []float64{85.0, 92.0, 88.0, 95.0, 90.0}},
        {"name": "Bob", "grades": []float64{78.0, 82.0, 75.0, 88.0, 80.0}},
        {"name": "Charlie", "grades": []float64{95.0, 98.0, 92.0, 96.0, 94.0}},
        {"name": "Diana", "grades": []float64{70.0, 75.0, 72.0, 78.0, 74.0}},
    }

    for _, student := range students {
        var inserted map[string]any
        if err := client.Query("CreateStudent", helix.WithData(student)).Scan(&inserted); err != nil {
            log.Fatalf("CreateStudent failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("AnalyzeStudentGrades", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("AnalyzeStudentGrades failed: %s", err)
    }

    fmt.Printf("Student grade analysis: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const students = [
        { name: "Alice", grades: [85.0, 92.0, 88.0, 95.0, 90.0] },
        { name: "Bob", grades: [78.0, 82.0, 75.0, 88.0, 80.0] },
        { name: "Charlie", grades: [95.0, 98.0, 92.0, 96.0, 94.0] },
        { name: "Diana", grades: [70.0, 75.0, 72.0, 78.0, 74.0] },
    ];

    for (const student of students) {
        await client.query("CreateStudent", student);
    }

    const result = await client.query("AnalyzeStudentGrades", {});

    console.log("Student grade analysis:", result);
}

main().catch((err) => {
    console.error("AnalyzeStudentGrades query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateStudent \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","grades":[85.0,92.0,88.0,95.0,90.0]}'

curl -X POST \
  http://localhost:6969/CreateStudent \
  -H 'Content-Type: application/json' \
  -d '{"name":"Bob","grades":[78.0,82.0,75.0,88.0,80.0]}'

curl -X POST \
  http://localhost:6969/CreateStudent \
  -H 'Content-Type: application/json' \
  -d '{"name":"Charlie","grades":[95.0,98.0,92.0,96.0,94.0]}'

curl -X POST \
  http://localhost:6969/CreateStudent \
  -H 'Content-Type: application/json' \
  -d '{"name":"Diana","grades":[70.0,75.0,72.0,78.0,74.0]}'

curl -X POST \
  http://localhost:6969/AnalyzeStudentGrades \
  -H 'Content-Type: application/json' \
  -d '{}'

Example 3: Sensor data monitoring

Monitor and summarize IoT sensor readings:

QUERY MonitorSensorReadings() =>
    sensors <- N::Sensor
        ::{
            sensor_id,
            readings,
            max_reading: MAX(_::{readings}),
            min_reading: MIN(_::{readings}),
            avg_reading: AVG(_::{readings}),
            total_readings: COUNT(_::{readings}),
            reading_sum: SUM(_::{readings})
        }
    RETURN sensors

QUERY CreateSensor(sensor_id: String, readings: [F64]) =>
    sensor <- AddN<Sensor>({ sensor_id: sensor_id, readings: readings })
    RETURN sensor
N::Sensor {
    sensor_id: String,
    readings: [F64]
}

Here’s how to run the query using the SDKs or curl

from helix.client import Client

client = Client(local=True, port=6969)

# Create sensor records with readings
sensors = [
    {"sensor_id": "TEMP-001", "readings": [22.5, 23.1, 22.8, 23.5, 22.9]},
    {"sensor_id": "TEMP-002", "readings": [25.0, 25.5, 24.8, 26.0, 25.2]},
    {"sensor_id": "HUMID-001", "readings": [45.0, 47.0, 46.5, 48.0, 46.0]},
    {"sensor_id": "PRESS-001", "readings": [1013.0, 1012.5, 1014.0, 1013.5, 1013.0]},
]

for sensor in sensors:
    client.query("CreateSensor", sensor)

result = client.query("MonitorSensorReadings", {})
print("Sensor monitoring:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    let sensors = vec![
        ("TEMP-001", vec![22.5, 23.1, 22.8, 23.5, 22.9]),
        ("TEMP-002", vec![25.0, 25.5, 24.8, 26.0, 25.2]),
        ("HUMID-001", vec![45.0, 47.0, 46.5, 48.0, 46.0]),
        ("PRESS-001", vec![1013.0, 1012.5, 1014.0, 1013.5, 1013.0]),
    ];

    for (sensor_id, readings) in &sensors {
        let _inserted: serde_json::Value = client.query("CreateSensor", &json!({
            "sensor_id": sensor_id,
            "readings": readings,
        })).await?;
    }

    let result: serde_json::Value = client.query("MonitorSensorReadings", &json!({})).await?;

    println!("Sensor monitoring: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    sensors := []map[string]any{
        {"sensor_id": "TEMP-001", "readings": []float64{22.5, 23.1, 22.8, 23.5, 22.9}},
        {"sensor_id": "TEMP-002", "readings": []float64{25.0, 25.5, 24.8, 26.0, 25.2}},
        {"sensor_id": "HUMID-001", "readings": []float64{45.0, 47.0, 46.5, 48.0, 46.0}},
        {"sensor_id": "PRESS-001", "readings": []float64{1013.0, 1012.5, 1014.0, 1013.5, 1013.0}},
    }

    for _, sensor := range sensors {
        var inserted map[string]any
        if err := client.Query("CreateSensor", helix.WithData(sensor)).Scan(&inserted); err != nil {
            log.Fatalf("CreateSensor failed: %s", err)
        }
    }

    var result map[string]any
    if err := client.Query("MonitorSensorReadings", helix.WithData(map[string]any{})).Scan(&result); err != nil {
        log.Fatalf("MonitorSensorReadings failed: %s", err)
    }

    fmt.Printf("Sensor monitoring: %#v\n", result)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    const sensors = [
        { sensor_id: "TEMP-001", readings: [22.5, 23.1, 22.8, 23.5, 22.9] },
        { sensor_id: "TEMP-002", readings: [25.0, 25.5, 24.8, 26.0, 25.2] },
        { sensor_id: "HUMID-001", readings: [45.0, 47.0, 46.5, 48.0, 46.0] },
        { sensor_id: "PRESS-001", readings: [1013.0, 1012.5, 1014.0, 1013.5, 1013.0] },
    ];

    for (const sensor of sensors) {
        await client.query("CreateSensor", sensor);
    }

    const result = await client.query("MonitorSensorReadings", {});

    console.log("Sensor monitoring:", result);
}

main().catch((err) => {
    console.error("MonitorSensorReadings query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateSensor \
  -H 'Content-Type: application/json' \
  -d '{"sensor_id":"TEMP-001","readings":[22.5,23.1,22.8,23.5,22.9]}'

curl -X POST \
  http://localhost:6969/CreateSensor \
  -H 'Content-Type: application/json' \
  -d '{"sensor_id":"TEMP-002","readings":[25.0,25.5,24.8,26.0,25.2]}'

curl -X POST \
  http://localhost:6969/CreateSensor \
  -H 'Content-Type: application/json' \
  -d '{"sensor_id":"HUMID-001","readings":[45.0,47.0,46.5,48.0,46.0]}'

curl -X POST \
  http://localhost:6969/CreateSensor \
  -H 'Content-Type: application/json' \
  -d '{"sensor_id":"PRESS-001","readings":[1013.0,1012.5,1014.0,1013.5,1013.0]}'

curl -X POST \
  http://localhost:6969/MonitorSensorReadings \
  -H 'Content-Type: application/json' \
  -d '{}'

Common Aggregate Patterns

Combining Aggregates

Aggregate functions are often used together for comprehensive analysis:

QUERY ComprehensiveStats() =>
    stats <- {
        count: COUNT(N::DataPoint::{value}),
        sum: SUM(N::DataPoint::{value}),
        avg: AVG(N::DataPoint::{value}),
        min: MIN(N::DataPoint::{value}),
        max: MAX(N::DataPoint::{value}),
        range: SUB(MAX(N::DataPoint::{value}), MIN(N::DataPoint::{value}))
    }
    RETURN stats

Filtered Aggregates

Combine aggregates with filtering for conditional statistics:

QUERY FilteredStats(threshold: F64) =>
    filtered <- N::DataPoint WHERE _::{value} > threshold
    stats <- {
        count: COUNT(filtered::{value}),
        average: AVG(filtered::{value}),
        maximum: MAX(filtered::{value})
    }
    RETURN stats

Nested Aggregates

Calculate aggregates on aggregate results:

QUERY NestedAggregates() =>
    groups <- N::Group
        ::{
            name,
            member_count: COUNT(_::{members}),
            avg_age: AVG(_::{members}::age),
            max_score: MAX(_::{members}::score)
        }
    overall_stats <- {
        total_groups: COUNT(groups),
        avg_group_size: AVG(groups::{member_count})
    }
    RETURN overall_stats

💡 Tip

Aggregate functions are particularly useful for data analysis, reporting, and creating dashboards.

Empty Collections

ℹ️ Note

When aggregate functions are applied to empty collections:

  • COUNT returns 0
  • SUM returns 0
  • AVG, MIN, MAX return null or error depending on implementation

Statistical Variance

To calculate variance and standard deviation, combine aggregate functions:

// Calculate variance
QUERY CalculateVariance() =>
    values <- N::Value::{amount}
    mean <- AVG(values)
    squared_diffs <- values
        ::{
            diff_squared: POW(SUB(_::{amount}, mean), 2.0)
        }
    variance <- AVG(squared_diffs::{diff_squared})
    std_dev <- SQRT(variance)
    RETURN { variance: variance, std_dev: std_dev }

Performance Considerations

ℹ️ Note

Aggregate functions scan entire collections, so consider:

  • Adding appropriate indexes for frequently aggregated fields
  • Using filters before aggregation to reduce data volume
  • Caching aggregate results for frequently accessed statistics

Output Values

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

HelixQL queries end with a RETURN clause. You can return bindings (variables), projected properties, aggregations, literals, or choose to return nothing at all.

Quick reference

Return TypeSyntaxReturn TypeSyntax
Return a bindingRETURN usersExclude fieldsRETURN users::!{ email }
Return multipleRETURN user, postsAggregation/scalarRETURN count
Project fieldsRETURN users::{ name, age }LiteralRETURN "ok"
Only IDsRETURN users::IDNo payloadRETURN NONE

⚠️ Warning

When using the Python SDK, the output values are wrapped in an array for multiple query calls, so you will need to access the first element of the array to get the result of the first call.

Returning bindings

Return any previously bound value from your traversal.

QUERY GetAllUsers() =>
    users <- N<User>
    RETURN users
N::User {
    name: String,
    age: U8,
    email: String
}
{
  "users": [
    { "name": "Alice", "age": 25, "email": "alice@example.com" },
    { "name": "Bob", "age": 30, "email": "bob@example.com" },
    { "name": "Charlie", "age": 28, "email": "charlie@example.com" },
  ]
}

Returning multiple values creates multiple top-level fields in the response, named after the variables.

QUERY GetUserAndPosts(user_id: ID) =>
    user <- N<User>(user_id)
    posts <- user::Out<User_to_Post>
    RETURN user, posts
N::User {
    name: String,
    age: U8,
    email: String
}

N::Post {
    title: String,
    content: String
}

E::User_to_Post {
    From: User,
    To: Post
}
{
  "user": {"name": "Alice", "age": 25, "email": "alice@example.com"},
  "posts": [ 
    {"title": "My First Post", "content": "This is my first blog post!"}, 
    ...,
  ]
}

Returning projections and properties

Use property projection to shape the returned data.

QUERY FindUsers() =>
    users <- N<User>::RANGE(0, 10)
    RETURN users::{ name, age }
N::User {
    name: String,
    age: U8,
    email: String
}
{
  "users": [
    { "name": "Alice", "age": 25 },
    { "name": "Bob", "age": 30 },
    { "name": "Charlie", "age": 28 }
  ]
}

Return just the ID of each element:

QUERY FindUserIDs() =>
    users <- N<User>::RANGE(0, 10)
    RETURN users::ID
N::User {
    name: String,
    age: U8,
    email: String
}
{
  "users": [
    "c2ca233f-0cd8-4fae-8136-b40593792071",
    "d3db344g-1de9-5gbf-9247-c51604803082",
    "e4ec455h-2ef0-6hcg-0358-d62715914193"
  ]
}

Exclude specific properties:

QUERY FindUsersNoPII() =>
    users <- N<User>::RANGE(0, 10)
    RETURN users::!{ email, location }
N::User {
    name: String,
    age: U8,
    email: String,
    location: String
}
{
  "users": [
    { "name": "Alice", "age": 25 },
    { "name": "Bob", "age": 30 },
    { "name": "Charlie", "age": 28 }
  ]
}

You can also create nested or remapped shapes in RETURN using nested mappings:

QUERY FindFriends(user_id: ID) =>
    user <- N<User>(user_id)
    posts <- user::Out<User_to_Post>::RANGE(0, 20)
    RETURN user::|u|{
        userID: u::ID,
        posts: posts::{
            postID: ID,
            creatorID: u::ID,
            ..
        }
    }
N::User {
    name: String,
    age: U8,
    email: String
}

N::Post {
    title: String,
    content: String
}

E::User_to_Post {
    From: User,
    To: Post
}
{
  "user": {
    "userID": "c2ca233f-0cd8-4fae-8136-b40593792071",
    "posts": [
      {
        "postID": "d3db344g-1de9-5gbf-9247-c51604803082",
        "creatorID": "c2ca233f-0cd8-4fae-8136-b40593792071",
        "title": "My First Post",
        "content": "This is my first blog post!"
      },
      {
        "postID": "e4ec455h-2ef0-6hcg-0358-d62715914193",
        "creatorID": "c2ca233f-0cd8-4fae-8136-b40593792071",
        "title": "Weekend Plans",
        "content": "Planning to explore the city."
      }
    ]
  }
}

See property access, remappings, and exclusion for more details.


Returning scalars and literals

Aggregations and scalar bindings can be returned directly:

QUERY CountUsers() =>
    user_count <- N<User>::COUNT
    RETURN user_count
N::User {
    name: String,
    age: U8,
    email: String
}
{
  "user_count": 42
}

You can also return literals (strings, numbers, booleans) when useful:

QUERY DeleteCity(city_id: ID) =>
    DROP N<City>(city_id)
    RETURN "success"
N::City {
    name: String,
    population: U32
}
{
  "result": "success"
}

Returning nothing

For mutations or maintenance operations where you do not want a response payload, use RETURN NONE.

QUERY DeleteCityQuietly(city_id: ID) =>
    DROP N<City>(city_id)
    RETURN NONE
N::City {
    name: String,
    population: U32
}
{}

RETURN NONE signals that the query intentionally produces no output values. This is handy to avoid sending placeholder strings like “success” when a silent acknowledgement is preferred.

MCP Macro

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Usage

The #[mcp] macro enables you to expose any HelixQL query as an MCP (Model Context Protocol) endpoint, making it directly accessible to AI agents and LLM applications.

#[mcp]
QUERY QueryName(param1: Type, param2: Type) =>
    result <- traversal_expression
    RETURN result

🚨 Danger

You MUST only return a single object/value from the query otherwise you will get a compile error. (See E401)

⚠️ Warning

Make sure to set mcp = true under the instance you are using in the helix.toml file.

How it works

When you add the #[mcp] attribute to a query:

  1. HelixDB automatically registers the query as an MCP tool
  2. The query parameters become the tool’s input schema
  3. The query’s return type becomes the tool’s output schema
  4. AI agents can call this tool directly through the MCP server

ℹ️ Note

The MCP macro automatically converts your HelixQL query into a callable MCP tool that can be used by LLM providers like OpenAI, Anthropic, and Gemini.


Using MCP Queries with LLM Providers

Once you’ve defined your MCP queries, you can use them with any LLM provider that supports MCP:

#[mcp]
QUERY get_user(user_id: ID) =>
    user <- N<User>(user_id)
    RETURN user
N::User {
    name: String,
    age: U32,
    email: String
}
from helix.client import Client
from helix.mcp import MCPServer
from helix.providers.openai_client import OpenAIProvider
from fastmcp.tools.tool import FunctionTool

# Initialize MCP server
client = Client(local=True)
mcp = MCPServer("helix-mcp", client)

# Add your custom tool to the MCP server
def get_user(connection_id: str, user_id: str):
    """
    Get the name of a user by their ID
    Args: connection_id: The connection ID, user_id: The ID of the user
    Returns: The user object
    """
    return client.query(
        "mcp/get_userMcp", 
        {"connection_id": connection_id, "data":{"user_id": user_id}}
    )

mcp.add_tool(FunctionTool.from_function(get_user))

# Start MCP server
mcp.run_bg()

# Enable MCP in your LLM provider
llm = OpenAIProvider(
    name="openai-llm",
    instructions="You are a helpful assistant with access to user data.",
    model="gpt-4o",
    history=True
)
llm.enable_mcps("helix-mcp")

# The AI can now call your MCP queries
response = llm.generate(f"What is the name of user with ID {user_id}?")
print(response)
import HelixDB from "helix-ts";

const client = new HelixDB("http://localhost:6969");

// Add your custom MCP tool via /mcp to your MCP server
// AI agents connected via MCP can call your queries

// Example MCP tool call:
const connection_id = await client.query("mcp/init", {});

const user = await client.query("mcp/get_userMcp", {
    connection_id,
    data: {
        user_id: user_id
    }
});
# Initialize the connection, this will return a connection_id
curl -X POST \
  http://localhost:6969/mcp/init \    
  -H 'Content-Type: application/json' \
  -d '{}'

# Use the connection_id from above to call the MCP endpoint
curl -X POST \
  http://localhost:6969/mcp/get_userMcp \
  -H 'Content-Type: application/json' \
  -d '{
    "connection_id": "connection_id",
    "data": {
        "user_id": "user_id"
    }
  }'

Best Practices

Use descriptive query names

Choose query names that clearly describe what the tool does. AI agents rely on function names to understand capabilities.

// Good
#[mcp]
QUERY get_user_purchase_history(user_id: ID) => ...

// Less clear
#[mcp]
QUERY query1(id: ID) => ...
Keep query signatures simple

Use clear parameter types and avoid overly complex signatures. AI agents work best with straightforward interfaces.

// Good
#[mcp]
QUERY search_products(name: String, max_price: F64) => ...

// More complex - consider breaking into multiple queries
#[mcp]
QUERY complex_search(filters: [FilterType], options: SearchOptions) => ...

  • Query Basics — Learn the fundamentals of writing HelixQL queries

Model Macro

⚠️ Warning

HelixQL is deprecated in HelixDB v2. Queries are now written with the Rust DSL and dispatched as JSON — see the Querying guide. This section is kept as a reference for legacy HelixQL projects.

For the complete documentation index optimized for AI agents, see llms.txt.

Usage

The #[model] macro specifies which embedding model to use for Embed() calls within a query. This allows you to optimize embeddings for different use cases—such as storing documents versus searching them.

#[model("provider:model-name:task-type")]
QUERY QueryName(param: Type) =>
    result <- AddV<Type>(Embed(param), {properties})
    RETURN result

ℹ️ Note

The #[model] macro overrides the default embedding model configured in your helix.toml file for that specific query.


Syntax Reference

The model string follows the format "provider:model-name:task-type":

  • provider The embedding service provider
  • model-name The specific model identifier
  • task-type Task optimization hint

Supported models

ProviderModelTask TypeNotes
geminigemini-embedding-001RETRIEVAL_DOCUMENT OR RETRIEVAL_QUERYRETRIEVAL_DOCUMENT is the default if no task type is included
openaitext-embedding-ada-002Not SupportedNot recommended
openaitext-embedding-smallNot Supported
openaitext-embedding-largeNot Supported

Many embedding providers offer task-specific optimizations. For example, Gemini’s embedding models support task types that hint whether the text being embedded is a document (to be stored and searched) or a query (used to search documents).

  • RETRIEVAL_DOCUMENT - Optimized for embedding documents that will be stored and retrieved later
  • RETRIEVAL_QUERY - Optimized for embedding search queries that find relevant documents

Using the appropriate task type can improve search relevance and retrieval quality.

⚠️ Warning

All vectors in a vector type must have the same dimensions. When using different task types, ensure both use the same model (e.g., gemini-embedding-001) so the dimensions match.


This example shows how to use different task types for storing clinical notes versus searching them.

#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY addClinicalNote (text: String) =>
    note <- AddV<ClinicalNote>(Embed(text), {text: text})
    RETURN note

#[model("gemini:gemini-embedding-001:RETRIEVAL_QUERY")]
QUERY searchClinicalNotes (query: String, k: I64) =>
    notes <- SearchV<ClinicalNote>(Embed(query), k)
    RETURN notes
V::ClinicalNote {
    vector: [F64],
    text: String
}
GEMINI_API_KEY=your_api_key

Here’s how to run the queries using the SDKs or curl:

from helix.client import Client

client = Client(local=True, port=6969)

# Store clinical notes with RETRIEVAL_DOCUMENT optimization
notes = [
    "Patient presents with acute lower back pain radiating to left leg.",
    "Follow-up visit: Blood pressure 120/80, heart rate normal.",
    "MRI results show mild disc herniation at L4-L5 level."
]

for note in notes:
    client.query("addClinicalNote", {"text": note})

# Search with RETRIEVAL_QUERY optimization
results = client.query("searchClinicalNotes", {
    "query": "back pain symptoms",
    "k": 5
})

print(results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = HelixDB::new(Some("http://localhost"), Some(6969), None);

    // Store clinical notes with RETRIEVAL_DOCUMENT optimization
    let notes = vec![
        "Patient presents with acute lower back pain radiating to left leg.",
        "Follow-up visit: Blood pressure 120/80, heart rate normal.",
        "MRI results show mild disc herniation at L4-L5 level."
    ];

    for note in &notes {
        let _inserted: serde_json::Value = client.query("addClinicalNote", &json!({
            "text": note
        })).await?;
    }

    // Search with RETRIEVAL_QUERY optimization
    let results: serde_json::Value = client.query("searchClinicalNotes", &json!({
        "query": "back pain symptoms",
        "k": 5
    })).await?;

    println!("Search results: {results:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

    "github.com/HelixDB/helix-go"
)

func main() {
    client := helix.NewClient("http://localhost:6969")

    // Store clinical notes with RETRIEVAL_DOCUMENT optimization
    notes := []string{
        "Patient presents with acute lower back pain radiating to left leg.",
        "Follow-up visit: Blood pressure 120/80, heart rate normal.",
        "MRI results show mild disc herniation at L4-L5 level.",
    }

    for _, note := range notes {
        payload := map[string]any{"text": note}

        var inserted map[string]any
        if err := client.Query("addClinicalNote", helix.WithData(payload)).Scan(&inserted); err != nil {
            log.Fatalf("addClinicalNote failed: %s", err)
        }
    }

    // Search with RETRIEVAL_QUERY optimization
    searchPayload := map[string]any{
        "query": "back pain symptoms",
        "k":     int64(5),
    }

    var results map[string]any
    if err := client.Query("searchClinicalNotes", helix.WithData(searchPayload)).Scan(&results); err != nil {
        log.Fatalf("searchClinicalNotes failed: %s", err)
    }

    fmt.Printf("Search results: %#v\n", results)
}
import HelixDB from "helix-ts";

async function main() {
    const client = new HelixDB("http://localhost:6969");

    // Store clinical notes with RETRIEVAL_DOCUMENT optimization
    const notes = [
        "Patient presents with acute lower back pain radiating to left leg.",
        "Follow-up visit: Blood pressure 120/80, heart rate normal.",
        "MRI results show mild disc herniation at L4-L5 level."
    ];

    for (const note of notes) {
        await client.query("addClinicalNote", { text: note });
    }

    // Search with RETRIEVAL_QUERY optimization
    const results = await client.query("searchClinicalNotes", {
        query: "back pain symptoms",
        k: 5
    });

    console.log("Search results:", results);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Store clinical notes
curl -X POST \
  http://localhost:6969/addClinicalNote \
  -H 'Content-Type: application/json' \
  -d '{"text":"Patient presents with acute lower back pain radiating to left leg."}'

curl -X POST \
  http://localhost:6969/addClinicalNote \
  -H 'Content-Type: application/json' \
  -d '{"text":"Follow-up visit: Blood pressure 120/80, heart rate normal."}'

curl -X POST \
  http://localhost:6969/addClinicalNote \
  -H 'Content-Type: application/json' \
  -d '{"text":"MRI results show mild disc herniation at L4-L5 level."}'

# Search clinical notes
curl -X POST \
  http://localhost:6969/searchClinicalNotes \
  -H 'Content-Type: application/json' \
  -d '{"query":"back pain symptoms","k":5}'

⚠️ Warning

Make sure to set your GEMINI_API_KEY environment variable (or OPENAI_API_KEY for OpenAI models) in the same location as your queries.hx, schema.hx, and config.hx.json files.


Best Practices

Use consistent models for the same vector type

Always use the same embedding model for all queries that operate on the same vector type. Different models produce vectors with different dimensions, which will cause errors.

// Good - same model, different task types
#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY storeDoc (text: String) => ...

#[model("gemini:gemini-embedding-001:RETRIEVAL_QUERY")]
QUERY searchDocs (query: String, k: I64) => ...

// Bad - different models for same vector type
#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY storeDoc (text: String) => ...

#[model("openai:text-embedding-3-small")]
QUERY searchDocs (query: String, k: I64) => ...
Match task types to operations

Use RETRIEVAL_DOCUMENT for operations that store data and RETRIEVAL_QUERY for operations that search data.

// Storing documents
#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY addDocument (content: String) =>
    doc <- AddV<Document>(Embed(content), {content: content})
    RETURN doc

// Searching documents
#[model("gemini:gemini-embedding-001:RETRIEVAL_QUERY")]
QUERY findDocuments (query: String, limit: I64) =>
    docs <- SearchV<Document>(Embed(query), limit)
    RETURN docs
Consider using the default model for simple cases

If you don’t need task-specific optimization, you can skip the #[model] macro and use the default model from your config.hx.json.

// Uses default model from config.hx.json
QUERY simpleSearch (text: String, k: I64) =>
    results <- SearchV<Document>(Embed(text), k)
    RETURN results

  • Embedding Vectors — Learn how to use the Embed function for automatic text embedding

  • Vector Search — Perform similarity search on vector data

  • MCP Macro — Expose queries as MCP endpoints for AI agents

  • AddV — Create new vector entries with embeddings

E101

For the complete documentation index optimized for AI agents, see llms.txt.

Unknown Node Type

Erroneous Code Example

This error occurs when you try to reference a node type that is not defined in your schema.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    RETURN user
// no types defined

Solution

Define the node type User in your schema.

N::User {
    // fields
}

E102

For the complete documentation index optimized for AI agents, see llms.txt.

Unknown Edge Type

Erroneous Code Example

This error occurs when you try to reference an edge type that is not defined in your schema.

QUERY getUsersPosts(id: ID) =>
    posts <- N<User>(id)::Out<Posted>
    RETURN posts
N::User {
    // fields
}

N::Post {
    // fields
}

Solution

Define the edge type Posted in your schema.

N::User {
    // fields
}

N::Post {
// fields
}

E::Posted {
    From: User,
    To: Post,
}

E103

For the complete documentation index optimized for AI agents, see llms.txt.

Unknown Vector Type

Erroneous Code Example

This error occurs when you try to reference a vector type that is not defined in your schema.

QUERY search(query: String) =>
    results <- SearchV<Document>(Embed(query), 10)
    RETURN results
// no types defined

Solution

Define the vector type Document in your schema.

V::Document {
    // fields
}

E104

For the complete documentation index optimized for AI agents, see llms.txt.

Cannot Access Properties on This Type

Erroneous Code Example

This error occurs when you try to access properties on a type that doesn’t support property access.

QUERY getValue(x: I32) =>
    result <- x::{value}
    RETURN result

Solution

Ensure you are accessing properties on a node, edge, or vector type that has defined fields, not on primitive types.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    result <- user::{name}
    RETURN result
N::User {
    name: String,
}

E105

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Identifier

This error occurs when you use an invalid identifier in your HelixQL query.

Erroneous Code Example

This error occurs when you use an invalid identifier in your HelixQL query. For example, Out is a reserved keyword so it cannot be used as a variable or a parameter name.

QUERY getUser(id: ID) =>
    Out <- N<User>(id)
    RETURN Out
QUERY getUser(Out: ID) =>
    user <- N<User>(Out)
    RETURN user

Solution

Rename the variable or parameter to a valid identifier.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    RETURN user
QUERY getUser(id: ID) =>
    user <- N<User>(id)
    RETURN user

E106

For the complete documentation index optimized for AI agents, see llms.txt.

Use of Undeclared Node or Vector Type in Schema

Erroneous Code Example

This error occurs when you reference a node or vector type in an edge definition before that type has been declared in the schema.

E::Follows {
    From: User,
    To: User,
}

// User is not defined yet

Solution

Declare the node or vector type in your schema before using it in an edge definition.

N::User {
    name: String,
}

E::Follows {
    From: User,
    To: User,
}

E107

For the complete documentation index optimized for AI agents, see llms.txt.

Duplicate Schema Definition

Erroneous Code Example

This error occurs when you define the same node, edge, or vector type more than once in your schema.

N::User {
    name: String,
}

N::User {
    email: String,
}

Solution

Remove the duplicate definition or rename one of the types.

N::User {
    name: String,
    email: String,
}

E108

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Schema Version

Erroneous Code Example

This error occurs when the schema version specified is invalid or not supported.

VERSION 999

N::User {
    name: String,
}

Solution

Use a valid schema version number that is supported by your version of Helix.

VERSION 1

N::User {
    name: String,
}

E109

For the complete documentation index optimized for AI agents, see llms.txt.

Duplicate Field Name in Schema

Erroneous Code Example

This error occurs when you define the same field name more than once within a single node, edge, or vector type.

N::User {
    name: String,
    age: U32,
    name: String,
}

Solution

Remove the duplicate field or rename one of them.

N::User {
    name: String,
    age: U32,
}

E110

For the complete documentation index optimized for AI agents, see llms.txt.

Schema Item Name is a Reserved Type Name

Erroneous Code Example

This error occurs when you try to use a reserved type name as the name for a node, edge, or vector type.

N::String {
    value: String,
}

Solution

Rename the type to something that is not a reserved type name.

N::TextValue {
    value: String,
}

E201

For the complete documentation index optimized for AI agents, see llms.txt.

Item Type Not in Schema

Erroneous Code Example

This error occurs when you reference an item type in a query that is not defined in your schema.

QUERY getProduct(id: ID) =>
    product <- N<Product>(id)
    RETURN product
N::User {
    name: String,
}
// Product is not defined

Solution

Define the item type in your schema before using it in queries.

N::User {
    name: String,
}

N::Product {
    name: String,
    price: F64,
}

E202

For the complete documentation index optimized for AI agents, see llms.txt.

Field Not in Schema

Erroneous Code Example

This error occurs when you reference a field that is not defined in your schema for a given item type.

For example, the field username is not defined in the schema for the User item type.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    RETURN user::{username}
N::User {
    age: U32,
}

Solution

Define the field username in the schema for the User item type.

N::User {
    username: String,
    age: U32,
}

E203

For the complete documentation index optimized for AI agents, see llms.txt.

Cannot Access Properties on the Type

Erroneous Code Example

This error occurs when you try to access properties on a type that doesn’t support property access, such as primitive types or certain intermediate query results.

QUERY getValue(count: I32) =>
    result <- count::{value}
    RETURN result

Solution

Ensure you are accessing properties on a node, edge, or vector type that has defined fields.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    result <- user::{name}
    RETURN result
N::User {
    name: String,
}

E204

For the complete documentation index optimized for AI agents, see llms.txt.

Reserved Field Name

Erroneous Code Example

This error occurs when you try to use a reserved field name in your schema or query.

For example, the field id is a reserved field name for any item type.

N::User {
    id: String,
}

Solution

Rename the field to a valid identifier.

N::User {
    github_id: String,
}

E205

For the complete documentation index optimized for AI agents, see llms.txt.

Type Mismatch

Erroneous Code Example

This error occurs when the type of a value you’re providing doesn’t match the expected field type defined in your schema.

For example, the field age is defined as an integer in the schema but you’re providing a string.

QUERY addUser(someField: U32) =>
    user <- AddN<User>({someField: someField})
    RETURN user
N::User {
    someField: String,
}

Solution

Solution 1

Either change the type of the field in the schema to U32.

QUERY addUser(someField: U32) =>
    user <- AddN<User>({someField: someField})
    RETURN user
N::User {
    someField: U32,
}

Solution 2

Or change the value you’re providing to a string.

QUERY addUser(someField: String) =>
    user <- AddN<User>({someField: someField})
    RETURN user
N::User {
    someField: String,
}

E206

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Value Type

Erroneous Code Example

This error occurs when you provide a value that is not a valid literal or identifier.

QUERY createUser() =>
    user <- N<User>::INSERT { name: }
    RETURN user
N::User {
    name: String,
}

Solution

Use a literal value or a valid identifier.

QUERY createUser(userName: String) =>
    user <- N<User>::INSERT { name: userName }
    RETURN user
N::User {
    name: String,
}

E207

For the complete documentation index optimized for AI agents, see llms.txt.

E207 - Invalid Edge Type for Item

Erroneous Code Example

This error occurs when you try to use an edge type that exists in the schema but is not valid for the specific item type you’re working with.

In this example, the Created edge exists. However, it goes from User to Post, not from Post to User. Therefore, it is not valid for an Out step from a Post node.

Query getUserFromPost(postId: ID) =>
    user <- N<Post>(postId)::Out<Created>
N::User {
    // Fields
}

N::Post {
    // Fields
}

E::Created {
    From: User,
    To: Post,
}

Solution

In this case, the solution would be to change the traversal to use the In step instead of the Out step.

Query getUserFromPost(postId: ID) =>
    user <- N<Post>(postId)::In<Created>

E208

For the complete documentation index optimized for AI agents, see llms.txt.

Field Has Not Been Indexed

Erroneous Code Example

This error occurs when you try to use a field in an operation that requires the field to be indexed, but the field has not been marked with INDEX in the schema.

QUERY findUserByEmail(email: String) =>
    users <- N<User>::WHERE(|u| u.email == email)
    RETURN users
N::User {
    name: String,
    email: String,
}

Solution

Add the INDEX modifier to the field in your schema.

N::User {
    name: String,
    email: String INDEX,
}

E209

For the complete documentation index optimized for AI agents, see llms.txt.

Unknown Type for Parameter

Erroneous Code Example

This error occurs when you specify an unknown type for a query parameter that is not a primitive type or a type defined in your schema.

QUERY getItem(data: UnknownType) =>
    RETURN data
N::User {
    name: String,
}

Solution

Use a primitive type (String, I32, U32, F64, Bool, ID, etc.) or declare the type in your schema.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    RETURN user
N::User {
    name: String,
}

E210

For the complete documentation index optimized for AI agents, see llms.txt.

Identifier Expected to be of Type ID

Erroneous Code Example

This error occurs when an identifier is expected to be of type ID but a different type was provided.

QUERY getUser(userId: String) =>
    user <- N<User>(userId)
    RETURN user
N::User {
    name: String,
}

Solution

Ensure the identifier is of type ID.

QUERY getUser(userId: ID) =>
    user <- N<User>(userId)
    RETURN user
N::User {
    name: String,
}

E301

For the complete documentation index optimized for AI agents, see llms.txt.

Variable Not in Scope

Erroneous Code Example

This error occurs when you try to reference a variable that is not currently in scope or has not been declared.

In this example, the variable userId is not declared.

Query getUser() =>
    user <- N<User>(userId)
    RETURN user

Solution

Declare the variable as a parameter before using it.

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    RETURN user

E302

For the complete documentation index optimized for AI agents, see llms.txt.

Variable Previously Declared

Erroneous Code Example

This error occurs when you try to declare a variable that has already been declared in the current scope. In this example, the variable user_name is declared twice.

Query getUser(userId: ID) =>
    userId <- N<User>(userId)::ID
    RETURN userId

Solution

Use a different variable name.

Query getUser(userId: ID) =>
    user_id <- N<User>(userId)::ID
    RETURN user_id

E303

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Primitive Value

Erroneous Code Example

This error occurs when you provide a primitive value that is not valid for the expected type.

QUERY createUser() =>
    user <- N<User>::INSERT { age: "not a number" }
    RETURN user
N::User {
    name: String,
    age: U32,
}

Solution

Provide a valid primitive value that matches the expected type.

QUERY createUser() =>
    user <- N<User>::INSERT { age: 25 }
    RETURN user
N::User {
    name: String,
    age: U32,
}

E304

For the complete documentation index optimized for AI agents, see llms.txt.

Missing Item Type

Erroneous Code Example

This error occurs when an item type is required but not provided in your query.

In this example, the User item type is required but not provided.

Query getUser(userId: ID) =>
    user <- N(userId)
    RETURN user
Query getUser(userId: ID) =>
    user <- N<User>(userId)::Out
    RETURN user

Solution

Specify the required item type within the < and > brackets after the traversal step in your query.

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    RETURN user
Query getUser(userId: ID) =>
    user <- N<User>(userId)::Out<Knows>
    RETURN user

E305

For the complete documentation index optimized for AI agents, see llms.txt.

Missing Parameter

Example

This error occurs when a required parameter is missing from a function call or operation.

In this example, the SearchV function is called without the k parameter which is required to limit the number of results.

Query searchUsers(vector: [F64]) =>
    users <- SearchV<Document>(vector)
    RETURN users

Solution

Provide all required parameters for functions and operations.

Query searchUsers(vector: [F64]) =>
    users <- SearchV<Document>(vector, 10)
    RETURN users

E306

For the complete documentation index optimized for AI agents, see llms.txt.

Expression is Not a Boolean

Erroneous Code Example

This error occurs when an expression is expected to result in a boolean value but produces a different type.

QUERY getUsers() =>
    users <- N<User>::WHERE(|u| u.name)
    RETURN users
N::User {
    name: String,
    active: Bool,
}

Solution

Ensure the expression evaluates to a boolean value.

QUERY getUsers() =>
    users <- N<User>::WHERE(|u| u.active == true)
    RETURN users
N::User {
    name: String,
    active: Bool,
}

E401

For the complete documentation index optimized for AI agents, see llms.txt.

MCP Single Value Requirement

Erroneous Code Example

This error occurs when an MCP (Model Context Protocol) query returns multiple values when only a single value is expected.

In this example, the MCP query returns multiple users, but MCP expects a single value. This is so the agent using the MCP tool can traverse from the return value of the query.

#[mcp]
Query getUsers(userId: ID, postId: ID) =>
    user <- N<User>(userId)
    post <- N<Post>(postId)
    RETURN user, post

Solution

Ensure MCP queries return exactly one value.

#[mcp]
Query getUsers(userId: ID, postId: ID) =>
    user <- N<User>(userId)
    post <- N<Post>(postId)
    RETURN user

E501

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Date

Example

This error occurs when you provide a date value that cannot be parsed or is in an invalid format.

In this example, the date value is invalid and cannot be parsed.

Query addEvent() =>
    AddN<Event>({date: "2023-99-99"})
N::Event {
    date: Date,
}

Solution

Ensure the date value is in the correct format.

Query addEvent() =>
    AddN<Event>({date: "2023-01-01"})

E601

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Traversal

Note: This error is not currently emitted by the compiler.

Erroneous Code Example

This error occurs when you attempt a malformed traversal.

Query getUser() =>
    user <- N() 

Solution

Ensure each step in the traversal is valid.

Query getUser() =>
    user <- N<User>
    RETURN user

E602

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Step

Erroneous Code Example

This error occurs when you try to use a step that is not valid in the current context.

In this example, the OutE<Knows> step results in edges, but the Out step can only be used on nodes.

Query getUser() =>
    user <- N<User>::OutE<Knows>::Out<Knows>
    RETURN user

Solution

Use valid step types in your traversals.

Query getUser() =>
    user <- N<User>::Out<Knows>::Out<Knows>
    RETURN user

E603

For the complete documentation index optimized for AI agents, see llms.txt.

SearchV Must Be Used on a Vector Type

Erroneous Code Example

This error occurs when you use SearchV on a type that is not a vector type.

QUERY findSimilar(embedding: [F32; 128]) =>
    results <- N<User>::SearchV(embedding, 10)
    RETURN results
N::User {
    name: String,
}

Solution

Use SearchV on a vector type, not a node or edge type.

QUERY findSimilar(embedding: [F32; 128]) =>
    results <- V<Document>::SearchV(embedding, 10)
    RETURN results
V<128>::Document {
    title: String,
    content: String,
}

E604

For the complete documentation index optimized for AI agents, see llms.txt.

Update Restriction

Erroneous Code Example

This error occurs when you try to perform an update operation on something other than nodes or edges.

In this example, the update step is used on a vector type which is not allowed yet.

Query updateVector(vectorId: ID) =>
    vector <- V<Vector>(vectorId)::UPDATE({field1: "updated value"})
V::Vector {
    field1: String,
}

E611

For the complete documentation index optimized for AI agents, see llms.txt.

Missing To ID

Erroneous Code Example

This error occurs when you try to create an edge without specifying the target node ID.

In this example, the AddE step is used without specifying the To parameter.

Query addEdge(from: ID, to: ID) =>
    AddE<Follows>::From(from)

Solution

Provide both ‘from’ and ‘to’ node IDs when creating edges.

Query addEdge(from: ID, to: ID) =>
    AddE<Follows>::From(from)::To(to)

E612

For the complete documentation index optimized for AI agents, see llms.txt.

Missing From ID

Erroneous Code Example

This error occurs when you try to create an edge without specifying the source node ID.

In this example, the AddE step is used without specifying the From parameter.

Query addEdge(to: ID) =>
    AddE<Follows>::To(to)

Solution

Provide both ‘from’ and ‘to’ node IDs when creating edges.

Query addEdge(from: ID, to: ID) =>
    AddE<Follows>::From(from)::To(to)

E621

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Boolean Comparison

Erroneous Code Example

This error occurs when you try to apply a boolean comparison operation to a type that doesn’t support it.

In this example, the && operator is used on a non-boolean type.

Query getUser(user1: ID, user2: ID) =>
    user1 <- N<User>(user1)
    user2 <- N<User>(user2)
    is_eq <- user1::EQ(user2)
    RETURN is_eq
N::User {
    name: String,
}

Solution

Ensure boolean operations are used on values that result in primitive types like booleans, numbers, or strings.

Query getUser(user1: ID, user2: ID) =>
    user1_name <- N<User>(user1)::{name}
    user2_name <- N<User>(user2)::{name}
    is_eq <- user1_name::EQ(user2_name)
    RETURN is_eq

E622

For the complete documentation index optimized for AI agents, see llms.txt.

Type Mismatch in Comparison

Erroneous Code Example

This error occurs when you compare a property with a value of a different type.

In this example, the age property is an integer, but the comparison is done with a string value.

Query getUser(user: ID) =>
    user <- N<User>(user)
    is_eq <- user::{name}::EQ(25)
    RETURN is_eq
N::User {
    name: String,
    age: I64,
}

Solution

Ensure compared values match the property type.

Query getUser(user: ID) =>
    user <- N<User>(user)
    is_eq <- user::{age}::EQ(25)
    RETURN is_eq

E623

For the complete documentation index optimized for AI agents, see llms.txt.

Edge Type Does Not Have a Node Type as Its From Source

Erroneous Code Example

This error occurs when you try to use an edge in a context that requires the From type to be a node, but the edge’s From type is not a node.

QUERY getConnections(id: ID) =>
    doc <- V<Document>(id)
    connections <- doc->Related
    RETURN connections
V<128>::Document {
    title: String,
}

E::Related {
    From: Document,
    To: Document,
}

Solution

Ensure the edge type has a node type as its From source, or use the appropriate traversal for vector types.

N::User {
    name: String,
}

E::Follows {
    From: User,
    To: User,
}
QUERY getFollowing(id: ID) =>
    user <- N<User>(id)
    following <- user->Follows
    RETURN following

E624

For the complete documentation index optimized for AI agents, see llms.txt.

Edge Type Does Not Have a Node Type as Its To Source

Erroneous Code Example

This error occurs when you try to use an edge in a context that requires the To type to be a node, but the edge’s To type is not a node.

QUERY getConnections(id: ID) =>
    user <- N<User>(id)
    connections <- user->LinksTo
    RETURN connections
N::User {
    name: String,
}

V<128>::Document {
    title: String,
}

E::LinksTo {
    From: User,
    To: Document,
}

Solution

Ensure the edge type has a node type as its To source, or use the appropriate traversal for vector types.

N::User {
    name: String,
}

N::Post {
    content: String,
}

E::Authored {
    From: User,
    To: Post,
}
QUERY getPosts(id: ID) =>
    user <- N<User>(id)
    posts <- user->Authored
    RETURN posts

E625

For the complete documentation index optimized for AI agents, see llms.txt.

Edge Type Does Not Have a Vector Type as Its From Source

Erroneous Code Example

This error occurs when you try to use an edge in a context that requires the From type to be a vector type, but the edge’s From type is not a vector.

QUERY getRelated(id: ID) =>
    user <- N<User>(id)
    related <- user->>SimilarTo
    RETURN related
N::User {
    name: String,
}

E::SimilarTo {
    From: User,
    To: User,
}

Solution

Set the From type of the edge to a vector type.

V<128>::Document {
    title: String,
}

E::SimilarTo {
    From: Document,
    To: Document,
}
QUERY getRelated(id: ID) =>
    doc <- V<Document>(id)
    related <- doc->>SimilarTo
    RETURN related

E626

For the complete documentation index optimized for AI agents, see llms.txt.

Edge Type Does Not Have a Vector Type as Its To Source

Erroneous Code Example

This error occurs when you try to use an edge in a context that requires the To type to be a vector type, but the edge’s To type is not a vector.

QUERY getRelated(id: ID) =>
    doc <- V<Document>(id)
    related <- doc->>References
    RETURN related
V<128>::Document {
    title: String,
}

N::Author {
    name: String,
}

E::References {
    From: Document,
    To: Author,
}

Solution

Set the To type of the edge to a vector type.

V<128>::Document {
    title: String,
}

V<128>::Citation {
    source: String,
}

E::References {
    From: Document,
    To: Citation,
}
QUERY getRelated(id: ID) =>
    doc <- V<Document>(id)
    citations <- doc->>References
    RETURN citations

E627

For the complete documentation index optimized for AI agents, see llms.txt.

Shortest Path Requires From or To Parameter

Erroneous Code Example

This error occurs when you use a shortest path operation without providing either a from or to parameter.

QUERY findPath() =>
    path <- N<User>::ShortestPath()
    RETURN path
N::User {
    name: String,
}

E::Follows {
    From: User,
    To: User,
}

Solution

Add a from or to parameter to the shortest path operation.

QUERY findPath(startId: ID, endId: ID) =>
    path <- N<User>::ShortestPath(from: startId, to: endId)
    RETURN path
N::User {
    name: String,
}

E::Follows {
    From: User,
    To: User,
}

E628

For the complete documentation index optimized for AI agents, see llms.txt.

DROP Can Only Be Applied to Traversals

Erroneous Code Example

This error occurs when you try to use DROP on an expression that is not a traversal.

QUERY deleteValue(value: String) =>
    DROP value

Solution

Ensure DROP is applied to a traversal that returns nodes, edges, or vectors.

QUERY deleteUser(id: ID) =>
    user <- N<User>(id)
    DROP user
N::User {
    name: String,
}

E631

For the complete documentation index optimized for AI agents, see llms.txt.

Incomplete Range

Erroneous Code Example

This error occurs when you define a range without both start and end values.

In this example, the range is incomplete because it is missing the end value.

Query getUser() =>
    subset_of_users <- N<User>::RANGE(0)
    RETURN subset_of_users

Solution

Provide both start and end values for ranges.

Query getUser() =>
    subset_of_users <- N<User>::RANGE(0, 100)
    RETURN subset_of_users

E632

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Range Order

Erroneous Code Example

This error occurs when the start value of a range is greater than or equal to the end value. This error is only emitted by the compiler when both start and end values are provided as literals. A runtime error will be emitted if the range is not valid at runtime.

In this example, the start value is greater than the end value.

Query getUser() =>
    subset_of_users <- N<User>::RANGE(100, 10)
    RETURN subset_of_users

Solution

Ensure range start value is less than end value.

Query getUser() =>
    subset_of_users <- N<User>::RANGE(10, 100)
    RETURN subset_of_users

E633

For the complete documentation index optimized for AI agents, see llms.txt.

Non-Integer Range Index

Erroneous Code Example

This error occurs when you use a non-integer value as an index in a range operation.

In this example, the range index is a float value.

Query getUser() =>
    subset_of_users <- N<User>::RANGE(1.5, 4.9)
    RETURN subset_of_users
Query getUser(end: F32) =>
    subset_of_users <- N<User>::RANGE(0, end)
    RETURN subset_of_users

Solution

Use integer values for range indices or set variable types to integer types.

Query getUser() =>
    subset_of_users <- N<User>::RANGE(1, 5)
    RETURN subset_of_users
Query getUser(end: I64) =>
    subset_of_users <- N<User>::RANGE(0, end)
    RETURN subset_of_users

E641

For the complete documentation index optimized for AI agents, see llms.txt.

Closure Position Restriction

Erroneous Code Example

This error occurs when you place a closure operation in a position other than the last step of a traversal.

In this example, the closure is not at the end of the traversal.

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    RETURN user::|u|{
                userID: u::ID
            }::Out<Knows>

Solution

Place closure operations only at the end of traversals.

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    out_users <- user::Out<Knows>
    RETURN out_users, user::|u|{
                            userID: u::ID
                        }::Out<Knows>

E642

For the complete documentation index optimized for AI agents, see llms.txt.

Object Position Restriction

Erroneous Code Example

This error occurs when you place an object operation in a position other than the last step of a traversal.

In this example, the object is not at the end of the traversal.

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    RETURN user::{
                userID: ID
            }::Out<Knows>

Solution

Place object operations only at the end of traversals.

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    out_users <- user::Out<Knows>
    RETURN out_users, user::{
                            userID: ID
                        }::Out<Knows>

E643

For the complete documentation index optimized for AI agents, see llms.txt.

Field Previously Excluded

Erroneous Code Example

This error occurs when you try to access or include a field that has been previously excluded in the traversal.

In this example, the email field is excluded in the traversal, but is being accessed.

Query getUser(userId: ID) =>
    user <- N<User>(userId)::!{email}
    RETURN user::{email}

Solution

Remove the exclusion or access the field after the exclusion.

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    RETURN user::{email}

E644

For the complete documentation index optimized for AI agents, see llms.txt.

Exclude Position Restriction

Erroneous Code Example

This error occurs when you place an exclude operation in a position other than the last step of a traversal or the step before an object remapping or closure remapping step.

In this example, the exclude step is used before the Out<Knows> step which is not allowed.

Query getUser(userId: ID) =>
    users <- N<User>(userId)::!{email}::Out<Knows>
    RETURN users

E645

For the complete documentation index optimized for AI agents, see llms.txt.

Empty Object Remapping

This error occurs when you define an object remapping with no fields.

Example

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    RETURN user::{}

Solution

Query getUser(userId: ID) =>
    user <- N<User>(userId)
    RETURN user::{name}

E646

For the complete documentation index optimized for AI agents, see llms.txt.

Field Value is Empty

Erroneous Code Example

This error occurs when you provide an empty value for a field in an object remapping or insert operation.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    RETURN user::{ name: }
N::User {
    name: String,
    email: String,
}

Solution

Provide a valid value for the field. The value must be a literal, identifier, traversal, or object.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    RETURN user::{ name: user.name }
N::User {
    name: String,
    email: String,
}

E651

For the complete documentation index optimized for AI agents, see llms.txt.

Non-Iterable Variable

Erroneous Code Example

This error occurs when you try to use a non-iterable variable in an iteration context.

In this example, the parameter name is not a collection, so it cannot be used in an iteration context.

Query addUser(name: String) =>
    FOR n IN name {
        AddN<User>({name: n})
    }
    RETURN "User added"

Solution

Ensure variables used in iterations are collections or arrays.

Query addUser(names: [String]) =>
    FOR n IN names {
        AddN<User>({name: n})
    }
    RETURN "Users added"

E652

For the complete documentation index optimized for AI agents, see llms.txt.

Invalid Field Access

Erroneous Code Example

This error occurs when you try to access a field that doesn’t exist on the inner type of an iterable variable.

Query addUser(userData: [{name: String, age: I64}]) =>
    FOR { name, age, nonexistent_field } IN userData {
        AddN<User>({name: name, age: age})
    }
    RETURN "Users added"

Solution

Ensure to only access fields that exist on the inner type.

Query addUser(userData: [{name: String, age: I64}]) =>
    FOR { name, age } IN userData {
        AddN<User>({name: name, age: age})
    }
    RETURN "Users added"

E653

For the complete documentation index optimized for AI agents, see llms.txt.

Non-Object Inner Type

This error occurs when the inner type of an iterable variable is not an object type, preventing field access or object destructuring.

Erroneous Code Example

In this example, the numbers variable contains primitives, not objects, so field access is not allowed.

Query addUsers(names: [String]) =>
    FOR {name} In names {
        AddN<User>({name: name})
    }
    RETURN "Users added"

Solution

Ensure iterable contains object types for field access.

Query addUsers(names: [String]) =>
    FOR name IN names {
        AddN<User>({name: name})
    }
    RETURN "Users added"
Query addUsers(names: [{name: String}]) =>
    FOR {name} IN names {
        AddN<User>({name: name})
    }
    RETURN "Users added"

W101

For the complete documentation index optimized for AI agents, see llms.txt.

Query Has No Return

Erroneous Code Example

This warning occurs when a query does not have a RETURN statement. While not always an error, queries typically should return a value.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
N::User {
    name: String,
}

Solution

Add a RETURN statement to specify what the query should return, or use a mutation operation like INSERT, UPDATE, or DROP if the query is intended to modify data without returning a value.

QUERY getUser(id: ID) =>
    user <- N<User>(id)
    RETURN user
N::User {
    name: String,
}