For the complete documentation index, see llms.txt. This page is also available as Markdown.

Queries

BoxLang provides the easiest way to query databases with powerful SQL execution and manipulation capabilities

A query is a BoxLang data type that represents tabular data from database operations or programmatic construction. It stores rows and columns of data along with metadata about the query execution and structure.

CFML became famous in its infancy because it was easy to query databases with a simple cfquery tag and no verbose ceremonious coding. BoxLang continues this tradition while adding modern features like functional programming operations, query metadata access, and flexible datasource configuration.

Java Interoperability: BoxLang queries implement Java's Collection<IStruct> interface, making them compatible with Java collection operations and frameworks. Each row can be treated as a Map<Key, Object> in Java code.

💻 Queries in Code

Let's explore query creation and manipulation:

// Create a query programmatically
users = queryNew(
    "id,name,email,age",
    "integer,varchar,varchar,integer",
    [
        { id: 1, name: "Alice", email: "alice@example.com", age: 30 },
        { id: 2, name: "Bob", email: "bob@example.com", age: 25 },
        { id: 3, name: "Charlie", email: "charlie@example.com", age: 35 }
    ]
);

// Access query properties
println( "Records: " & users.recordCount );
println( "Columns: " & users.columnList );
println( "First user: " & users.name[ 1 ] );

// Functional programming with queries
println( "--- Functional Operations ---" );

// Filter - get rows matching condition (returns new query)
adults = users.filter( ( row ) -> row.age >= 30 );
println( "Adults: " & adults.recordCount );

// Map - transform each row (returns array of transformed values)
emails = users.map( ( row ) -> row.email.ucase() );
println( "Emails: " & emails.toString() );

// Reduce - combine all rows into single value
totalAge = users.reduce( ( sum, row ) -> sum + row.age, 0 );
println( "Total age: " & totalAge );

// Each - perform action on each row
users.each( ( row, index ) => {
    println( "#index#: #row.name# (#row.age#)" );
} );

// Sort - order by column(s)
sortedUsers = users.sort( "age DESC, name ASC" );
println( "Sorted: " & sortedUsers.name.toList() );

Please note that all member functions can also be used as traditional query functions. However, member functions look much better for readability.

📚 Query Built-In Functions (BIFs)

BoxLang provides a comprehensive set of query BIFs organized by functionality. All query BIFs can be called as member methods on Query objects.

🔨 Creation & Conversion Functions

Function
Purpose
Example

queryNew()

Create new query

queryNew("id,name", "integer,varchar")

queryExecute()

Execute SQL query

queryExecute("SELECT * FROM users")

queryAddRow()

Add row(s)

queryAddRow(qry, {id:1, name:"John"})

queryAddColumn()

Add column

queryAddColumn(qry, "age", "integer")

🔍 Access & Retrieval Functions

Function
Purpose
Example

queryGetCell()

Get single cell value

queryGetCell(qry, "name", 1)"John"

querySetCell()

Set single cell value

querySetCell(qry, "name", "Jane", 1)

queryGetRow()

Get row as struct

queryGetRow(qry, 1){id:1, name:"John"}

querySetRow()

Set entire row values

querySetRow(qry, 1, {name:"Jane"})

queryRowData()

Get row data (alias)

queryRowData(qry, 1)

queryColumnData()

Get column as array

queryColumnData(qry, "name")["John", "Jane"]

queryColumnArray()

Get column as array

queryColumnArray(qry, "name")

📊 Metadata Functions

Function
Purpose
Example

queryRecordCount()

Get row count

queryRecordCount(qry)10

queryColumnCount()

Get column count

queryColumnCount(qry)5

queryColumnList()

Get column names

queryColumnList(qry)"id,name,email"

queryColumnExists()

Check if column exists

queryColumnExists(qry, "age")true

queryKeyExists()

Check if key exists

queryKeyExists(qry, "name")true

queryCurrentRow()

Get current row number

queryCurrentRow(qry)3

queryGetResult()

Get execution metadata

queryGetResult(qry){sql, executionTime, ...}

➕ Modification Functions

Function
Purpose
Example

queryAddRow()

Add row(s)

queryAddRow(qry, 3) adds 3 empty rows

queryDeleteRow()

Delete row

queryDeleteRow(qry, 2)

queryInsertAt()

Insert row at position

queryInsertAt(qry, 2, {id:5})

queryRowSwap()

Swap two rows

queryRowSwap(qry, 1, 3)

queryAddColumn()

Add column

queryAddColumn(qry, "age", "integer")

queryDeleteColumn()

Delete column

queryDeleteColumn(qry, "age")

queryClear()

Remove all rows

queryClear(qry)

queryAppend()

Append another query

queryAppend(qry1, qry2)

queryPrepend()

Prepend another query

queryPrepend(qry1, qry2)

🔄 Functional Programming Functions

Function
Purpose
Example

queryEach()

Execute callback for each row

queryEach(qry, (row) -> println(row.name))

queryMap()

Transform each row

queryMap(qry, (row) -> row.name.ucase())

queryFilter()

Filter rows by condition

queryFilter(qry, (row) -> row.age > 18)

queryReduce()

Reduce to single value

queryReduce(qry, (sum, row) -> sum + row.age, 0)

queryEvery()

Test if all match

queryEvery(qry, (row) -> row.age >= 18)

querySome()

Test if any match

querySome(qry, (row) -> row.age > 50)

queryNone()

Test if none match

queryNone(qry, (row) -> row.age < 0)

📐 Manipulation Functions

Function
Purpose
Example

querySort()

Sort by column(s)

querySort(qry, "age DESC, name")

queryReverse()

Reverse row order

queryReverse(qry)

querySlice()

Extract portion of rows

querySlice(qry, 1, 10) → first 10 rows

🔧 Utility Functions

Function
Purpose
Example

valueList()

Column values as list

valueList(qry, "name")"John,Jane,Bob"

quotedValueList()

Quoted column values

quotedValueList(qry, "name")"'John','Jane'"

queryRegisterFunction()

Register function for QoQ

queryRegisterFunction("myFunc", myUDF)

🎯 Core Java Methods

The Query.java class provides essential methods for direct query manipulation:

Collection Interface Methods

Method
Purpose
Returns

size()

Get number of rows

int

isEmpty()

Check if query is empty

boolean

contains(Object)

Check if contains row data

boolean

iterator()

Get row iterator

Iterator<IStruct>

toArray()

Convert to array of row data

Object[]

toArrayOfStructs()

Convert to array of structs

Array

add(IStruct)

Add row as struct

boolean

remove(Object)

Remove row by object

boolean

clear()

Remove all rows

void

Query-Specific Methods

Method
Purpose
Returns

addRow(Object[])

Add row from array

int (row number)

addRow(Array)

Add row from Array

int

addRow(IStruct)

Add row from struct

int

addRows(int)

Add N empty rows

int (last row)

addEmptyRow()

Add one empty row

int

deleteRow(int)

Delete row at index

Query

swapRow(int, int)

Swap two rows

Query

getRow(int)

Get row as array

Object[]

getRowAsStruct(int)

Get row as struct

IStruct

getCell(Key, int)

Get cell value

Object

setCell(Key, int, Object)

Set cell value

Query

Column Methods

Method
Purpose
Returns

addColumn(Key, QueryColumnType)

Add column

Query

addColumn(Key, QueryColumnType, Object[])

Add column with data

Query

deleteColumn(Key)

Delete column

void

getColumn(Key)

Get QueryColumn object

QueryColumn

getColumnMeta(Key)

Get column metadata

IStruct

getColumnMeta()

Get all column metadata

IStruct

getColumnData(Key)

Get column data as array

Object[]

getColumnDataAsArray(Key)

Get column as BoxLang Array

Array

getColumnIndex(Key)

Get column position

int

getColumnList()

Get column names as string

String

getColumnArray()

Get column names as Array

Array

getColumnNames()

Get column names as Array

Array

hasColumn(Key)

Check if column exists

boolean

hasColumns()

Check if query has columns

boolean

Metadata & Duplication

Method
Purpose
Returns

getMetaData()

Get query metadata

IStruct

setMetadata(IStruct)

Set query metadata

Query

duplicate()

Shallow copy of query

Query

duplicate(boolean)

Shallow/deep copy

Query

duplicate(IBoxContext)

Context-aware copy

Query

toUnmodifiable()

Create immutable copy

UnmodifiableQuery

Advanced Methods

Method
Purpose
Returns

sort(Comparator<IStruct>)

Sort with comparator

void

sortData(Comparator<Object[]>)

Sort row arrays directly

void

truncate(long)

Keep only N rows

Query

insertQueryAt(int, Query)

Insert query at position

Query

fromResultSet(BoxStatement, ResultSet)

Create from JDBC ResultSet

Query (static)

fromArray(Array, Array, Object)

Create from arrays

Query (static)

📖 What is a Query?

A query is a request to a database representing the results' rows and columns. It returns a BoxLang query object containing a record set and other metadata information about the query. The query can ask for information from the database, write new data to the database, update existing information in the database, or delete records from the database. This can be done in several ways:

  • Using the bx:query component in script: bx:query name="qItems" { SELECT * FROM table }

  • Using the queryExecute() function: queryExecute("SELECT * FROM table")

Here's an example of using the default datasource in script syntax:

🔌 Datasource Configuration

BoxLang provides flexible datasource configuration options at multiple levels:

Configuration Locations

  1. Global - boxlang.json (runtime-wide)

  2. Application - Application.bx (per-application)

  3. Inline - Query-level (ad-hoc connections)

Basic Datasource Structure

Supported Database Drivers

Driver
Example URL

MySQL

jdbc:mysql://localhost:3306/mydb

PostgreSQL

jdbc:postgresql://localhost:5432/mydb

Microsoft SQL Server

jdbc:sqlserver://localhost:1433;databaseName=mydb

Oracle

jdbc:oracle:thin:@localhost:1521:XE

Derby

jdbc:derby:memory:mydb;create=true

H2

jdbc:h2:mem:mydb

HyperSQL

jdbc:hsqldb:mem:mydb

Connection Pool Options (HikariCP)

Default Datasource

Inline Datasource

https://github.com/ortus-boxlang/boxlang-docs/blob/v1.x/boxlang-language/datasources.mdJDBC & Databases

🔄 Iterating Over Queries

The query object can be iterated on like a normal collection through various looping constructs:

Each with Closure

Traditional Index Loop

Array Notation Access

🚀 Multi-Threaded Looping

BoxLang allows you to leverage each() operations in a multi-threaded fashion. The queryEach() and each() functions support parallel and maxThreads arguments so iteration can happen concurrently:

Example:

Limitation: This approach uses a single thread executor per execution and does not provide exception handling across threads. For production-grade parallel processing, consider BoxLang async programming features instead.

⚡ BoxLang Async Programming (Recommended for Parallel Operations)

For a more flexible approach to parallel programming, use BoxLang async programming constructs built on Java Concurrency and CompletableFuture.

Async Programming

🔒 Using Query Parameters (Preventing SQL Injection)

When using user input in queries, you must prevent SQL injection attacks. BoxLang provides query parameters for safe SQL execution.

Named Parameters (Recommended)

Positional Parameters

Using bx:queryParam Component

📋 Available SQL Types

The sqltype parameter binds values to specific database types for security and query plan optimization:

Type
Description
Example

bigint

64-bit integer

-9223372036854775808 to 9223372036854775807

bit

Boolean/bit value

true / false

char

Fixed-length string

"ABC " (padded)

varchar

Variable-length string

"Hello World"

nchar

Fixed-length Unicode string

"ABC "

nvarchar

Variable-length Unicode string

"Hello 世界"

longvarchar

Long text

Long text content

longnvarchar

Long Unicode text

Long Unicode content

integer

32-bit integer

-2147483648 to 2147483647

smallint

16-bit integer

-32768 to 32767

tinyint

8-bit integer

-128 to 127

numeric

Fixed precision decimal

123.45

decimal

Fixed precision decimal

123.45

float

Floating point

123.456789

double

Double precision float

123.456789012345

real

Single precision float

123.456

money

Currency value

1234.56

money4

Small currency value

214748.3647

date

Date only

2025-12-09

time

Time only

14:30:00

timestamp

Date and time

2025-12-09 14:30:00

blob

Binary large object

Binary data

clob

Character large object

Large text

nclob

Unicode large object

Large Unicode text

sqlxml

XML data

<root><item /></root>

refcursor

Result set reference

Oracle REF CURSOR

idstamp

Unique identifier

UUID/GUID

🏗️ Building Queries Programmatically

You can create and manipulate queries without database connections using BoxLang query construction functions:

Creating Empty Queries

Creating Queries with Data

Manipulating Query Data

Method Chaining

🔍 Query of Queries (QoQ)

Query existing query objects using SQL without hitting the database. BoxLang's QoQ implementation is extremely fast - 5x faster than Lucee and 17x faster than Adobe ColdFusion.

Advanced QoQ Features

BoxLang QoQ supports modern SQL features:

For complete QoQ documentation including custom functions, bitwise operators, and performance tips, see Query of Queries.

📦 Alternative Return Types

You can return query results as arrays or structs instead of query objects - perfect for JSON APIs and modern frameworks.

Return as Array of Structs

Return as Struct of Structs

Convert Existing Query

🔄 Query Transformers — Custom Result Formatting

Since BoxLang 1.14.0. Query Transformers let you process query result sets natively and return exactly what you need — eliminating boilerplate post-processing.

BoxLang's queryExecute() and bx:query are locked into three hardcoded return types: query, array, and struct. Users who want tabular arrays, rich column descriptors, JSON, domain objects, or any other format must post-process results in a separate step — doubling memory and CPU for large queries. Adding new native return types for every use case is unsustainable.

Query Transformers solve this by allowing you to provide a transformer option that receives the raw query and execution metadata, giving you complete control over the result format.

Transformer input types:

  1. Closure/Lambda(query, metadata) => any or (query, metadata) → any

  2. Class instance — any class with a transform(query, metadata) method

  3. String — name of a registered transformer from this.queryTransformers in Application.bx

When transformer is provided, it takes precedence over returnType. The transformer receives two arguments:

  • query — the raw Query object (.recordCount, .toArrayOfStructs(), .getData(), .getColumnNames(), .getColumnMeta(), etc.)

  • metadata — a struct containing sql, parameters, executionTime, columnMetadata, and more

Inline Closure Transformers

Custom Struct with Metadata:

Domain Objects:

Tabular Format (Near Zero-Copy):

Rich Format with Column Descriptors:

Class Instance Transformers

Create reusable transformer classes for complex formatting logic:

Registered Transformers (Application.bx)

Register transformers globally in your application for reuse across the entire codebase:

bx:query Component Support

Transformers also work with the bx:query component:

JDBC Metadata Enhancement

As a prerequisite for rich column descriptors, QueryColumn now captures JDBC metadata that was previously discarded after the ResultSet was closed — exposed via query.getColumnMeta():

Property
Source
Description

nullable

ResultSetMetaData.isNullable()

Whether the column accepts NULL

readOnly

ResultSetMetaData.isReadOnly() / isAutoIncrement()

Whether the column is read-only

decimals

ResultSetMetaData.getScale()

Number of decimal digits (numeric types)

maxLength

ResultSetMetaData.getColumnDisplaySize()

Maximum column width (string types)

🏗️ QB - Query Builder Module

QB (Query Builder) is a powerful module for building database queries with a fluent, chainable API. It abstracts database differences and makes complex queries readable and maintainable.

Installation

Features

  • ✅ Fluent, chainable query building

  • ✅ Database-agnostic (MySQL, PostgreSQL, MSSQL, Oracle, etc.)

  • ✅ Complex joins, subqueries, and CTEs

  • ✅ Query caching and pagination

  • ✅ Raw expressions when needed

  • ✅ Schema builder for migrations

Basic Usage

Insert, Update, Delete

📖 Full Documentation: https://qb.ortusbooks.com/

⚙️ Query Options

BoxLang supports comprehensive query options for controlling execution behavior:

Core Options

Option
Type
Description
Example

result

String

Variable name to store query metadata

result: "queryResult"

maxRows

Integer

Limit number of rows returned

maxRows: 100

queryTimeout

Integer

Max execution time (seconds)

queryTimeout: 30

returnType

String

Return format: query, array, struct

returnType: "array"

columnKey

String

Key column for struct return type

columnKey: "id"

fetchSize

Integer

JDBC batch size for large results

fetchSize: 500

dbtype

String

Database type ("query" for QoQ)

dbtype: "query"

Caching Options

Option
Type
Description
Example

cache

Boolean

Enable query caching

cache: true

cacheKey

String

Unique cache identifier

cacheKey: "userList"

cacheProvider

String

Cache provider name

cacheProvider: "default"

cacheTimeout

Duration

Cache expiration time

cacheTimeout: "1h"

CFML Option
BoxLang Equivalent

blockfactor

fetchSize

cacheID

cacheKey

cacheRegion

cacheProvider

cachedAfter

Converted to cacheTimeout

cachedWithin

cacheTimeout

Future Options: Support is planned for cachedWithin="request", timezone, psq, and lazy loading.

Last updated

Was this helpful?