Queries
Concourse uses the Concourse Command Language (CCL) to express
conditions for finding records. Queries are used with the find
method to locate records, and can also be passed to read, write,
and aggregation methods to filter their scope.
For the complete CCL language specification — including all
commands, functions, and formal syntax — see the
CCL Reference .
Concourse Command Language (CCL)
CCL is a human-readable query language for expressing conditions
on record data. A CCL expression evaluates against every record in
the database and returns those that match.
Basic Syntax
A CCL condition has the form:
For example:
// Java
Set < Long > records = concourse . find ( "age > 30" );
Operators
CCL supports the following operators:
Operator
Symbol
Description
EQUALS
=, eq
Exact equality
NOT_EQUALS
!=, ne, neq
Inequality
GREATER_THAN
>, gt
Greater than
GREATER_THAN_OR_EQUALS
>=, gte
Greater than or equal
LESS_THAN
<, lt
Less than
LESS_THAN_OR_EQUALS
<=, lte
Less than or equal
BETWEEN
bw, ><
Between two values (inclusive start, exclusive end)
LINKS_TO
lnk2, ->
Links to a specific record
REGEX
regex, nfx
Regular expression match
NOT_REGEX
not_regex, nrx
Negated regular expression
LIKE
like, lk
Pattern matching
NOT_LIKE
not_like, nlk
Negated pattern matching
CONTAINS
contains
Full-text search within query
NOT_CONTAINS
not_contains
Negated full-text search
Value Types in Queries
CCL automatically infers value types from the syntax:
Syntax
Type
Example
true / false
Boolean
active = true
Unquoted integer
Integer/Long
age > 30
Decimal number
Float/Double
price >= 9.99
Double-quoted string
String
name = "Jeff"
@recordId
Link
employer lnk2 @5
Between Operator
The BETWEEN operator requires two values and matches records
where the key’s value falls within the range (inclusive start,
exclusive end).
// Java
Set < Long > records = concourse . find (
"age" , Operator . BETWEEN , 18 , 30 );
Conjunctions
Combine multiple conditions with and and or:
name = "Jeff" and age > 30
department = "Engineering" or department = "Design"
Parenthetical Grouping
Use parentheses to control evaluation order:
(department = "Engineering" or department = "Design")
and active = true
Without parentheses, and has higher precedence than or.
Navigation Keys in Criteria
You can use dot-separated navigation keys in CCL conditions to
query across linked records. See Graph for details.
employer.name = "Cinchapi"
This finds all records whose linked employer record has a
name of "Cinchapi".
Temporal Conditions
CCL supports querying against historical data by appending a
timestamp clause:
age > 30 at "January 1, 2025"
Function Values
A function value is an aggregation result used as the
comparison value on the right-hand side of a condition. This
lets you write dynamic conditions that compare a key’s value
against a computed aggregate rather than a literal.
Call Syntax
Use a function call as the value:
age > avg(age)
score >= min(score)
salary < max(salary)
This finds records where the key’s value exceeds (or meets) the
aggregate computed across all records. The function is evaluated
first, and the result is used as the comparison value.
Function values can also scope the aggregate to specific records
or a sub-condition:
age > avg(age, 1, 2, 3)
score > avg(score, department = "Engineering")
age > avg(age, at "yesterday")
score > avg(score, active = true, at "last month")
Pipe Syntax
Alternatively, use the pipe (|) operator on the key side to
apply a function to the key’s values before comparing:
score | avg > 80
age | sum > 1000
items | count > 5
See the CCL Reference for the full function
syntax including all call forms.
Criteria Builder
The Criteria builder provides a type-safe, programmatic
alternative to CCL strings. The builder uses a state-machine
pattern to guide you through valid parameter sequences.
Basic Usage
// Java
Set < Long > records = concourse . find (
Criteria . where ()
. key ( "age" )
. operator ( Operator . GREATER_THAN )
. value ( 30 )
. build ());
Combining Conditions
// Java
Set < Long > records = concourse . find (
Criteria . where ()
. key ( "age" ). operator ( Operator . GREATER_THAN )
. value ( 30 )
. and ()
. key ( "department" )
. operator ( Operator . EQUALS )
. value ( "Engineering" )
. build ());
Grouping Conditions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 // Java
Set < Long > records = concourse . find (
Criteria . where ()
. group (
Criteria . where ()
. key ( "dept" )
. operator ( Operator . EQUALS )
. value ( "Engineering" )
. or ()
. key ( "dept" )
. operator ( Operator . EQUALS )
. value ( "Design" ))
. and ()
. key ( "active" )
. operator ( Operator . EQUALS )
. value ( true )
. build ());
Temporal Criteria
Pin a Criteria to a specific timestamp:
// Java
Criteria criteria = Criteria . where ()
. key ( "age" )
. operator ( Operator . GREATER_THAN )
. value ( 30 )
. build ()
. at ( Timestamp . fromString ( "last week" ));
Parsing CCL into Criteria
You can parse a CCL string into a Criteria object:
// Java
Criteria criteria = Criteria . parse ( "age > 30" );
Converting Criteria to CCL
// Java
String ccl = criteria . ccl ();
The find Method
The find method returns the set of record IDs that match a
query. It accepts CCL strings, Criteria objects, or individual
key/operator/value parameters.
Find with CCL
// Java
Set < Long > records = concourse . find ( "age > 30" );
Find with Key, Operator, Value
// Java
Set < Long > records = concourse . find (
"age" , Operator . GREATER_THAN , 30 );
Find with Equality Shorthand
When using the EQUALS operator, you can omit the operator:
// Java
Set < Long > records = concourse . find ( "name" , "Jeff" );
// Equivalent to: find("name", Operator.EQUALS, "Jeff")
Queries in Other Methods
Most Concourse methods accept a CCL string or Criteria in place
of explicit record IDs. This lets you combine querying and reading
in a single call.
// Java
// Instead of:
Set < Long > records = concourse . find ( "age > 30" );
Map < Long , Map < String , Set < Object >>> data =
concourse . select ( records );
// You can write:
Map < Long , Map < String , Set < Object >>> data =
concourse . select ( "age > 30" );
This pattern works with select, get, navigate, clear,
set, revert, diff, and all aggregation methods.
Ordering Results
When reading data from multiple records, you can specify a sort
order using the Order builder.
Single-Key Sort
// Java
Map < Long , Map < String , Set < Object >>> data =
concourse . select ( "department = Engineering" ,
Order . by ( "name" ). ascending ());
Multi-Key Sort
// Java
Order order = Order . by ( "department" ). ascending ()
. then ( "name" ). ascending ();
Descending Sort
// Java
Order order = Order . by ( "age" ). descending ();
Temporal Sort
Sort by the value of a key at a specific timestamp:
// Java
Order order = Order . by ( "score" )
. at ( Timestamp . fromString ( "yesterday" ))
. descending ();
Large result sets can be paginated using the Page builder.
Limit Results
// Java
Map < Long , Map < String , Set < Object >>> data =
concourse . select ( "age > 21" ,
Page . limit ( 10 ));
Skip and Limit
// Java
// Skip the first 20 results, return the next 10
Map < Long , Map < String , Set < Object >>> data =
concourse . select ( "age > 21" ,
Page . skipLimit ( 20 , 10 ));
// Java
Page page = Page . offset ( 20 ); // Start at offset 20
Page page = Page . offsetLimit ( 20 , 10 ); // Offset 20, limit 10
Navigating Pages
// Java
Page first = Page . limit ( 10 );
Page second = first . next ();
Page previous = second . previous ();
Combining Order and Page
Ordering and pagination can be used together:
// Java
Map < Long , Map < String , Set < Object >>> data =
concourse . select ( "department = Engineering" ,
Order . by ( "name" ). ascending (),
Page . limit ( 25 ));