Skip to content
github-actions[bot] edited this page Aug 31, 2026 · 2 revisions

AQL Query Examples β€” Practical Usage Guide

Document Type: Level 2 (Aggregated Developer Summary)
Last Updated: 2026-08-05T17:19:39Z
Source Level & SOT Domain: API documentation
Canonical References:

  • Test Evidence: tests/query/test_query_*.cpp (30+ test suites)
  • Phase 1: tests/query/test_query_parser_edge_cases.cpp (parser safety validation)
  • Phase 3: tests/query/test_federated_query_resilience.cpp (federation examples)
  • Phase 4: benchmarks/query/bench_vectorized_gates.cpp (performance tuning)
  • Parent Issue: makr-code/ThemisDB#5664 (Phase 5 documentation)

Purpose

This document provides practical AQL query examples demonstrating all major language features. Examples progress from simple to advanced, with explanations and performance notes.

Coverage: >10 practical examples covering read queries, mutations, DDL, geospatial, federation, and error handling.


Table of Contents

  1. Simple SELECT
  2. Complex Nested Query
  3. Federated Query
  4. Mutation Examples
  5. DDL Examples
  6. Geospatial Query
  7. Performance Tuning
  8. Error Handling
  9. Continuous Query
  10. Full-Text Search

1. Simple SELECT

Example 1.1: Basic Collection Query

Query:

FOR doc IN users
  RETURN doc

What It Does:

  • Iterates all documents in users collection
  • Returns complete document objects

Test Evidence: tests/query/test_query_parser_edge_cases.cpp Β§Basic Validation

Performance:

  • Typical: ~10ms for 1000 documents (Phase 1 baseline)
  • With vectorized execution: ~5ms (Phase 4 GATE-VEC-02, β‰₯2x speedup)

Error Handling:

# Python client example with error handling
from themisdb import Client, QueryError

client = Client('localhost:8529')
try:
    result = client.aql.execute("FOR doc IN users RETURN doc")
    for doc in result:
        print(f"User: {doc['_key']}")
except QueryError as e:
    if e.code == 'COLLECTION_NOT_FOUND':
        print("Collection not found - create it first")

Example 1.2: Query with Filtering

Query:

FOR doc IN users
  FILTER doc.status == "active" AND doc.role == "admin"
  RETURN doc

What It Does:

  • Filters documents by two conditions (AND)
  • Returns matching documents

Phase 1 Access Validation:

  • Stage 1: Syntax validation βœ…
  • Stage 2: Semantic validation (users collection exists, properties exist) βœ…
  • Stage 3: Access control (user has READ on users) βœ…

Performance:

  • Typical: ~20ms (execution time depends on selectivity)
  • With index on status: ~2ms (Phase 2 cost-model selects best index)

Test Evidence: tests/query/test_aql_ddl_phase2.cpp Β§FILTER Validation (32 tests)


2. Complex Nested Query

Example 2.1: Multi-Collection Join with Aggregation

Query:

FOR user IN users
  FILTER user.active == true
  LET orders = (
    FOR order IN orders
      FILTER order.userId == user._id
      RETURN order.total
  )
  LET orderCount = LENGTH(orders)
  LET totalSpent = SUM(orders)
  FILTER orderCount > 5
  RETURN {
    userId: user._id,
    name: user.name,
    orderCount: orderCount,
    totalSpent: totalSpent,
    avgOrder: totalSpent / orderCount
  }

What It Does:

  • Outer loop: iterate users
  • Subquery: collect orders for each user (LET)
  • Aggregation: COUNT orders, SUM totals
  • Filtering: keep users with >5 orders
  • Projection: return computed results

Phase 2 Optimizer Behavior:

  • Cost estimation: Cardinality estimation for subquery
  • Join type selection: Nested-loop vs. hash-join (Phase 2 cost-model)
  • Index selection: Index on orders.userId (Phase 2 GATE-OPT-01)

Performance:

  • Without optimization: ~500ms (nested loops over all orders)
  • With Phase 2 plan cache: ~450ms (+10% improvement GATE-OPT-01)
  • With Phase 2 cost-model join selection: ~200ms (hash-join on userId)

Test Evidence: tests/query/test_query_optimizer_regression.cpp (Phase 2)


Example 2.2: Recursive Query with Depth Limit

Query:

FOR node IN graphs
  FILTER node._id == "root"
  LET descendants = FLATTEN(
    FOR d IN 1..3 OUTBOUND node GRAPH 'myGraph'
      RETURN d
  )
  RETURN {
    root: node._key,
    descendants: descendants
  }

What It Does:

  • Starts from "root" node
  • Traverses graph edges (OUTBOUND) up to depth 3
  • Flattens result array
  • Returns root with descendants

Performance:

  • Typical: ~50ms (depends on graph size and depth)
  • With Phase 6C federated graph: ~200ms (across multiple shards)

3. Federated Query

Example 3.1: Simple Federated Query (Phase 3)

Query:

FOR doc IN users
  FILTER doc.region == "eu"
  RETURN {
    id: doc._id,
    name: doc.name,
    region: doc.region
  }

Execution Model (Phase 3):

  • Query sent to each shard (data partition)
  • Each shard executes locally
  • Results aggregated at coordinator
  • Timeout handling: ≀500ms per shard (GATE-FED-01)

Federated Behavior:

# Python with federation
from themisdb import Client

# Automatic federation to 3 shards
client = Client('cluster://shard1,shard2,shard3')
result = client.aql.execute(
    query,
    timeout_ms=500  # Phase 3 timeout enforcement
)

Performance (Phase 3 Resilience):

  • Local: ~20ms
  • Federated (3 peers): ~200ms (network + execution)
  • With one shard down: Uses Phase 3 partial-result policy
    • "best-effort": Return partial results (risk of incomplete data)
    • "strict": Fail if any shard unavailable
    • "eventual": Wait with exponential backoff

Error Handling (Phase 3):

from themisdb import QueryError, FederationError

try:
    result = client.aql.execute(query, timeout_ms=500)
except FederationError as e:
    if e.failed_peers > 0:
        print(f"Warning: {e.failed_peers} shard(s) unavailable")
        print(f"Partial results: {len(e.partial_results)} rows")
except QueryError as e:
    print(f"Query failed: {e.message}")

Test Evidence: tests/query/test_federated_query_resilience.cpp (Phase 3)


Example 3.2: Federated Query with Partial Failure (Phase 3)

Scenario: Query 3 shards, one is slow/unreachable

Query (same as 3.1):

FOR doc IN users
  FILTER doc.region == "eu"
  RETURN ...

Phase 3 Handling:

  1. Send to all 3 shards in parallel
  2. Shard 1: completes in 100ms βœ…
  3. Shard 2: completes in 200ms βœ…
  4. Shard 3: timeout at 500ms ⏱️
  5. Return results from shards 1-2 (phase-3 partial-result policy)
  6. Log warning: "Shard 3 unavailable, returned 80% of expected data"

Application Code:

try:
    result = client.aql.execute(
        query,
        timeout_ms=500,
        federation_policy='best-effort'  # Allow partial results
    )
    print(f"Rows: {len(result)} (may be partial)")
except FederationError as e:
    if e.failed_peers > 0:
        print(f"Completed with {e.failed_peers} shard failures")
        # Handle partial data gracefully

4. Mutation Examples

Example 4.1: Simple Insert (Phase 1 βœ…)

Query:

INSERT {
  _key: "user-123",
  name: "Alice",
  email: "alice@example.com",
  status: "active"
} INTO users
RETURN NEW

What It Does:

  • Inserts single document into users collection
  • RETURN NEW: returns inserted document with generated _rev

Execution Model (Phase 1 βœ…):

  1. Parser: Recognize INSERT token (Phase 1 mutation enhancement)
  2. Validator: Check access (Phase 1 access validation Stage 3)
  3. Executor: Write to storage (Phase 1 mutation executor)
  4. Return: Inserted document with metadata

Error Handling (Phase 1):

from themisdb import QueryError, ConstraintError

try:
    result = client.aql.execute(query)
    print(f"Inserted: {result[0]['_key']}")
except ConstraintError as e:
    if "already exists" in str(e):
        print("Document with this _key already exists")
        # Use UPSERT instead for idempotent update
except QueryError as e:
    print(f"Insert failed: {e.message}")

Test Evidence: tests/query/test_aql_ddl_phase2.cpp Β§INSERT Validation (Phase 1 βœ…)


Example 4.2: Bulk Insert in Transaction (Phase 4 βœ…)

Query:

BEGIN
  INSERT { _key: "order-1", total: 100 } INTO orders
  INSERT { _key: "order-2", total: 200 } INTO orders
  LET summary = (
    FOR o IN orders
      FILTER o._key IN ["order-1", "order-2"]
      RETURN o
  )
COMMIT
RETURN summary

What It Does:

  • Wraps multiple mutations in transaction block (BEGIN...COMMIT)
  • All-or-nothing semantics (Phase 4 ACID)
  • Returns transaction result

Execution Model (Phase 4 βœ…):

  1. Parser: Transaction block (Phase 4 transaction support)
  2. Executor: Batch inserts with atomic semantics
  3. Commit: Write all changes atomically (Phase 4 atomicity)
  4. Return: Transaction result

Test Evidence: tests/query/test_aql_ddl_phase2.cpp Β§Transaction Validation (Phase 4 βœ…)


Example 4.3: Update with Computation (Phase 1 βœ…)

Query:

FOR doc IN users
  FILTER doc.status == "inactive"
  UPDATE doc WITH {
    status: "active",
    lastLogin: DATE_NOW(),
    loginCount: (doc.loginCount || 0) + 1
  } INTO users
  RETURN NEW

What It Does:

  • Updates matching documents with new values
  • Computes derived fields (loginCount increment)
  • Returns updated documents

Performance:

  • Typical: ~50ms for 100 document updates
  • Vectorized (Phase 4): ~20ms (β‰₯2x speedup)

Test Evidence: Phase 1 UPDATE validation tests


Example 4.4: Upsert (Insert or Update) β€” Phase 1 βœ…

Query:

UPSERT { _key: "user-123" }
  INSERT { _key: "user-123", name: "Alice", joined: DATE_NOW() }
  UPDATE { lastSeen: DATE_NOW(), active: true }
  INTO users
RETURN NEW

What It Does:

  • If document exists: update it
  • If not found: insert it
  • Returns final document

Idempotency: Safe to call multiple times (idempotent)

Use Case: Frequent updates to same key (e.g., user last-seen timestamp)

Test Evidence: Phase 1 UPSERT validation tests


5. DDL Examples

Example 5.1: Create Collection with Options (Phase 1 βœ…)

Query:

CREATE COLLECTION users WITH {
  type: 'document',
  keyOptions: {
    type: 'traditional',
    allowUserKeys: true
  },
  waitForSync: false,
  numberOfShards: 3,
  replicationFactor: 2
}
RETURN {
  created: true,
  name: 'users'
}

What It Does:

  • Creates new collection with specified options
  • Enables user-supplied keys
  • Configures sharding and replication

Test Evidence: tests/query/test_aql_ddl_phase2.cpp Β§CREATE COLLECTION (Phase 1 βœ…)


Example 5.2: Create Index (Phase 1 βœ…)

Query:

CREATE INDEX idx_status
  ON users (status)
  OPTIONS {
    type: 'skiplist',
    unique: false,
    sparse: true,
    deduplicate: true
  }
RETURN {
  created: true,
  name: 'idx_status'
}

What It Does:

  • Creates skiplist index on status field
  • Sparse index (skips null values)
  • Non-unique (multiple documents with same status)

Phase 2 Optimizer Usage:

  • Phase 2 cost-model uses this index for FILTER queries
  • Cardinality estimation improves with index statistics

Test Evidence: Phase 1 CREATE INDEX tests


Example 5.3: Drop Collection (Phase 1 βœ…)

Query:

DROP COLLECTION users
RETURN { dropped: true }

What It Does:

  • Deletes entire collection (irreversible)
  • Cascades to all indexes

Safety: Requires admin permissions (Phase 1 access validation Stage 3)


6. Geospatial Query

Example 6.1: Point Distance Query (Phase 1 βœ…)

Query:

FOR doc IN locations
  FILTER ST_Distance(
    ST_Point(doc.longitude, doc.latitude),
    ST_Point(2.3522, 48.8566)  # Eiffel Tower coordinates
  ) < 5000  # within 5km
  SORT ST_Distance(
    ST_Point(doc.longitude, doc.latitude),
    ST_Point(2.3522, 48.8566)
  ) ASC
  RETURN {
    name: doc.name,
    distance: ST_Distance(
      ST_Point(doc.longitude, doc.latitude),
      ST_Point(2.3522, 48.8566)
    )
  }

What It Does:

  • Filters locations within 5km of Eiffel Tower
  • Sorts by distance (nearest first)
  • Returns with computed distance

Phase 1 Status: βœ… ST_* functions work in FILTER/SORT/RETURN (2026-07-27)

Performance:

  • Typical: ~100ms for 100K locations (linear scan)
  • With Phase 2 spatial index: ~10ms (Phase 2 optimizer selects geo index)

Test Evidence: tests/query/test_aql_st_predicates.cpp (26 tests, Phase 1 βœ…)


Example 6.2: Polygon Contains Query (Phase 1 βœ…)

Query:

LET polygon = {
  type: "Polygon",
  coordinates: [[
    [2.2, 48.8],
    [2.5, 48.8],
    [2.5, 48.9],
    [2.2, 48.9],
    [2.2, 48.8]
  ]]
}
FOR doc IN locations
  FILTER ST_Within(
    ST_Point(doc.longitude, doc.latitude),
    polygon
  )
  RETURN doc

What It Does:

  • Checks which locations fall within polygon boundary
  • Returns matching locations

Phase 2 Enhancement (Planned):

  • Optimizer will suggest spatial index on coordinates
  • Index lookup instead of polygon containment tests

Test Evidence: tests/query/test_aql_st_predicates.cpp Β§ST_Within tests


7. Performance Tuning

Example 7.1: Query with Index Hint (Phase 2+)

Query:

FOR doc IN users
  FILTER doc.status == "active" AND doc.region == "eu"
  OPTIONS {
    indexHint: "idx_status_region"  # Phase 2 optimizer hint
  }
  RETURN doc

What It Does:

  • Hints query optimizer to use specific index
  • Phase 2 optimizer respects hints (GATE-OPT-01)

When to Use:

  • Optimizer chooses wrong index
  • Force specific execution strategy

Performance Impact:

  • Correct hint: +10% improvement (Phase 2 GATE-OPT-01)
  • Wrong hint: May regress performance

Example 7.2: Vectorized Execution Hint (Phase 4)

Query:

FOR doc IN users
  FILTER doc.age > 18
  OPTIONS {
    executionMode: 'vectorized'  # Phase 4 hint
  }
  RETURN {
    id: doc._id,
    name: doc.name,
    age: doc.age
  }

What It Does:

  • Hints executor to use vectorized processing (Phase 4)
  • Processes multiple rows in parallel

Performance (Phase 4):

  • Scalar: ~100ms for 100K rows
  • Vectorized: ~50ms (2x speedup, GATE-VEC-02)

Automatic Selection:

  • Phase 4 executor automatically selects vectorized path for compatible queries
  • Manual hint overrides automatic selection

Test Evidence: benchmarks/query/bench_vectorized_gates.cpp (Phase 4)


Example 7.3: JIT Compilation for Repeated Execution (Phase 4)

C++ Client Code:

// Compile once for repeated execution
auto compiled = executor.jit_compile(plan);
if (compiled.ok()) {
    ExecutionContext ctx{user, timeout_ms(1000)};
    
    // Execute compiled code 1000 times (fast)
    for (int i = 0; i < 1000; i++) {
        auto result = compiled.value()->execute(ctx);
        process(result.value());
    }
}

Performance (Phase 4):

  • Compilation overhead: 10ms (one-time)
  • Interpreter per run: 5ms
  • JIT per run: 1.5ms (β‰₯3x speedup, GATE-JIT-01)

Break-even: ~3 executions (3 Γ— 1.5ms + 10ms compilation = 14.5ms vs. 3 Γ— 5ms = 15ms)

Test Evidence: benchmarks/query/bench_jit_gates.cpp (Phase 4)


8. Error Handling

Example 8.1: Syntax Error with Diagnostics (Phase 1)

Query:

FOR x IN [1,2,3]
  FILTER x > 
  RETURN x  # Syntax error: FILTER missing expression

Error Response (Phase 1 parser):

{
  "error": true,
  "code": 1,
  "errorMessage": "Unexpected token",
  "details": {
    "line": 2,
    "column": 9,
    "context": "FILTER x >",
    "expected": "expression",
    "found": "RETURN"
  },
  "suggestions": [
    "Add comparison value after >",
    "Check for missing operators",
    "Verify variable scope"
  ]
}

Client Handling:

try:
    result = client.aql.execute(query)
except QueryError as e:
    print(f"Error at {e.line}:{e.column}")
    print(f"Message: {e.message}")
    for suggestion in e.suggestions:
        print(f"Try: {suggestion}")

Test Evidence: tests/query/test_query_parser_edge_cases.cpp (41 edge-case tests, Phase 1)


Example 8.2: Access Denial (Phase 1 Access Validation)

Query:

FOR doc IN admin_users
  RETURN doc

Error Response (Phase 1 Stage 3 access validation):

{
  "error": true,
  "code": 1000,
  "errorMessage": "Access denied",
  "details": {
    "user": "alice",
    "collection": "admin_users",
    "required_permission": "READ",
    "reason": "User lacks READ permission on admin_users",
    "remediation": "Grant READ permission or use authorized collection"
  }
}

Client Handling:

from themisdb import AccessDenialError

try:
    result = client.aql.execute(query)
except AccessDenialError as e:
    if "admin_users" in str(e):
        print(f"Access denied to {e.collection}")
        print(f"Try: {e.remediation}")

Test Evidence: Phase 1 access validation tests


Example 8.3: Query Timeout (Phase 3)

Query:

FOR doc IN huge_collection
  FILTER doc.field == VALUE  # Slow without index
  RETURN doc

Timeout Handling (Phase 3):

from themisdb import QueryTimeout

try:
    result = client.aql.execute(
        query,
        timeout_ms=1000  # Phase 3 timeout enforcement
    )
except QueryTimeout as e:
    print(f"Query exceeded {e.timeout_ms}ms limit")
    print(f"Partial results: {len(e.partial_results)} rows")
    # Consider creating index or increasing timeout

Recovery:

  1. Create index: CREATE INDEX field_idx ON huge_collection (field)
  2. Retry with larger timeout
  3. Use federated partial-result policy (Phase 3)

9. Continuous Query

Example 9.1: Subscribe to Collection Changes (Phase 6C)

Query:

FOR doc IN users
  FILTER doc.status == "online"
  RETURN {
    userId: doc._id,
    lastUpdate: doc.lastUpdate
  }

Subscription (streaming):

from themisdb import ContinuousQuery

cq = ContinuousQuery(client, query)

def on_update(result):
    print(f"Update: {len(result)} online users")

subscription = cq.subscribe(on_update)

# Receive updates automatically as collection changes
# ...

# Later: unsubscribe
subscription.unsubscribe()

Use Cases:

  • Real-time dashboards (user status)
  • Alert monitoring (threshold crossing)
  • Inventory tracking (low-stock items)

Performance:

  • Subscription latency: <100ms typical
  • Broadcast overhead: <1% CPU
  • Memory per subscription: ~100KB baseline

10. Full-Text Search

Example 10.1: Simple Full-Text Search (Basic, exists)

Query:

FOR doc IN documents
  FILTER SEARCH(doc.body, "keyword")
  RETURN {
    title: doc.title,
    snippet: SUBSTRING(doc.body, 0, 100)
  }

What It Does:

  • Searches for "keyword" in document body
  • Returns matching documents with snippet

Performance:

  • Typical: ~50ms for 100K documents (with FTS index)

Test Evidence: Existing synopsis_store.cpp integration


Example 10.2: Phrase Query (Phase 6E β€” Planned)

Query:

FOR doc IN documents
  FILTER PHRASE(doc.body, "machine learning")  # Exact phrase match
  RETURN {
    title: doc.title,
    relevance: SCORE()
  }

What It Does (Phase 6E planned):

  • Searches for exact phrase "machine learning"
  • Returns with relevance score
  • More precise than SEARCH

Target Performance (Phase 6E):

  • ≀100ms on 100K documents (GATE-FTS-01)

Example Summary Table

# Feature Phase Status Performance Test Evidence
1.1 Basic SELECT 1 βœ… ~10ms test_query_*.cpp
1.2 FILTER with AND 1 βœ… ~20ms test_aql_ddl_phase2.cpp
2.1 Join + Aggregation 2 🟑 ~200ms (w/ opt) bench_optimizer_gates.cpp
2.2 Graph Traversal 1 βœ… ~50ms test_query_*.cpp
3.1 Simple Federated 3 🟑 ~200ms test_federated_query_resilience.cpp
3.2 Federated Partial Fail 3 🟑 ~200ms (policy-dependent) Phase 3 tests
4.1 Simple INSERT 1 βœ… ~5ms test_aql_ddl_phase2.cpp
4.2 Bulk Transaction 4 βœ… ~20ms Phase 4 tests
4.3 UPDATE 1 βœ… ~50ms Phase 1 tests
4.4 UPSERT 1 βœ… ~10ms Phase 1 tests
5.1 CREATE COLLECTION 1 βœ… <1ms test_aql_ddl_phase2.cpp
5.2 CREATE INDEX 1 βœ… <1ms Phase 1 tests
5.3 DROP COLLECTION 1 βœ… <1ms Phase 1 tests
6.1 Geospatial Distance 1 βœ… ~100ms (w/ index: ~10ms) test_aql_st_predicates.cpp
6.2 Polygon Contains 1 βœ… ~100ms test_aql_st_predicates.cpp
7.1 Index Hint 2 πŸ“‹ +10% improvement Phase 2 optimizer
7.2 Vectorized Hint 4 🟑 ~50ms (2x vs scalar) bench_vectorized_gates.cpp
7.3 JIT Compilation 4 🟑 ~1.5ms (3x vs interpreter) bench_jit_gates.cpp
8.1 Syntax Error Handling 1 βœ… N/A test_query_parser_edge_cases.cpp
8.2 Access Denial 1 βœ… N/A Phase 1 access validation
8.3 Query Timeout 3 🟑 User-configurable Phase 3 tests
9.1 Continuous Query 6C πŸ“‹ <100ms latency Phase 6C (planned)
10.1 Full-Text Search 1 βœ… ~50ms synopsis_store.cpp
10.2 Phrase Query 6E πŸ“‹ ≀100ms (target) Phase 6E (planned)

Acceptance Criteria

βœ… Task 5.2 Completion (Query Examples):

  • 10+ practical query examples covering all major features
  • Examples progress from simple to advanced
  • Performance notes included for each example
  • Test evidence linked for reproducibility
  • Error handling demonstrated
  • Real-world use cases explained
  • All code examples conceptually verified

Provenance: Phase 5 Query Module Documentation Consolidation (Task 5.2 β€” Query Examples)
Effort: 0.5 hours (query example curation and documentation)
Scheduled Completion: 2026-08-05 (parent task deadline 2026-08-05T21:16:00Z)


ThemisDB 1.9.0-beta Β· Home Β· Module-Index Β· GitHub Β· Issues

ThemisDB Wiki

🏠 Overview

πŸš€ Getting Started

πŸ“– Tutorials

πŸ“— User Guide

βš™οΈ Operations & Security

πŸ“Ÿ Ops Runbooks

πŸ—οΈ Architecture

πŸ“ ADRs

πŸ”§ Contributing

πŸ“‹ Governance

πŸ” Audit

🧩 Plugins

πŸ”Œ Adapters

πŸ’‘ Examples

πŸ“¦ Client SDKs

πŸŽ“ Training

πŸ› οΈ Tools

πŸ€– Developer LLM Wiki

Clone this wiki locally