Query language
RhyDB queries are declarative and support a pipeline syntax.
Declarative language
RhyDB’s query language is based on SaneQL which is declarative and conceptually like SQL but has a more consistent syntax and resembles typical dataframe APIs. SaneQL was proposed by T. Neumann and V. Leis in their paper “A Critique of Modern SQL And A Proposal Towards A Simple and Expressive Query Language” (2024).
A declarative query describes the result through relational operators without specifying the physical steps used to compute it. The order of the operators defines the meaning of the query, while the query optimizer determines a physical execution plan that can differ from the written expression.
Tables and column symbols
Relational operators work on tables and scalar values. A table name identifies a table. Column identifiers refer to columns in the table that is currently in scope. The names of columns, relations and operators are also called symbols.
Pipeline operators
Every pipeline operator takes a table as input and produces a table as output. The response schema — which fields are returned and their types — is always the output schema of the last operator in the pipeline.
Pipeline-style syntax
A query can be written as a pipeline of operators chained with expr.method(arg1) syntax. The dot passes the table produced on its left as the first argument of the next operator. This is also known as the Uniform Function Call Syntax (UFCS). As a simple example, the following query counts the sequences per lineage in Switzerland in a table called “measles”:
measles
.filter(country = 'Switzerland')
.groupBy({count := count()}, {lineage})
.orderBy({count.desc()})
Functional-style syntax
The same query can be written as nested function calls such as method(expr, arg1). The example above can be written in functional-style syntax as:
orderBy(
groupBy(
filter(measles, country = 'Switzerland'),
{count := count()},
{lineage}
),
{desc(count)}
)
Operator arguments
SaneQL operator signatures distinguish table, expression and symbol arguments, as well as lists of expressions or symbols. A table argument supplies rows and columns to an operator; an expression is an unevaluated term such as a filter predicate; and a symbol names a column, relation or operator. RhyDB uses set literals for arguments such as the columns of project or groupBy. Record literals represent named expressions such as {count := count()}. Arguments can be positional or named with :=; after the first named argument, all remaining arguments must also be named.
Boolean and comparison operators
&&combines two expressions with logical AND,||combines them with logical OR and!negates an expression.=,<>,<,<=,>and>=compare a column with a literal value. The column may stand on either side of the operator.- A boolean column is a predicate by itself, so
filter(isHuman)meansfilter(isHuman = true). - A null cell matches no comparison,
<>included.isNull()andisNotNull()test for missing values; comparing against thenullliteral is an error.
Literals
Literals represent values written directly in a query. In addition to scalar values, RhyDB has set and record literals for operator arguments and named output fields.
| Type | Syntax | Example |
|---|---|---|
| String | single-quoted | ‘Switzerland’ |
| Integer | bare number | 42 |
| Float | decimal | 3.14 |
| Boolean |
| true |
| Null | null | null |
| Date | ‘YYYY-MM-DD’::date | ‘2021-03-15’::date |
| Set | {elem1, elem2, …} | {‘A’, ‘B’, ‘C’} |
| Record | {field1 := value1, …} | {x := ‘A’, y := ‘B’, z := 3} |
The query-language reference describes the operators and functions currently implemented by RhyDB.
