Skip to content

Graph#

Concourse’s document-graph data model lets you store relationships between records and traverse them efficiently. Relationships are represented as Links — directional pointers from one record to another.

Linking Records#

Use the link method to create a directed relationship from a source record to a destination record via a named key.

1
2
3
// Java
// Create a link: record 1 --employer--> record 100
concourse.link("employer", 100, 1);
1
2
// CaSH
link "employer", 100, 1

Links are directional. The example above creates a relationship from record 1 to record 100, but record 100 has no automatic back-reference to record 1.

1
2
3
// Java
concourse.link("friends",
    Lists.newArrayList(2L, 3L, 4L), 1);

Use unlink to remove a relationship:

1
2
// Java
concourse.unlink("employer", 100, 1);
1
2
// CaSH
unlink "employer", 100, 1

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

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

This returns all records whose employer key contains a link to record 100.

Navigation lets you traverse links and read data from linked records using dot-separated key paths called navigation keys.

A navigation key is a dot-separated path where each segment before the last is a key containing links, and the final segment is the key to read from the destination record.

1
employer.name

This means: follow the employer link, then read the name key from the linked record.

Using navigate()#

The navigate method traverses navigation keys and returns data from the destination records.

1
2
3
4
// Java
// Get the name of the employer linked from record 1
Map<Long, Set<Object>> results =
    concourse.navigate("employer.name", 1);
1
2
// CaSH
navigate "employer.name" from 1

Multi-Hop Navigation#

Navigation keys can span multiple hops:

1
employer.ceo.name

This traverses two links: first employer, then ceo, and finally reads name from the destination.

1
2
3
// Java
Map<Long, Set<Object>> results =
    concourse.navigate("employer.ceo.name", 1);
1
2
3
4
5
6
// Java
Map<Long, Map<String, Set<Object>>> results =
    concourse.navigate(
        Lists.newArrayList(
            "employer.name", "employer.city"),
        1);
1
2
3
4
// Java
Map<Long, Set<Object>> results =
    concourse.navigate("employer.name",
        Lists.newArrayList(1L, 2L, 3L));

Navigate from all records matching a query:

1
2
3
4
// Java
Map<Long, Set<Object>> results =
    concourse.navigate("employer.name",
        "department = Engineering");

Historical Navigation#

1
2
3
4
// Java
Map<Long, Set<Object>> results =
    concourse.navigate("employer.name", 1,
        Timestamp.fromString("last month"));

Navigation keys can be used directly in CCL conditions to filter records based on data in linked records:

1
employer.name = "Cinchapi"
1
2
3
// Java
Set<Long> records = concourse.find(
    "employer.name = \"Cinchapi\"");

This finds all records whose linked employer record has a name of "Cinchapi". Navigation keys work with all CCL operators:

1
2
3
employer.founded > 2010
manager.department contains "Engineering"
employer.address.city = "Atlanta"

Tracing References#

The trace method returns all incoming links to a record — it answers the question “which records link to this one?”

1
2
3
4
// Java
Map<String, Set<Long>> incoming =
    concourse.trace(100);
// e.g., {"employer" -> [1, 2, 3], "partner" -> [50]}
1
2
// CaSH
trace 100

The result maps each key name to the set of records that contain a link to the traced record via that key.

Trace Multiple Records#

1
2
3
// Java
Map<Long, Map<String, Set<Long>>> results =
    concourse.trace(Lists.newArrayList(100L, 200L));

Historical Trace#

1
2
3
4
// Java
Map<String, Set<Long>> incoming =
    concourse.trace(100,
        Timestamp.fromString("last week"));

Traversal Optimization#

Concourse automatically selects the most efficient traversal strategy for navigation queries. Depending on the shape of the data, it may use:

  • Forward traversal: Start from the source records and follow links forward to find destination data. This is more efficient when there are fewer source records than destination records.

  • Reverse traversal: Start from potential destination records and trace links backward to find matching sources. This is more efficient when there are fewer destination records than source records.

The optimizer chooses the strategy automatically based on data characteristics. No manual tuning is required.