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.
All BoxLang queries are passed to functions as memory references, not values. Keep that in mind when working with queries. There is also the passby=reference|value attribute to function arguments where you can decide whether to pass by reference or value.
📋 Table of Contents
💻 Queries in Code
Let's explore query creation and manipulation:
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
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
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
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
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
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
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
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
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
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
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
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
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:querycomponent 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
Global -
boxlang.json(runtime-wide)Application -
Application.bx(per-application)Inline - Query-level (ad-hoc connections)
Basic Datasource Structure
Supported Database Drivers
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:
For-In Loop (Recommended)
Each with Closure
Traditional Index Loop
Array Notation Access
Best Practice: Use for-in loops or each() for cleaner, more readable code. Reserve index-based loops for when you need precise position control.
🚀 Multi-Threaded Looping
BoxLang allows you to leverage the each() operations in a multi-threaded fashion. The queryEach() or each() functions allow for parallel and maxThreads arguments so the iteration can happen concurrently:
Example:
Thread Safety Warning: When using parallel execution, ensure proper var scoping and implement appropriate locking strategies. Thread concurrency requires careful attention to shared state and race conditions.
⚡ BoxLang Async Programming (Recommended for Parallel Operations)
For a functional and much more flexible approach to multi-threaded or parallel programming, use BoxLang's built-in async programming constructs, which leverage the Java Concurrency and CompletableFutures frameworks.
Key async methods for parallel query processing:
🔒 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.
Security Critical: Never concatenate user input directly into SQL strings! Always use query parameters.
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:
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
{% hint style="warning" %} The cf_sql_{type} syntax (e.g., cf_sql_varchar) is only supported when bx-compat-cfml is installed. Use the native type names (e.g., varchar) in all new code. {% endhint %}
🏗️ Building Queries Programmatically
You can create and manipulate queries without database connections using BoxLang's 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:
{% hint style="info" %} For complete QoQ documentation including custom functions, bitwise operators, and performance tips, see Query of Queries. {% endhint %}
{% hint style="success" %} Performance Tip: For simple filtering and sorting, use functional methods like queryFilter() and querySort() instead of QoQ - they're even faster and more type-safe! {% endhint %}
📦 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
{% hint style="success" %} Best Practice: Use returntype="array" for REST APIs and JSON responses. It's cleaner and more compatible with JavaScript frameworks like React, Vue, and Angular. {% endhint %}
🏗️ 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/
{% hint style="success" %} Recommended: Use QB for complex queries and database migrations. It provides better testability and database portability than raw SQL. {% endhint %}
⚙️ Query Options
BoxLang supports comprehensive query options for controlling execution behavior:
Core Options
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"
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"
Example Usage
CFML Compatibility Options
When bx-compat-cfml is installed, these aliases are available:
blockfactor
fetchSize
cacheID
cacheKey
cacheRegion
cacheProvider
cachedAfter
Converted to cacheTimeout
cachedWithin
cacheTimeout
{% hint style="info" %} Future Options: Support planned for cachedWithin="request", timezone, psq, and lazy loading. {% endhint %}
{% hint style="warning" %} Unsupported: The following CFML options are not supported: `
username, password, debug, clientInfo, fetchClientInfo, ormoptions`.
Last updated
Was this helpful?
