Skip to main content

Introduction

The DataLinks Query Language is a powerful domain-specific language designed to query and manipulate datasets within the DataLinks ecosystem. It provides a flexible and intuitive way to access data, apply filters, establish relationships between datasets, and sort results. This language is particularly useful for:
  • Retrieving data from specific datasets
  • Filtering data based on various conditions
  • Discovering relationships between different datasets
  • Traversing complex data structures
  • Sorting and limiting result sets
  • Counting, summing, averaging and grouping records

Basic Syntax

The query language follows a fluent interface pattern, where methods are chained together to build complex queries. The basic structure of a query is:
or
Both Ontology and OntologyObject are valid entry points for creating a query.

Querying Datasets

Basic Dataset Query

To query a dataset, specify its name:
Dataset names are case-insensitive, so Ontology("movies") and Ontology("Movies") refer to the same dataset.

Namespaced Datasets

Datasets can be organized in namespaces. To query a namespaced dataset, use the format:
For example:
If a dataset name is ambiguous (exists in multiple namespaces), you must specify the namespace to avoid errors.

Filtering Data

Basic Filtering

To filter data, use the filter() method with a comparison expression:
or

Comparison Operators

The query language supports the following comparison operators:
  • == (equal to)
  • != (not equal to)
  • < (less than)
  • <= (less than or equal to)
  • > (greater than)
  • >= (greater than or equal to)
Examples:

Logical Operators

Combine multiple conditions using logical operators:
  • && (AND)
  • || (OR)
Examples:

Nested Conditions

Use parentheses to create complex nested conditions:

Filter Expression Syntax

The query language provides flexible syntax for filter expressions:
  1. Quoting Field Names: Field names can be unquoted, single-quoted, or double-quoted:
  1. Quoting Values: String values can be unquoted (for simple strings) or quoted:
  1. Multi-word Values: Multi-word string values must be quoted:
Note: Unquoted multi-word values (e.g., title == The Matrix) are not supported.

Linking Datasets

To find related datasets, use the searchAround() and find() methods:
This searches for actors related to movies based on defined links between the datasets.

Specifying Search Depth

You can specify the depth of the search (how many hops to traverse):
A depth of 2 means it will look for relationships that are two links away. To follow specific columns when linking datasets:
This specifies that the link between Movies and Actor should be through the “director” column. You can specify multiple columns to follow:

Sorting and Limiting Results

Sorting Results

Sort results in ascending or descending order:
Alternative syntax:

Multiple Sort Criteria

Sort by multiple fields:
This sorts actors first by nationality in descending order, then by age in ascending order.

Limiting Results

Limit the number of results returned:
You can also limit results at different stages of a complex query:
This limits the movies to 10 and the related actors to 5.

Aggregations

Most aggregations replace the matched records with a computed result; countLinked() and sumLinked() instead keep every record and enrich it. Place the aggregation after find() when the query traverses links, or directly after Ontology() / filter() otherwise; after it, only filter(), sortAscending() / sortDescending() and limit() may follow (plus maxDepth() for the linked aggregations). groupBy() is always paired with one count(), sum() or avg(), and that pair counts as a single aggregation. Only one aggregation is supported per query — do not combine count(), sum(), avg(), groupBy(…).<reducer>(), countLinked() or sumLinked() with each other.

Counting Records

count() returns a single number: how many records matched.
Aggregations placed directly on the root dataset are exact. After a searchAround().find() they run over the traversed records, which are capped at 10,000 — use countLinked() / sumLinked() when you need exact per-record totals over links.

Sum and Average

sum() and avg() compute a single total or average over a numeric column. The column argument is mandatory.
sum() and avg() only work on numeric columns.

Grouping

groupBy() groups the matched records by the distinct values of one or more columns and returns one synthetic row per group. It must be followed by count(), sum() or avg():
Each returned row holds the group value(s) plus the computed metric, named after the operation and column:
  • groupBy("director").count() → director, count.director
  • groupBy("director").sum("budget") → director, sum.budget
  • groupBy("director").avg("budget") → director, avg.budget
Within groupBy(), sum() and avg() accept several value columns at once, producing one metric per column:
Group by several columns to get one row per distinct combination of values:
The count metric is then named after all the group columns in order:
  • groupBy("director", "year").sum("budget") → director, year, sum.budget
  • groupBy("director", "year").count() → director, year, count.director.year
The query language has no distinct() operation, but groupBy("column").count() returns one row per distinct value, so the number of rows returned is the number of distinct values. At most 10,000 groups are returned (the largest by record count unless sorted), and grouping a column with more than 100,000 distinct values is rejected:

Filtering Groups

A filter() placed before the aggregation narrows the records that get aggregated. A filter() placed after a groupBy() aggregation applies to the groups it produced, comparing against the emitted metric — the equivalent of SQL’s HAVING. Both can appear in the same query:
After a groupBy() aggregation only the group column(s) and the metric exist on the rows. A filter that names any other column is rejected.

Sorting and Limiting Groups

Sort grouped results by their metric to get the top or bottom N groups:
Always pair limit() with a sort on the metric. Without a sort, groups come back ordered by record count, so an unsorted limit() returns the largest groups rather than the highest totals.Sorting on the bare value column (e.g. sortDescending("budget") instead of sortDescending("sum.budget")) is rejected, because that column no longer exists on the grouped rows.

Counting and Summing Linked Records

countLinked("X") / sumLinked("X", …) keep the matched records and add an aggregate over the linked records in X to each one. By default only direct (one-hop) links count:
Returns every actor, each with _link_count: the number of movies they appear in.
Returns the 10 actors whose movies earned the most, each with sum.boxOffice. sumLinked() accepts several numeric columns at once; each adds its own sum.<column> field:
Returns every actor, each with sum.budget and sum.boxOffice. If X is further away, add maxDepth(n). The in-between datasets are found for you, and the results stay on your original records:
Returns every actor, each with sum.ticketsSold: the tickets sold across all screenings of all their movies (Actor → Movies → Screenings). Each screening counts once per actor, however many paths reach it. To get the aggregate per record of an in-between dataset instead, traverse to it first and aggregate from there. The results then belong to that dataset, not to the one you started from:
Returns movies, not actors, each with sum.ticketsSold for that movie’s own screenings. A filter() placed after countLinked() / sumLinked() applies to the linked dataset’s columns and restricts which linked records are counted or summed:
Unlike groupBy() metrics, the _link_count and sum.<column> fields produced by countLinked() / sumLinked() cannot be used in a filter(). Apply any threshold on the returned rows instead.

Natural Language Query Generation

The system can generate queries from natural language questions. For example: Natural language: “What is the age of the director of Braveheart?” Generated query:

Examples

Basic Query Examples

  1. Find all movies:
  1. Find a specific movie:
  1. Find movies released before 2000:

Relationship Query Examples

  1. Find actors who directed movies:
  1. Find movies directed by actors under 60:
  1. Find actors who directed movies, then find other actors who worked with them:

Sorting and Limiting Examples

  1. Find the 3 oldest movies:
  1. Find the 5 highest budget movies:

Aggregation Examples

  1. Count movies released before 2000:
  1. Total budget per director, top 5:
  1. Directors with more than two movies:
  1. Movies ranked by number of linked actors:

Error Handling

If a query returns unexpected results:
  1. Check field names and table relationships for correctness.
  2. Ensure the depth in searchAround is sufficient for your query.
  3. Debug by breaking the query into smaller parts and testing them individually.