Skip to content

Data Types#

Concourse is a dynamically typed database. Value types are intelligently inferred when data is written, and comparisons can be made across compatible types. You never need to declare a schema or specify column types.

Primitive Types#

Boolean#

A true or false value. Booleans are stored as a single byte and support equality and inequality comparisons.

1
2
// Java
concourse.add("active", true, 1);
1
2
// CaSH
add "active", true, 1

Double#

A 64-bit IEEE 754 double-precision floating-point number. Use Double for high-precision decimal values or very large/small numbers.

1
2
// Java
concourse.add("price", 29.99d, 1);

Concourse distinguishes between Double and Float values. If precision matters, be explicit about which type you use.

Float#

A 32-bit IEEE 754 single-precision floating-point number. Values written as floating-point literals without a type suffix are stored as Float by default.

1
2
// Java
concourse.add("rating", 4.5f, 1);

Integer#

A 32-bit signed integer, supporting values from -2,147,483,648 to 2,147,483,647. Whole numbers within this range are stored as Integer by default.

1
2
// Java
concourse.add("age", 30, 1);
1
2
// CaSH
add "age", 30, 1

Long#

A 64-bit signed integer, supporting values from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Whole numbers that exceed the Integer range are automatically stored as Long.

1
2
// Java
concourse.add("population", 8000000000L, 1);

String#

A UTF-8 encoded character sequence. Strings are automatically indexed for full-text search, enabling substring and keyword matching.

1
2
// Java
concourse.add("name", "Jeff Nelson", 1);
1
2
// CaSH
add "name", "Jeff Nelson", 1

String vs Tag

If you do not want a string value to be indexed for full-text search, store it as a Tag instead.

Tag#

A Tag is a string that is not indexed for full-text search. Use Tags for structured or categorical data (e.g., identifiers, codes, enum-like values) where full-text search is unnecessary.

Once stored, a Tag is treated as a String for all other purposes. Tag values are returned as Strings when read back, and queries treat Tags and Strings identically for comparison.

1
2
// Java
concourse.add("status", Tag.create("ACTIVE"), 1);

Timestamp#

A Timestamp represents a point in time with microsecond precision. Internally, it is a 64-bit signed integer counting microseconds since the Unix epoch (January 1, 1970 00:00:00 UTC). The signed representation supports dates approximately 290,000 years into the past and future.

1
2
// Java
concourse.add("created_at", Timestamp.now(), 1);

Creating Timestamps#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Java
Timestamp now = Timestamp.now();
Timestamp epoch = Timestamp.epoch();
Timestamp fromMicros = Timestamp.fromMicros(1609459200000000L);
Timestamp fromJoda =
    Timestamp.fromJoda(DateTime.now());
Timestamp fromInstant =
    Timestamp.fromInstant(Instant.now());
Timestamp fromString =
    Timestamp.fromString("January 1, 2025");
Timestamp parsed =
    Timestamp.parse("2025-01-01", "yyyy-MM-dd");

Natural Language Timestamps#

The Timestamp.fromString method accepts natural language descriptions of time, which are especially useful in CaSH and for time travel queries.

1
2
3
4
5
// CaSH examples
get "name" from 1 at "yesterday"
get "name" from 1 at "3 days ago"
get "name" from 1 at "last week"
get "name" from 1 at "January 1, 2025"

A Link is a pointer to another record, identified by that record’s unique ID. Links are the foundation of Concourse’s graph features, enabling you to model relationships between records and traverse them with navigation queries.

Links are directional — a link from record 1 to record 2 does not imply a link from record 2 back to record 1.

1
2
3
// Java
concourse.link("employer", 100, 1);
// Record 1 now links to record 100 via the "employer" key

Links are displayed with an @ prefix (e.g., @100) when read back from Concourse.

You can query for records that link to a specific record using the LINKS_TO operator:

1
employer lnk2 @100
1
2
3
4
// Java
Set<Long> employees = concourse.find(
    "employer", Operator.LINKS_TO,
    Link.to(100));

Use dot-separated navigation keys to traverse links and read data from linked records. See Graph for details.

NULL#

The NULL type represents the absence of a value. Concourse handles nulls internally in certain operations (e.g., when a key has no value in a record). You typically do not write null values directly.

Advanced Types#

A dynamic link is a conceptual relationship where the set of linked records is determined by evaluating a criteria at read time. Unlike a resolvable link, which is evaluated once at write time and produces static links, a dynamic link would re-evaluate its criteria on every read.

Note

Concourse does not currently have a native dynamic link type. To achieve dynamic link behavior, use a navigation key query that evaluates criteria at read time. For example, employer.name = "Cinchapi" dynamically finds all records whose linked employer has a matching name.

A resolvable link is a write-time instruction to create links to all records that match a given criteria. Unlike a regular Link, a resolvable link is evaluated once at the time of write. The resulting links are static and do not change as the matching criteria’s results change over time.

Use Link.toWhere(criteria) to create a resolvable link instruction within an insert operation:

1
2
3
4
5
6
// Java
Map<String, Object> data = Maps.newLinkedHashMap();
data.put("name", "Engineering");
data.put("members",
    Link.toWhere("department = Engineering"));
long record = concourse.insert(data);

Only use within insert operations

Resolvable links should only be used within insert operations (JSON, Map, or Multimap). Do not use add to write resolvable links because the evaluation and linking would not be atomic.

Type Inference and Coercion#

Concourse automatically infers the type of a value when it is written. In most cases, the inferred type matches what you expect:

Input Inferred Type
true / false Boolean
42 Integer
8000000000 Long
3.14 Float
3.14d Double
"hello" String
Tag.create("x") Tag
Link.to(5) Link

Cross-Type Comparisons#

Concourse supports comparisons across compatible numeric types. An Integer value of 42 is considered equal to a Long value of 42, and numeric ordering works across Integer, Long, Float, and Double types.

String and Tag values are compared as strings, regardless of which type was used during the write.