Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Nested FOR Loops

⚠️ Warning

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

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

Use nested FOR loops for multi-level iteration

Nest FOR loops to perform operations across multiple collections simultaneously. This is particularly useful for creating relationships between all pairs of elements or processing hierarchical data structures.

FOR outer_var IN outer_collection {
    FOR inner_var IN inner_collection {
        // Operations using both outer_var and inner_var
    }
}

ℹ️ Note

Nested loops are executed for every combination of elements from the outer and inner collections, resulting in a cartesian product of operations.

⚠️ Warning

Be mindful of performance when using nested loops with large collections. The number of operations grows multiplicatively with collection sizes (N × M operations for two collections).

⚠️ Warning

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

Example 1: Creating connections between all user pairs

QUERY CreateUserNetwork (user_data: [{name: String, age: U8, interests: String}]) =>
    // First, create all users
    FOR {name, age, interests} IN user_data {
        user <- AddN<User>({
            name: name,
            age: age,
            interests: interests
        })
    }

    // Then create connections between all users
    all_users <- N<User>
    FOR user1 IN all_users {
        FOR user2 IN all_users {
            // Don't create self-connections
            IF user1::ID != user2::ID THEN {
                AddE<Knows>::From(user1)::To(user2)
            }
        }
    }
    RETURN "User network created"
N::User {
    name: String,
    age: U8,
    interests: String
}

E::Knows {
}

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

from helix.client import Client

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

# Create a fully connected user network
user_data = [
    {"name": "Alice", "age": 25, "interests": "Technology, Reading"},
    {"name": "Bob", "age": 30, "interests": "Sports, Music"},
    {"name": "Charlie", "age": 28, "interests": "Art, Photography"},
    {"name": "Diana", "age": 22, "interests": "Travel, Cooking"},
]

result = client.query("CreateUserNetwork", {"user_data": user_data})
print("Network creation result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

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

    // Create a fully connected user network
    let user_data = vec![
        json!({"name": "Alice", "age": 25, "interests": "Technology, Reading"}),
        json!({"name": "Bob", "age": 30, "interests": "Sports, Music"}),
        json!({"name": "Charlie", "age": 28, "interests": "Art, Photography"}),
        json!({"name": "Diana", "age": 22, "interests": "Travel, Cooking"}),
    ];

    let result: serde_json::Value = client.query("CreateUserNetwork", &json!({
        "user_data": user_data
    })).await?;
    println!("Network creation result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

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

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

    // Create a fully connected user network
    userData := []map[string]any{
        {"name": "Alice", "age": uint8(25), "interests": "Technology, Reading"},
        {"name": "Bob", "age": uint8(30), "interests": "Sports, Music"},
        {"name": "Charlie", "age": uint8(28), "interests": "Art, Photography"},
        {"name": "Diana", "age": uint8(22), "interests": "Travel, Cooking"},
    }

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

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

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

    // Create a fully connected user network
    const userData = [
        { name: "Alice", age: 25, interests: "Technology, Reading" },
        { name: "Bob", age: 30, interests: "Sports, Music" },
        { name: "Charlie", age: 28, interests: "Art, Photography" },
        { name: "Diana", age: 22, interests: "Travel, Cooking" },
    ];

    const result = await client.query("CreateUserNetwork", { user_data: userData });
    console.log("Network creation result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
curl -X POST \
  http://localhost:6969/CreateUserNetwork \
  -H 'Content-Type: application/json' \
  -d '{
    "user_data": [
      {"name": "Alice", "age": 25, "interests": "Technology, Reading"},
      {"name": "Bob", "age": 30, "interests": "Sports, Music"},
      {"name": "Charlie", "age": 28, "interests": "Art, Photography"},
      {"name": "Diana", "age": 22, "interests": "Travel, Cooking"}
    ]
  }'

Example 2: Cross-product operations with hierarchical data

QUERY LoadDocumentStructure (
    chapters: [{
        id: I64,
        title: String,
        subchapters: [{
            title: String,
            content: String,
            chunks: [{chunk: String, vector: [F64]}]
        }]
    }]
) =>
    FOR {id, title, subchapters} IN chapters {
        chapter_node <- AddN<Chapter>({
            chapter_index: id,
            title: title
        })

        FOR {title, content, chunks} IN subchapters {
            subchapter_node <- AddN<SubChapter>({
                title: title,
                content: content
            })
            AddE<Contains>::From(chapter_node)::To(subchapter_node)

            FOR {chunk, vector} IN chunks {
                vec <- AddV<Embedding>(vector)
                AddE<EmbeddingOf>({chunk: chunk})::From(subchapter_node)::To(vec)
            }
        }
    }
    RETURN "Document structure loaded"
N::Chapter {
    chapter_index: I64,
    title: String
}

N::SubChapter {
    title: String,
    content: String
}

V::Embedding {
    dimension: 384
}

E::Contains {
}

E::EmbeddingOf {
    chunk: String
}

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

from helix.client import Client
import random

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

# Helper to generate sample embeddings
def generate_embedding(dim=384):
    return [random.random() for _ in range(dim)]

# Load hierarchical document structure
chapters = [
    {
        "id": 1,
        "title": "Introduction",
        "subchapters": [
            {
                "title": "Overview",
                "content": "This chapter provides an overview of the topic.",
                "chunks": [
                    {"chunk": "First chunk of text", "vector": generate_embedding()},
                    {"chunk": "Second chunk of text", "vector": generate_embedding()},
                ]
            },
            {
                "title": "Background",
                "content": "Historical context and background information.",
                "chunks": [
                    {"chunk": "Background chunk 1", "vector": generate_embedding()},
                    {"chunk": "Background chunk 2", "vector": generate_embedding()},
                ]
            }
        ]
    },
    {
        "id": 2,
        "title": "Main Content",
        "subchapters": [
            {
                "title": "Key Concepts",
                "content": "Detailed explanation of key concepts.",
                "chunks": [
                    {"chunk": "Concept explanation 1", "vector": generate_embedding()},
                    {"chunk": "Concept explanation 2", "vector": generate_embedding()},
                ]
            }
        ]
    }
]

result = client.query("LoadDocumentStructure", {"chapters": chapters})
print("Document structure result:", result)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;
use rand::Rng;

fn generate_embedding(dim: usize) -> Vec<f64> {
    let mut rng = rand::thread_rng();
    (0..dim).map(|_| rng.gen::<f64>()).collect()
}

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

    // Load hierarchical document structure
    let chapters = vec![
        json!({
            "id": 1,
            "title": "Introduction",
            "subchapters": [
                {
                    "title": "Overview",
                    "content": "This chapter provides an overview of the topic.",
                    "chunks": [
                        {"chunk": "First chunk of text", "vector": generate_embedding(384)},
                        {"chunk": "Second chunk of text", "vector": generate_embedding(384)},
                    ]
                },
                {
                    "title": "Background",
                    "content": "Historical context and background information.",
                    "chunks": [
                        {"chunk": "Background chunk 1", "vector": generate_embedding(384)},
                        {"chunk": "Background chunk 2", "vector": generate_embedding(384)},
                    ]
                }
            ]
        }),
        json!({
            "id": 2,
            "title": "Main Content",
            "subchapters": [
                {
                    "title": "Key Concepts",
                    "content": "Detailed explanation of key concepts.",
                    "chunks": [
                        {"chunk": "Concept explanation 1", "vector": generate_embedding(384)},
                        {"chunk": "Concept explanation 2", "vector": generate_embedding(384)},
                    ]
                }
            ]
        })
    ];

    let result: serde_json::Value = client.query("LoadDocumentStructure", &json!({
        "chapters": chapters
    })).await?;
    println!("Document structure result: {result:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"
    "math/rand"

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

func generateEmbedding(dim int) []float64 {
    embedding := make([]float64, dim)
    for i := range embedding {
        embedding[i] = rand.Float64()
    }
    return embedding
}

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

    // Load hierarchical document structure
    chapters := []map[string]any{
        {
            "id":    int64(1),
            "title": "Introduction",
            "subchapters": []map[string]any{
                {
                    "title":   "Overview",
                    "content": "This chapter provides an overview of the topic.",
                    "chunks": []map[string]any{
                        {"chunk": "First chunk of text", "vector": generateEmbedding(384)},
                        {"chunk": "Second chunk of text", "vector": generateEmbedding(384)},
                    },
                },
                {
                    "title":   "Background",
                    "content": "Historical context and background information.",
                    "chunks": []map[string]any{
                        {"chunk": "Background chunk 1", "vector": generateEmbedding(384)},
                        {"chunk": "Background chunk 2", "vector": generateEmbedding(384)},
                    },
                },
            },
        },
        {
            "id":    int64(2),
            "title": "Main Content",
            "subchapters": []map[string]any{
                {
                    "title":   "Key Concepts",
                    "content": "Detailed explanation of key concepts.",
                    "chunks": []map[string]any{
                        {"chunk": "Concept explanation 1", "vector": generateEmbedding(384)},
                        {"chunk": "Concept explanation 2", "vector": generateEmbedding(384)},
                    },
                },
            },
        },
    }

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

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

function generateEmbedding(dim: number = 384): number[] {
    return Array.from({ length: dim }, () => Math.random());
}

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

    // Load hierarchical document structure
    const chapters = [
        {
            id: 1,
            title: "Introduction",
            subchapters: [
                {
                    title: "Overview",
                    content: "This chapter provides an overview of the topic.",
                    chunks: [
                        { chunk: "First chunk of text", vector: generateEmbedding() },
                        { chunk: "Second chunk of text", vector: generateEmbedding() },
                    ]
                },
                {
                    title: "Background",
                    content: "Historical context and background information.",
                    chunks: [
                        { chunk: "Background chunk 1", vector: generateEmbedding() },
                        { chunk: "Background chunk 2", vector: generateEmbedding() },
                    ]
                }
            ]
        },
        {
            id: 2,
            title: "Main Content",
            subchapters: [
                {
                    title: "Key Concepts",
                    content: "Detailed explanation of key concepts.",
                    chunks: [
                        { chunk: "Concept explanation 1", vector: generateEmbedding() },
                        { chunk: "Concept explanation 2", vector: generateEmbedding() },
                    ]
                }
            ]
        }
    ];

    const result = await client.query("LoadDocumentStructure", { chapters });
    console.log("Document structure result:", result);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Note: This example shows the structure. In practice, you'd generate actual embedding vectors.
curl -X POST \
  http://localhost:6969/LoadDocumentStructure \
  -H 'Content-Type: application/json' \
  -d '{
    "chapters": [
      {
        "id": 1,
        "title": "Introduction",
        "subchapters": [
          {
            "title": "Overview",
            "content": "This chapter provides an overview of the topic.",
            "chunks": [
              {"chunk": "First chunk of text", "vector": [0.1, 0.2, 0.3]},
              {"chunk": "Second chunk of text", "vector": [0.4, 0.5, 0.6]}
            ]
          }
        ]
      }
    ]
  }'

Performance considerations

⚠️ Warning

Nested loops can significantly impact performance:

  • Two loops over collections of size N and M result in N × M operations
  • Three nested loops result in N × M × P operations
  • Consider using WHERE clauses to filter collections before looping
  • For large datasets, evaluate whether nested loops are the most efficient approach

Tips for optimizing nested loops:

  1. Filter early: Apply WHERE clauses before entering loops to reduce iteration count
  2. Consider alternatives: Sometimes traversals or joins can be more efficient than nested loops
  3. Batch operations: Group related operations to minimize database calls
  4. Monitor performance: Test with realistic data volumes to identify bottlenecks