Skip to content

Command API#

The Command API provides a type-safe, fluent interface for building and executing Concourse operations. Commands can be constructed programmatically, parsed from CCL strings, executed individually, or submitted as a batch.

Building Commands#

Commands are built using fluent builders accessed through three interchangeable entry points:

  • Command.to() — for actions (e.g., add, set, remove)
  • Command.is() — for checks and queries (e.g., holds, verify)
  • Command.go() — for imperatives (e.g., ping)

All three return the same builder — the choice is purely for readability at the call site.

Write Commands#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Java
Command addCmd = Command.to()
    .add("name").as("Jeff Nelson").in(1);

Command setCmd = Command.to()
    .set("email").as("jeff@cinchapi.com").in(1);

Command removeCmd = Command.to()
    .remove("tag").as("beta").from(1);

Command clearCmd = Command.to()
    .clear("name").from(1);

Command insertCmd = Command.to()
    .insert("{\"name\": \"Jeff\"}");

Command linkCmd = Command.to()
    .link("employer").from(1).to(100);

Command unlinkCmd = Command.to()
    .unlink("employer").from(1).to(100);

Read Commands#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// Java
Command selectCmd = Command.to()
    .select("name", "age").from(1);

Command selectAllCmd = Command.to()
    .selectAll().from(1);

Command getCmd = Command.to()
    .get("name").from(1);

Command browseCmd = Command.to()
    .browse("color");

Command describeCmd = Command.to()
    .describe(1);

Command navigateCmd = Command.to()
    .navigate("employer.name").from(1);

Query Commands#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Java
Command findCmd = Command.to()
    .find("age > 30");

Command findWithCriteria = Command.to()
    .find(Criteria.where()
        .key("age")
        .operator(Operator.GREATER_THAN)
        .value(30)
        .build());

Command searchCmd = Command.to()
    .search("name").for_("Jeff");

Check Commands#

1
2
3
4
5
6
// Java
Command holdsCmd = Command.is()
    .holds(1);

Command verifyCmd = Command.to()
    .verify("name").as("Jeff").in(1);

Atomic Operation Commands#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Java
Command vasCmd = Command.to()
    .verifyAndSwap("balance")
    .as(100).in(1).to(90);

Command vosCmd = Command.to()
    .verifyOrSet("status").as("active").in(1);

Command foaCmd = Command.to()
    .findOrAdd("email").as("jeff@cinchapi.com");

Command foiCmd = Command.to()
    .findOrInsert("email = jeff@cinchapi.com")
    .with("{\"email\": \"jeff@cinchapi.com\"}");

Time Travel Commands#

Commands support temporal operations:

1
2
3
4
5
6
// Java
Command historicalGet = Command.to()
    .get("name").from(1).at("yesterday");

Command historicalFind = Command.to()
    .find("age > 30").at("last month");

Ordering and Pagination#

1
2
3
4
5
6
7
8
// Java
Command ordered = Command.to()
    .select("name").from("age > 21")
    .order().by("name").ascending();

Command paged = Command.to()
    .select("name").from("age > 21")
    .page().skip(0).limit(10);

Parsing Commands from CCL#

Commands can be parsed from CCL command strings:

1
2
3
4
5
6
// Java
Command cmd = Command.parse("select name from 1");

// Parse multiple semicolon-separated commands
List<Command> cmds = Command.parseAll(
    "add name as Jeff in 1; set age as 30 in 1");

Converting Commands to CCL#

Any command can be rendered as a CCL string:

1
2
// Java
String ccl = command.ccl();

Executing Commands#

Two families of methods execute Command objects:

Method When to use
exec(...) You only care about the final command’s result
submit(...) You need the result of every command

Both families execute commands sequentially in a single server round trip. If any command fails, execution stops and the exception is propagated immediately.

exec() — returns the last result#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Java

// Single command
Object result = concourse.exec(
        Command.to().get("name").from(1));

// Multiple commands as varargs &mdash; returns last result
Object last = concourse.exec(
        Command.to().add("name").as("Jeff").in(1),
        Command.to().add("age").as(30).in(1),
        Command.to().select("name", 1));

// From a pre-built list
List<Command> cmds = ...;
Object last = concourse.exec(cmds);

// Directly from CCL text
Object last = concourse.exec(
        "add name as Jeff in 1; select name from 1");

submit() — returns every result#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Java

// Single command &mdash; returns a one-element list
List<Object> r = concourse.submit(
        Command.to().add("name").as("Jeff").in(1));

// Multiple commands as varargs
List<Object> results = concourse.submit(
        Command.to().add("name").as("Jeff").in(1),
        Command.to().add("age").as(30).in(1),
        Command.to().select(1));

// From a pre-built list
List<Command> cmds = ...;
List<Object> results = concourse.submit(cmds);

// Directly from CCL text (semicolon- or newline-separated)
List<Object> results = concourse.submit(
        "add name as Jeff in 1; add age as 30 in 1");

// From a CommandGroup built via prepare()
List<Object> results = concourse.submit(group);

In CCL text form, commands are separated by a semicolon (;) or a newline. Whitespace between commands is ignored.

Command Groups (prepare()/submit())#

prepare() returns a CommandGroup — a void-returning mirror of the entire Concourse API. Call methods on the group to accumulate operations, then pass the group to submit() to execute them all in a single round trip.

1
2
3
4
5
6
7
// Java
CommandGroup group = concourse.prepare();
group.add("name", "Jeff", 1);
group.add("age", 30, 1);
group.set("status", "active", 1);

List<Object> results = concourse.submit(group);

CommandGroup supports every read, write, aggregation, atomic, and system operation that Concourse does: select, get, find, browse, navigate, audit, chronicle, trace, consolidate, calculate, verifyAndSwap, verifyOrSet, findOrAdd, findOrInsert, stage, commit, abort, ping, holds, inventory, and more. Each group method signature matches the corresponding method on Concourse but returns void — results surface when the group is submitted.

Inspecting accumulated commands#

1
2
// Java
List<Command> queued = group.commands();

This returns an unmodifiable snapshot of the commands the group has collected. It is useful for logging, assertions in tests, or rendering a preview of what submit(group) will execute.

Transaction commands inside a group#

A group can bracket its own transaction by queueing stage(), the body, and commit():

1
2
3
4
5
6
7
8
// Java
CommandGroup group = concourse.prepare();
group.stage();
group.add("name", "Jeff", 1);
group.set("email", "jeff@cinchapi.com", 1);
group.commit();

concourse.submit(group);

Because the group is submitted atomically as a single batch, the whole transaction either executes end-to-end or fails on the first conflicting step.

Commands in CaSH#

CaSH natively supports CCL command syntax. You can type commands directly at the prompt:

1
2
3
4
[default/cash]$ select name, age from 1
[default/cash]$ find age > 30
[default/cash]$ add name as "Jeff" in 1
[default/cash]$ get name from 1 at "yesterday"

See Concourse Shell for details on CaSH’s dual-mode input and prepare mode.