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

Model Macro

⚠️ Warning

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

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

Usage

The #[model] macro specifies which embedding model to use for Embed() calls within a query. This allows you to optimize embeddings for different use cases—such as storing documents versus searching them.

#[model("provider:model-name:task-type")]
QUERY QueryName(param: Type) =>
    result <- AddV<Type>(Embed(param), {properties})
    RETURN result

ℹ️ Note

The #[model] macro overrides the default embedding model configured in your helix.toml file for that specific query.


Syntax Reference

The model string follows the format "provider:model-name:task-type":

  • provider The embedding service provider
  • model-name The specific model identifier
  • task-type Task optimization hint

Supported models

ProviderModelTask TypeNotes
geminigemini-embedding-001RETRIEVAL_DOCUMENT OR RETRIEVAL_QUERYRETRIEVAL_DOCUMENT is the default if no task type is included
openaitext-embedding-ada-002Not SupportedNot recommended
openaitext-embedding-smallNot Supported
openaitext-embedding-largeNot Supported

Many embedding providers offer task-specific optimizations. For example, Gemini’s embedding models support task types that hint whether the text being embedded is a document (to be stored and searched) or a query (used to search documents).

  • RETRIEVAL_DOCUMENT - Optimized for embedding documents that will be stored and retrieved later
  • RETRIEVAL_QUERY - Optimized for embedding search queries that find relevant documents

Using the appropriate task type can improve search relevance and retrieval quality.

⚠️ Warning

All vectors in a vector type must have the same dimensions. When using different task types, ensure both use the same model (e.g., gemini-embedding-001) so the dimensions match.


This example shows how to use different task types for storing clinical notes versus searching them.

#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY addClinicalNote (text: String) =>
    note <- AddV<ClinicalNote>(Embed(text), {text: text})
    RETURN note

#[model("gemini:gemini-embedding-001:RETRIEVAL_QUERY")]
QUERY searchClinicalNotes (query: String, k: I64) =>
    notes <- SearchV<ClinicalNote>(Embed(query), k)
    RETURN notes
V::ClinicalNote {
    vector: [F64],
    text: String
}
GEMINI_API_KEY=your_api_key

Here’s how to run the queries using the SDKs or curl:

from helix.client import Client

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

# Store clinical notes with RETRIEVAL_DOCUMENT optimization
notes = [
    "Patient presents with acute lower back pain radiating to left leg.",
    "Follow-up visit: Blood pressure 120/80, heart rate normal.",
    "MRI results show mild disc herniation at L4-L5 level."
]

for note in notes:
    client.query("addClinicalNote", {"text": note})

# Search with RETRIEVAL_QUERY optimization
results = client.query("searchClinicalNotes", {
    "query": "back pain symptoms",
    "k": 5
})

print(results)
use helix_rs::{HelixDB, HelixDBClient};
use serde_json::json;

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

    // Store clinical notes with RETRIEVAL_DOCUMENT optimization
    let notes = vec![
        "Patient presents with acute lower back pain radiating to left leg.",
        "Follow-up visit: Blood pressure 120/80, heart rate normal.",
        "MRI results show mild disc herniation at L4-L5 level."
    ];

    for note in &notes {
        let _inserted: serde_json::Value = client.query("addClinicalNote", &json!({
            "text": note
        })).await?;
    }

    // Search with RETRIEVAL_QUERY optimization
    let results: serde_json::Value = client.query("searchClinicalNotes", &json!({
        "query": "back pain symptoms",
        "k": 5
    })).await?;

    println!("Search results: {results:#?}");

    Ok(())
}
package main

import (
    "fmt"
    "log"

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

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

    // Store clinical notes with RETRIEVAL_DOCUMENT optimization
    notes := []string{
        "Patient presents with acute lower back pain radiating to left leg.",
        "Follow-up visit: Blood pressure 120/80, heart rate normal.",
        "MRI results show mild disc herniation at L4-L5 level.",
    }

    for _, note := range notes {
        payload := map[string]any{"text": note}

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

    // Search with RETRIEVAL_QUERY optimization
    searchPayload := map[string]any{
        "query": "back pain symptoms",
        "k":     int64(5),
    }

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

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

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

    // Store clinical notes with RETRIEVAL_DOCUMENT optimization
    const notes = [
        "Patient presents with acute lower back pain radiating to left leg.",
        "Follow-up visit: Blood pressure 120/80, heart rate normal.",
        "MRI results show mild disc herniation at L4-L5 level."
    ];

    for (const note of notes) {
        await client.query("addClinicalNote", { text: note });
    }

    // Search with RETRIEVAL_QUERY optimization
    const results = await client.query("searchClinicalNotes", {
        query: "back pain symptoms",
        k: 5
    });

    console.log("Search results:", results);
}

main().catch((err) => {
    console.error("Query failed:", err);
});
# Store clinical notes
curl -X POST \
  http://localhost:6969/addClinicalNote \
  -H 'Content-Type: application/json' \
  -d '{"text":"Patient presents with acute lower back pain radiating to left leg."}'

curl -X POST \
  http://localhost:6969/addClinicalNote \
  -H 'Content-Type: application/json' \
  -d '{"text":"Follow-up visit: Blood pressure 120/80, heart rate normal."}'

curl -X POST \
  http://localhost:6969/addClinicalNote \
  -H 'Content-Type: application/json' \
  -d '{"text":"MRI results show mild disc herniation at L4-L5 level."}'

# Search clinical notes
curl -X POST \
  http://localhost:6969/searchClinicalNotes \
  -H 'Content-Type: application/json' \
  -d '{"query":"back pain symptoms","k":5}'

⚠️ Warning

Make sure to set your GEMINI_API_KEY environment variable (or OPENAI_API_KEY for OpenAI models) in the same location as your queries.hx, schema.hx, and config.hx.json files.


Best Practices

Use consistent models for the same vector type

Always use the same embedding model for all queries that operate on the same vector type. Different models produce vectors with different dimensions, which will cause errors.

// Good - same model, different task types
#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY storeDoc (text: String) => ...

#[model("gemini:gemini-embedding-001:RETRIEVAL_QUERY")]
QUERY searchDocs (query: String, k: I64) => ...

// Bad - different models for same vector type
#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY storeDoc (text: String) => ...

#[model("openai:text-embedding-3-small")]
QUERY searchDocs (query: String, k: I64) => ...
Match task types to operations

Use RETRIEVAL_DOCUMENT for operations that store data and RETRIEVAL_QUERY for operations that search data.

// Storing documents
#[model("gemini:gemini-embedding-001:RETRIEVAL_DOCUMENT")]
QUERY addDocument (content: String) =>
    doc <- AddV<Document>(Embed(content), {content: content})
    RETURN doc

// Searching documents
#[model("gemini:gemini-embedding-001:RETRIEVAL_QUERY")]
QUERY findDocuments (query: String, limit: I64) =>
    docs <- SearchV<Document>(Embed(query), limit)
    RETURN docs
Consider using the default model for simple cases

If you don’t need task-specific optimization, you can skip the #[model] macro and use the default model from your config.hx.json.

// Uses default model from config.hx.json
QUERY simpleSearch (text: String, k: I64) =>
    results <- SearchV<Document>(Embed(text), k)
    RETURN results

  • Embedding Vectors — Learn how to use the Embed function for automatic text embedding

  • Vector Search — Perform similarity search on vector data

  • MCP Macro — Expose queries as MCP endpoints for AI agents

  • AddV — Create new vector entries with embeddings