or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

administration.mdaql-queries.mddatabase-connection.mddocument-collections.mdgraph-operations.mdindex.mdquery-execution.mdtransactions.mdviews-search.md
tile.json

aql-queries.mddocs/

AQL Queries

AQL (ArangoDB Query Language) template system for building type-safe, injection-protected database queries with comprehensive support for bind parameters and query composition.

Capabilities

AQL Template String Handler

Main template string handler for creating safe AQL queries with automatic bind parameter handling.

/**
 * Template string handler for AQL queries with automatic bind parameter handling
 * @param templateStrings - Template string array
 * @param args - Values to interpolate (collections, literals, primitives)
 * @returns AQL query object with query string and bind parameters
 */
function aql<T = any>(
  templateStrings: TemplateStringsArray,
  ...args: AqlValue[]
): AqlQuery<T>;

/**
 * Generic AQL query object
 */
interface AqlQuery<T = any> {
  query: string;
  bindVars: Record<string, any>;
}

/**
 * Values that can be used in AQL template strings
 */
type AqlValue = 
  | ArangoCollection
  | View
  | Graph
  | GeneratedAqlQuery
  | AqlLiteral
  | string
  | number
  | boolean
  | null
  | undefined
  | Record<string, any>
  | any[];

Usage Examples:

import { Database, aql } from "arangojs";

const db = new Database();
const users = db.collection("users");
const posts = db.collection("posts");

// Basic AQL query with bind parameters
const activeUsers = await db.query(aql`
  FOR user IN ${users}
  FILTER user.active == ${true}
  RETURN user
`);

// Complex query with multiple collections
const userPosts = await db.query(aql`
  FOR user IN ${users}
  FOR post IN ${posts}
  FILTER post.authorId == user._key
  FILTER user.active == ${true}
  RETURN {
    user: user.name,
    post: post.title,
    created: post.createdAt
  }
`);

// Query with dynamic filtering
const status = "published";
const limit = 10;
const recentPosts = await db.query(aql`
  FOR post IN ${posts}
  FILTER post.status == ${status}
  SORT post.createdAt DESC
  LIMIT ${limit}
  RETURN post
`);

Query Composition and Literals

Functions for composing complex queries and creating trusted literals.

/**
 * Creates a trusted AQL literal that will be inlined directly
 * @param value - Value to convert to literal (string, number, boolean, null, undefined)
 * @returns AQL literal object
 */
function literal(
  value: string | number | boolean | AqlLiteral | null | undefined
): AqlLiteral;

/**
 * Constructs AQL queries from arrays of values
 * @param values - Array of values to join (behave like aql template interpolation)
 * @param sep - Separator to insert between values (inlined as literal)
 * @returns Combined AQL query
 */
function join(values: AqlValue[], sep?: string): AqlQuery;

/**
 * Object representing a trusted AQL literal for direct inlining
 */
interface AqlLiteral {
  toAQL(): string;
}

Usage Examples:

// Using literals (use sparingly - aql templates are safer)
const sortDirection = literal("DESC");
const query1 = aql`
  FOR doc IN ${collection}
  SORT doc.created ${sortDirection}
  RETURN doc
`;

// Better approach with nested aql templates
const sortClause = aql`DESC`;
const query2 = aql`
  FOR doc IN ${collection}
  SORT doc.created ${sortClause}
  RETURN doc
`;

// Using join for dynamic arrays
const filters = [];
if (includeActive) {
  filters.push(aql`FILTER doc.active == true`);
}
if (includePublished) {
  filters.push(aql`FILTER doc.status == "published"`);
}

const dynamicQuery = aql`
  FOR doc IN ${collection}
  ${join(filters)}
  RETURN doc
`;

// Complex composition example
const userFilters = [
  aql`FILTER user.active == true`,
  aql`FILTER user.verified == true`
];

const postFilters = [
  aql`FILTER post.published == true`,
  aql`FILTER post.createdAt > ${dateThreshold}`
];

const complexQuery = aql`
  FOR user IN ${users}
  ${join(userFilters)}
  FOR post IN ${posts}
  ${join(postFilters)}
  FILTER post.authorId == user._key
  RETURN {
    user: user.name,
    post: post.title,
    tags: post.tags
  }
`;

Type Guards and Utilities

Helper functions for working with AQL query objects and types.

/**
 * Checks if a value is an AQL query object
 * @param query - Value to check
 * @returns True if value is AqlQuery
 */
function isAqlQuery(query: any): query is AqlQuery;

/**
 * Checks if a value is a generated AQL query (from template or helpers)
 * @param query - Value to check
 * @returns True if value is GeneratedAqlQuery
 * @internal
 */
function isGeneratedAqlQuery(query: any): query is GeneratedAqlQuery;

/**
 * Checks if a value is an AQL literal
 * @param literal - Value to check
 * @returns True if value is AqlLiteral
 */
function isAqlLiteral(literal: any): literal is AqlLiteral;

Usage Examples:

// Type checking utilities
const query = aql`FOR doc IN ${collection} RETURN doc`;

if (isAqlQuery(query)) {
  console.log("Query string:", query.query);
  console.log("Bind vars:", query.bindVars);
}

const literal = literal("ASC");
if (isAqlLiteral(literal)) {
  console.log("Literal AQL:", literal.toAQL());
}

// Using in conditional logic
function executeQuery(queryOrString: string | AqlQuery) {
  if (isAqlQuery(queryOrString)) {
    return db.query(queryOrString);
  } else {
    return db.query({ query: queryOrString, bindVars: {} });
  }
}

Advanced Query Patterns

Common patterns for building complex AQL queries with proper type safety.

Dynamic Filtering:

function buildUserQuery(filters: {
  active?: boolean;
  role?: string;
  createdAfter?: Date;
}) {
  const conditions = [];
  
  if (filters.active !== undefined) {
    conditions.push(aql`FILTER user.active == ${filters.active}`);
  }
  
  if (filters.role) {
    conditions.push(aql`FILTER user.role == ${filters.role}`);
  }
  
  if (filters.createdAfter) {
    conditions.push(aql`FILTER user.createdAt > ${filters.createdAfter}`);
  }
  
  return aql`
    FOR user IN ${users}
    ${join(conditions)}
    RETURN user
  `;
}

// Usage
const adminUsers = await db.query(buildUserQuery({
  active: true,
  role: "admin"
}));

Subquery Composition:

// Build subqueries separately
const activeUsersSubquery = aql`
  FOR user IN ${users}
  FILTER user.active == true
  RETURN user
`;

const userPostsQuery = aql`
  FOR user IN (${activeUsersSubquery})
  FOR post IN ${posts}
  FILTER post.authorId == user._key
  RETURN {
    user: user.name,
    post: post.title
  }
`;

const results = await db.query(userPostsQuery);

Aggregation Queries:

const aggregationQuery = aql`
  FOR post IN ${posts}
  FILTER post.createdAt > ${startDate}
  COLLECT 
    author = post.authorId,
    month = DATE_FORMAT(post.createdAt, "%Y-%m")
  AGGREGATE 
    totalPosts = COUNT(),
    totalViews = SUM(post.views)
  RETURN {
    author,
    month,
    totalPosts,
    totalViews,
    avgViews: totalViews / totalPosts
  }
`;

Types

interface AqlQuery<T = any> {
  query: string;
  bindVars: Record<string, any>;
}

interface GeneratedAqlQuery<T = any> extends AqlQuery<T> {
  _source(): { strings: string[]; args: AqlValue[] };
}

interface AqlLiteral {
  toAQL(): string;
}

type AqlValue = 
  | ArangoCollection
  | View
  | Graph  
  | GeneratedAqlQuery
  | AqlLiteral
  | string
  | number
  | boolean
  | null
  | undefined
  | Record<string, any>
  | any[];