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 definitionQueryName: Identifier for the queryparameters: Input parameters in parenthesesType: Type of the parameter (e.g.String,I32,F64,Boolean,[Type]or schema Node/Edge)=>: Separates query header from body<-: Assignment operatorRETURN: 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:
| Type | Description | Type | Description |
|---|---|---|---|
ID | UUID for nodes, edges and vectors | [T] | Array of any type |
Date | Timestamp or RFC3339 string | F32 | 32-bit floating point |
String | Text data | F64 | 64-bit floating point |
Boolean | True/false value | U8 | 8-bit unsigned integer |
I8 | 8-bit signed integer | U16 | 16-bit unsigned integer |
I16 | 16-bit signed integer | U32 | 32-bit unsigned integer |
I32 | 32-bit signed integer | U64 | 64-bit unsigned integer |
I64 | 64-bit signed integer | U128 | 128-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() =>
- A query with one typed parameter.
- A query with multiple typed parameters.
- 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
- Return a single binding.
- Return multiple bindings.
- Return projected fields.
- Return only IDs.
- Return a string literal.
- 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
- Bind a node lookup to
user. - Bind a filtered traversal to
users. - 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
- A standalone comment line.
- 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
- Text data.
- True/false value.
- 8-bit unsigned integer. Also available:
U16,U32,U64,U128. - 64-bit signed integer. Also available:
I8,I16,I32. - 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}
- Accept an ID as a query parameter.
- 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
- A date field in a schema.
- 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]
- An array of 64-bit floats as a parameter.
- An array of objects as a parameter.
- 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,
}
- Define a
Usernode 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
}
}
- An edge with no properties connecting
UsertoUser. - An edge with properties connecting
UsertoUser.
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 {}
- A vector type with metadata properties.
- 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,
}
- Index the
emailfield onUsernodes for fast lookups viaN<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,
}
- Enforce uniqueness on the
emailfield. - Enforce that only one
BestFriendedge 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,
}
- 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"})
- Create a
Usernode with no properties. - Create a
Usernode 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})
- Select all
Usernodes. - Select a
Userby ID. - Select a
Userby 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})
- Find existing nodes matching a condition.
- 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)
- Create a
Followsedge between two nodes. - Create a
Friendsedge with properties.
See Create Edges for more details.
E
Select edges by type or by ID.
E<Follows>
E<Follows>(edge_id)
- Select all
Followsedges. - Select a specific
Followsedge 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)
- Select the edge type to operate on.
- 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})
- Create a vector with no metadata.
- Create a vector with metadata properties.
- Create a vector using the built-in
Embedfunction.
See Create Vectors for more details.
V
Select vectors by type or by ID.
V<Document>
V<Document>(vector_id)
- Select all
Documentvectors. - 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})
- Find existing vectors matching a condition.
- 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)
- Embed text and store it as a vector.
- 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>
- Get all users that
user_idfollows. - 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>
- 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>
- Get the outgoing
Followsedge 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>
- Get the incoming
Followsedge 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
- Get the node that the
Createsedge originates from.
See Traversals From Edges for more details.
::ToN
Get the target node of an edge.
E<Creates>(edge_id)::ToN
- Get the node that the
Createsedge points to.
See Traversals From Edges for more details.
::FromV
Get the source vector of an edge.
E<HasEmbedding>(edge_id)::FromV
- 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
- 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)))
- Filter users where
ageis greater than 18. - Filter users that have at least one follower.
- 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))
INTERSECTtakes an anonymous traversal expression, such as_::In<HasTag>.- Helix runs that sub-traversal for each upstream element, then keeps only IDs shared across all sub-results.
- 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))
- Filter where
statusequals the string"active". - Filter where
nameequals a query parameter.
See Conditional Steps for more details.
::NEQ
Not equals comparison. Works with strings, booleans, and numbers.
::WHERE(_::{role}::NEQ("admin"))
- Filter where
roleis not"admin".
See Conditional Steps for more details.
::GT
Greater than comparison. Works with numbers.
::WHERE(_::{age}::GT(18))
- Filter where
ageis greater than 18.
See Conditional Steps for more details.
::GTE
Greater than or equal comparison. Works with numbers.
::WHERE(_::{rating}::GTE(4.5))
- Filter where
ratingis at least 4.5.
See Conditional Steps for more details.
::LT
Less than comparison. Works with numbers.
::WHERE(_::{age}::LT(30))
- Filter where
ageis less than 30.
See Conditional Steps for more details.
::LTE
Less than or equal comparison. Works with numbers.
::WHERE(_::{priority}::LTE(2))
- Filter where
priorityis 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))
- Filter where
namecontains the substring"john". - Filter where the outgoing
Followsset containsuser.
See Conditional Steps for more details.
::IS_IN
Check if a value exists in an array.
::WHERE(_::{status}::IS_IN(["active", "pending"]))
- Filter where
statusis 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>))
- Filter for elements that have incoming
Followsedges. - Filter for elements that have no incoming
Followsedges (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)))
- Match when all conditions are true.
- Match when any condition is true.
- Negate an
AND/ORblock 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
- Get the first
Usernode. - 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
- Count all
Usernodes. - 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)
- Get the first 10 users (elements 0 through 9).
- 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})
- Sort users by
agedescending (oldest first). - Sort users by
ageascending (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)
- Group users by
age, returning[{age: 25, count: 3}, ...]. - 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)
- Aggregate users by
department, returning counts and full user objects per group. - 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}
- Return only
nameandagefrom each user. - Return three specific fields.
See Property Access for more details.
::ID
Access an element’s unique identifier.
users::ID
users::{userID: ID, name}
- Return only the IDs of users.
- 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}
- Return all user properties except
emailandlocation.
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}
- Rename
nametodisplayNameandagetouserAge. - Map the ID to
userIDandnametodisplayName.
See Property Remappings for more details.
Spread Operator (..)
Include all properties while optionally adding or remapping specific fields.
users::{
userID: ID,
..
}
- Include all schema properties plus a computed
userIDfield.
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,
..
}
}
- Scope
usras a reference touser, 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
}
- Add a
followerCountcomputed from the incomingFollowsedge 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})
- Update the
nameandageof 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>
- Delete a user node and all its connected edges.
- Delete all nodes that the user follows (and connecting edges).
- Delete only the outgoing
Followsedges without touching neighbor nodes.
See Delete Operation for more details.
Search
SearchV
Vector similarity search using cosine similarity.
SearchV<Document>(vector, 10)
SearchV<Document>(Embed(query), limit)
- Search for the 10 most similar
Documentvectors to a raw vector. - 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)
- Search
Documentnodes for keywords, returning up to 10 results. - 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)
- Apply RRF with the default
k=60. - Apply RRF with a custom
kparameter.
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")
- Apply MMR with cosine distance (default).
- Apply MMR with euclidean distance.
- 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)
- Find the shortest unweighted path via
Roadedges.
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)
- Find the shortest unweighted path from one city to another via
Roadedges.
See ShortestPathBFS for more details.
::ShortestPathDijkstras
Weighted shortest path using Dijkstra’s algorithm.
N<City>(start_id)::ShortestPathDijkstras<Road>(_::{distance})::To(end_id)
- Find the shortest weighted path using the
distanceedge 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)
- Find the shortest path using edge
distanceas weight and a nodeheuristicproperty 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})
- Use an edge property as weight.
- Reference the source node’s property.
- Reference the target node’s property.
- 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})
}
- 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})
}
- Destructure
name,age, andemailfrom 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)
- Addition:
price + tax. - Subtraction:
total - discount. - Multiplication:
quantity * unit_price. - Division:
total / count. - Power:
base ^ exponent. - 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)
- Absolute value.
- Square root.
- Natural logarithm.
- Base-10 logarithm.
- Logarithm with custom base.
- Exponential (
e^value). - Round up to nearest integer.
- Round down to nearest integer.
- 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)
- Sine of an angle.
- Cosine of an angle.
- Tangent of an angle.
- Inverse sine (arc sine).
- Inverse cosine (arc cosine).
- Inverse tangent (arc tangent).
- Two-argument inverse tangent.
See Trigonometric Functions for more details.
Constants
Mathematical constants.
PI()
E()
- Returns the value of pi (~3.14159).
- 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)
- Minimum value in a collection.
- Maximum value in a collection.
- Sum of all values.
- Average of all values.
- 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
- 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
- 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
- Return a string literal.
- Return projected properties.
- Return multiple bindings.
- Return all properties except
email. - 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_idandemail - Each project has a unique
project_code - Each project can only have one lead (via the
UNIQUEedge constraint) - Employees can work on multiple projects (non-unique
WorksOnedge)
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
| Type | Example | Description |
|---|---|---|
| String | DEFAULT "value" | Default string literal |
| Number | DEFAULT 0 | Default numeric value |
| Timestamp | DEFAULT NOW | Automatically 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.hxfile 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 '{}'
Related operations
- Select nodes - Query existing nodes
- Create edges - Connect nodes with relationships
- Update nodes - Modify node properties
- Schema definitions - Define node types
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.hxfile 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
F64values to represent the vector. We will be adding support for different types such asF32, 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.hxfile 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-002to 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
UpsertNis 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.hxfile 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
| Operation | Behavior |
|---|---|
AddN | Always creates a new node |
UPDATE | Only modifies existing nodes (fails if node doesn’t exist) |
UpsertN | Operates on traversal context: updates if nodes found, creates if empty |
💡 Tip
When updating,
UpsertNmerges properties: it updates specified properties while preserving any existing properties that aren’t included in the upsert.
Related operations
- Create nodes - Always create new nodes with AddN
- Update nodes - Modify existing node properties
- Upsert edges - Insert-or-update edges
- Upsert vectors - Insert-or-update vectors
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
UpsertEis 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 forUpsertE. The order ofFromandTois 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
| Operation | Behavior |
|---|---|
AddE | Always creates a new edge (can create duplicates) |
UpsertE | Operates on traversal context: updates if edge exists between nodes, creates if not |
💡 Tip
When updating,
UpsertEmerges properties: it updates specified properties while preserving any existing properties that aren’t included in the upsert.
Related operations
- Create edges - Always create new edges with AddE
- Upsert nodes - Insert-or-update nodes
- Upsert vectors - Insert-or-update vectors
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
UpsertVis 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
| Operation | Behavior |
|---|---|
AddV | Always creates a new vector |
UpsertV | Operates on traversal context: updates if vectors found, creates if empty |
💡 Tip
When updating,
UpsertVmerges properties: it updates specified properties while preserving any existing properties that aren’t included in the upsert. The vector data itself is also updated.
Related operations
- Create vectors - Always create new vectors with AddV
- Vector search - Search for similar vectors
- Upsert nodes - Insert-or-update nodes
- Upsert edges - Insert-or-update edges
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.
Related operations
- Create nodes - Add new nodes to your graph
- Traversals - Navigate between connected nodes
- Conditionals - Filter nodes by properties
- Property access - Work with node properties
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.hxfile 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
| Operation | Description | Example |
|---|---|---|
::EQ(value) | Equals | ::WHERE(_::{status}::EQ("active")) |
::NEQ(value) | Not equals | ::WHERE(_::{age}::NEQ(25)) |
Number Operations
| Operation | Description | Example |
|---|---|---|
::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
| Operation | Description | Example |
|---|---|---|
::CONTAINS(value) | String contains substring | ::WHERE(_::{name}::CONTAINS("john")) |
::CONTAINS(object) | Array contains object | ::WHERE(_::Out<Follows>::CONTAINS(user)) |
Array/Set Operations
| Operation | Description | Example |
|---|---|---|
::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.hxfile 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.hxfile 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.hxfile 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
::IDsyntax.
⚠️ Warning
When using the SDKs or curling the endpoint, the query name must match what is defined in the
queries.hxfile 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.hxfile 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.hxfile 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.hxfile 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.hxfile 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
IDlike we have shown previously:nested_field: ID. This ID would be the ID of theotheritem, not theitem_nameitem due to the tighter scope.
⚠️ Warning
When accessing properties in nested mappings, scope matters. Use the explicit
item_name::propertysyntax 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
F64values to represent the vector. We will be adding support for different types such asF32, 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.hxfile exactly.
Example 1: Basic vector search
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-002to 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.jsonfile). The default embedding model istext-embedding-ada-002from OpenAI.
⚠️ Warning
Make sure to set your
OPENAI_API_KEYenvironment variable with your API key in the same location as thequeries.hx,schema.hxandconfig.hx.jsonfiles. When using the SDKs or curling the endpoint, the query name must match what is defined in thequeries.hxfile 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-002to 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.hxfile exactly.
Example 1: Basic keyword search
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
- Retrieve more results initially: Fetch 100-200 candidates to give rerankers sufficient options to work with
- Apply rerankers before RANGE: Rerank first, then limit the number of results returned
- Choose the right reranker: Use RRF for combining searches, MMR for diversification
- 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.hxfile exactly.
How It Works
RRF uses a simple but effective formula to combine rankings:
RRF_score(d) = Σ 1/(k + rank_i(d))
Where:
dis a document/resultrank_i(d)is the rank of documentdin the i-th result listkis a constant that controls the impact of ranking differences
The algorithm works by:
- Taking multiple ranked lists of results
- For each item, calculating its RRF score based on its positions across all lists
- Re-ranking items by their combined RRF scores
- 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
E-commerce Product Search
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)
Related
- RerankMMR - For diversifying search results
- Vector Search - Basic vector search operations
- Result Operations - Other result manipulation operations
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.hxfile 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:
dis a candidate documentqis the queryd_iare already-selected documentsλ(lambda) controls the relevance vs. diversity trade-offSim1measures relevance to the querySim2measures similarity to selected documents
The algorithm works by:
- Starting with the most relevant result
- For each subsequent position, calculating MMR scores for remaining candidates
- Selecting the candidate with the highest MMR score (balancing relevance and novelty)
- 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)
Related
- RerankRRF - For combining multiple search results
- Rerankers Overview - General reranking concepts
- Vector Search - Basic vector search operations
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
| Algorithm | Best For | Graph Type | Complexity | Customizable Weights |
|---|---|---|---|---|
| BFS | Hop count minimization | Unweighted | O(V + E) | No |
| Dijkstra | Flexible weight calculations | Weighted | O((V + E) log V) | Yes |
| A* | Goal-directed search | Weighted + Heuristic | O((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:
- Use BFS when weights aren’t needed
- Index properties used in weight calculations
- For A*, ensure heuristic is admissible (never overestimates)
- Consider graph density when choosing algorithms
Related Topics
-
Custom Weights — Learn about property contexts and weight expressions
-
Advanced Expressions — Compose complex mathematical weight calculations
-
Graph Traversals — Other graph traversal operations
-
Mathematical Functions — Available math functions for weight expressions
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.hxfile exactly.
How It Works
BFS explores the graph level by level:
- Start from the source node
- Visit all neighbors at distance 1
- Then visit all neighbors at distance 2
- 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.
Related Topics
-
ShortestPathDijkstras — Find weighted shortest paths with custom calculations
-
ShortestPathAStar — Heuristic-guided pathfinding
-
Overview — Compare all shortest path algorithms
-
Graph Traversals — Other traversal operations
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.hxfile exactly.
How It Works
Dijkstra’s algorithm:
- Starts at the source node with distance 0
- Explores neighbors, calculating cumulative path weights
- Always processes the node with the smallest known distance next
- 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
| Context | Description | Example |
|---|---|---|
_::{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.
Related Topics
-
Custom Weights — Deep dive into property contexts and weight patterns
-
Weight Expressions — Advanced mathematical weight calculations
-
ShortestPathAStar — Faster pathfinding with heuristics
-
Mathematical Functions — All available math functions
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.hxfile exactly.
How It Works
A* combines two costs:
- g(n): Actual cost from start to current node (like Dijkstra)
- 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:
- Be stored on each node
- Estimate cost to reach the target
- Never overestimate (admissible)
- 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
| Scenario | Dijkstra | A* with Good Heuristic |
|---|---|---|
| Small graph (< 100 nodes) | Fast | Similar |
| Large graph (> 10,000 nodes) | Slow | 10-100x faster |
| Short paths | Fast | Similar |
| Long paths | Slow | Much faster |
| No spatial info | Only option | Not applicable |
| Spatial graph | Explores everything | Explores 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)
Related Topics
-
ShortestPathDijkstras — Learn about Dijkstra’s algorithm (A* without heuristic)
-
Custom Weights — Property contexts for weight calculations
-
Weight Expressions — Advanced mathematical weight expressions
-
Overview — Compare all shortest path algorithms
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 Syntax | Description | Example Use Case |
|---|---|---|
_::{property} | Edge property | Road distance, bandwidth, cost |
_::FromN::{property} | Source node property | Origin traffic, elevation, population |
_::ToN::{property} | Destination node property | Target popularity, capacity, demand |
_::FromV::{property} | Source node vector property | Embedding similarity |
_::ToV::{property} | Destination node vector property | Feature 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
Related Topics
-
Weight Expressions — Advanced mathematical weight calculations
-
ShortestPathDijkstras — Full Dijkstra algorithm documentation
-
Mathematical Functions — All available math functions
-
Overview — Compare all shortest path algorithms
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
| Complexity | Operations | Performance Impact |
|---|---|---|
| Simple | Single property | Minimal (1% overhead) |
| Moderate | 2-5 operations | Low (1-5% overhead) |
| Complex | 6-15 operations | Moderate (5-15% overhead) |
| Very Complex | 15+ operations | Higher (15-30% overhead) |
Optimization Tips
- Pre-calculate when possible: Store derived values as properties
// Instead of: POW(0.95, DIV(days, 30))
// Pre-calculate: decay_factor property
- Simplify expressions: Combine constants
// Instead of: MUL(MUL(_::{x}, 0.3), 0.4)
// Use: MUL(_::{x}, 0.12)
-
Avoid expensive operations in hot paths:
- Trigonometric functions (SIN, COS, TAN) are slower
- Multiple POW operations add up
- Consider lookup tables for complex functions
-
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))
Related Topics
-
Custom Weights — Property contexts and basic weight patterns
-
Mathematical Functions — Complete reference of all math functions
-
ShortestPathDijkstras — Dijkstra’s algorithm documentation
-
Overview — Compare all shortest path algorithms
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_BYreturns 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.hxfile 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_BYreturns 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.hxfile 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
FIRSTreturns 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.hxfile 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.hxfile 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
Ascfor ascending order andDescfor descending order.
⚠️ Warning
When using the SDKs or curling the endpoint, the query name must match what is defined in the
queries.hxfile 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.hxfile 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 '{}'
Related Topics
-
Aggregations — Aggregate results with full data objects
-
Grouping Guide — When to use GROUP_BY vs AGGREGATE_BY
-
COUNT Operation — Count operation and other result operations
-
Property Access — Property filtering and access patterns
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.hxfile 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.
Related Topics
-
Group By — Group results with count summaries only
-
Grouping Guide — When to use GROUP_BY vs AGGREGATE_BY
-
COUNT Operation — Count operation and other result operations
-
Property Access — Property filtering and access patterns
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
| Feature | GROUP_BY | AGGREGATE_BY |
|---|---|---|
| Returns | Count summaries only | Full data objects + counts |
| Memory Usage | Low - only stores counts | Higher - stores all objects |
| Use Case | Analytics, distributions | Detailed reports, processing |
| Output Size | Small, compact | Large, comprehensive |
| Best For | Dashboards, statistics | Data 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:
- Building analytics dashboards
- Showing data distributions
- Generating summary reports
- Optimizing for memory/bandwidth
- Working with large datasets (millions of records)
- Creating charts or graphs
Use AGGREGATE_BY When:
- Need to process grouped data further
- Building detailed reports with examples
- Need to display sample records per group
- Performing transformations on grouped items
- Working with moderate datasets (thousands of records)
- Building data exploration interfaces
⚠️ Warning
When using the SDKs or curling the endpoint, the query name must match what is defined in the
queries.hxfile 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.
Related Topics
-
Group By — Group results with count summaries
-
Aggregations — Aggregate results with full data objects
-
COUNT Operation — Count operation and other result operations
-
Property Access — Property filtering and access patterns
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 — Iterate over collections with simple variable binding
-
FOR Loop Destructuring — Extract multiple properties directly in loop variable binding
-
Nested FOR Loops — Use multiple levels of iteration for complex operations
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.hxfile 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"}
]
}'
Related topics
-
FOR Loop Destructuring — Extract multiple properties directly in loop variable binding
-
Nested FOR Loops — Use multiple levels of iteration for complex operations
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.hxfile 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"
}
]
}'
Related topics
-
Basic FOR Loops — Iterate over collections with simple variable binding
-
Nested FOR Loops — Use multiple levels of iteration for complex operations
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.hxfile 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:
- Filter early: Apply WHERE clauses before entering loops to reduce iteration count
- Consider alternatives: Sometimes traversals or joins can be more efficient than nested loops
- Batch operations: Group related operations to minimize database calls
- Monitor performance: Test with realistic data volumes to identify bottlenecks
Related topics
-
Basic FOR Loops — Iterate over collections with simple variable binding
-
FOR Loop Destructuring — Extract multiple properties directly in loop variable binding
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
Related Topics
-
Shortest Paths — Use math functions in custom weight calculations
-
Conditionals — Combine math with conditional logic
-
Properties — Access properties in mathematical expressions
-
Result Operations — Combine math with result filtering
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.hxfile 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)
)
)
Related Topics
-
Unary Math Functions — SQRT, ABS, LOG, and other single-argument functions
-
Custom Weight Calculations — Use arithmetic in shortest path weights
-
Math Overview — Overview of all mathematical functions
-
Aggregate Functions — MIN, MAX, SUM, AVG for collections
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.hxfile 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))
)
Related Topics
-
Arithmetic Functions — ADD, SUB, MUL, DIV, POW, MOD
-
Constants — PI and E constants
-
Custom Weights — Use in shortest path calculations
-
Math Overview — Overview of all math functions
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.hxfile 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
Related Topics
-
Unary Math Functions — SQRT, ABS, LN, LOG10, EXP, CEIL, FLOOR, ROUND
-
Constants — PI and E constants
-
Arithmetic Functions — ADD, SUB, MUL, DIV, POW, MOD
-
Math Overview — Overview of all math functions
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.hxfile 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
Related Topics
-
Trigonometric Functions — SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2
-
Unary Math Functions — SQRT, ABS, LN, LOG10, EXP, CEIL, FLOOR, ROUND
-
Arithmetic Functions — ADD, SUB, MUL, DIV, POW, MOD
-
Math Overview — Overview of all math functions
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.hxfile 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
Related Topics
-
Unary Math Functions — SQRT, ABS, LN, LOG10, EXP, CEIL, FLOOR, ROUND
-
Arithmetic Functions — ADD, SUB, MUL, DIV, POW, MOD
-
Conditionals — Filter data before aggregation
-
Math Overview — Overview of all math functions
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 Type | Syntax | Return Type | Syntax |
|---|---|---|---|
| Return a binding | RETURN users | Exclude fields | RETURN users::!{ email } |
| Return multiple | RETURN user, posts | Aggregation/scalar | RETURN count |
| Project fields | RETURN users::{ name, age } | Literal | RETURN "ok" |
| Only IDs | RETURN users::ID | No payload | RETURN 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 = trueunder the instance you are using in thehelix.tomlfile.
How it works
When you add the #[mcp] attribute to a query:
- HelixDB automatically registers the query as an MCP tool
- The query parameters become the tool’s input schema
- The query’s return type becomes the tool’s output schema
- 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) => ...
Related Documentation
- 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 yourhelix.tomlfile for that specific query.
Syntax Reference
The model string follows the format "provider:model-name:task-type":
providerThe embedding service providermodel-nameThe specific model identifiertask-typeTask optimization hint
Supported models
| Provider | Model | Task Type | Notes |
|---|---|---|---|
gemini | gemini-embedding-001 | RETRIEVAL_DOCUMENT OR RETRIEVAL_QUERY | RETRIEVAL_DOCUMENT is the default if no task type is included |
openai | text-embedding-ada-002 | Not Supported | Not recommended |
openai | text-embedding-small | Not Supported | |
openai | text-embedding-large | Not Supported |
Why use different models for storage vs. search?
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 laterRETRIEVAL_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.
Example: Document Storage vs. Search
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 ¬es {
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_KEYenvironment variable (orOPENAI_API_KEYfor OpenAI models) in the same location as yourqueries.hx,schema.hx, andconfig.hx.jsonfiles.
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
Related Documentation
-
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,
}