Skip to content

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:

1
key operator value

For example:

1
2
// CaSH
find "age > 30"
1
2
// 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).

1
age bw 18 30
1
2
3
// Java
Set<Long> records = concourse.find(
    "age", Operator.BETWEEN, 18, 30);

Conjunctions#

Combine multiple conditions with and and or:

1
2
name = "Jeff" and age > 30
department = "Engineering" or department = "Design"

Parenthetical Grouping#

Use parentheses to control evaluation order:

1
2
(department = "Engineering" or department = "Design")
    and active = true

Without parentheses, and has higher precedence than or.

You can use dot-separated navigation keys in CCL conditions to query across linked records. See Graph for details.

1
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:

1
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:

1
2
3
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:

1
2
3
4
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:

1
2
3
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#

1
2
3
4
5
6
7
// Java
Set<Long> records = concourse.find(
    Criteria.where()
        .key("age")
        .operator(Operator.GREATER_THAN)
        .value(30)
        .build());

Combining Conditions#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 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:

1
2
3
4
5
6
7
// 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:

1
2
// Java
Criteria criteria = Criteria.parse("age > 30");

Converting Criteria to CCL#

1
2
// 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#

1
2
// Java
Set<Long> records = concourse.find("age > 30");
1
2
// CaSH
find "age > 30"

Find with Key, Operator, Value#

1
2
3
// 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:

1
2
3
// 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.

1
2
3
4
5
6
7
8
9
// 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#

1
2
3
4
// Java
Map<Long, Map<String, Set<Object>>> data =
    concourse.select("department = Engineering",
        Order.by("name").ascending());

Multi-Key Sort#

1
2
3
// Java
Order order = Order.by("department").ascending()
    .then("name").ascending();

Descending Sort#

1
2
// Java
Order order = Order.by("age").descending();

Temporal Sort#

Sort by the value of a key at a specific timestamp:

1
2
3
4
// Java
Order order = Order.by("score")
    .at(Timestamp.fromString("yesterday"))
    .descending();

Pagination#

Large result sets can be paginated using the Page builder.

Limit Results#

1
2
3
4
// Java
Map<Long, Map<String, Set<Object>>> data =
    concourse.select("age > 21",
        Page.limit(10));

Skip and Limit#

1
2
3
4
5
// 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));

Offset-Based Pagination#

1
2
3
// Java
Page page = Page.offset(20);       // Start at offset 20
Page page = Page.offsetLimit(20, 10); // Offset 20, limit 10
1
2
3
4
// 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:

1
2
3
4
5
// Java
Map<Long, Map<String, Set<Object>>> data =
    concourse.select("department = Engineering",
        Order.by("name").ascending(),
        Page.limit(25));