Expressions
February 21, 2022 ยท View on GitHub
Reference: 17. Expressions and Testing Values
SPARQL supports expressions, which can be used to compute values and filter query results. In particular, expressions can be used in the following circumstances:
- As part of a
:filterclause. - As part of a
:bindclause, in an[expr var]form. - As part of a
:group-byclause, either as a freestanding expression or in an[expr var]form. - To aggregate or compute values in a
:select,:order-byor:havingclause.
In Flint, an expression is either a list of the form (op expr...), similar to Clojure functions, or a terminal, which can be a variable, an IRI or a literal.
Other than certain exceptions, like exists and not-exists, non-terminal expressions only accept other expressions as arguments.
The following is an example of an expression in Flint:
(if (< ?x ?y) (str ?x) (* 2 (+ 3 4)))
which is translated into SPARQL as:
IF((?x < ?y), STR(?x), (2 * (3 + 4)))
NOTE: Due to the complexity of SPARQL type semantics, Flint does not make any attempt to typecheck or validate expression arguments or return values.
Boolean and Arithmetic Expressions
SPARQL supports boolean and arithmetic operations, with accept one or more expression arguments and return boolean or numeric values. Like all expressions, boolean and arithmetic operations are written in Clojure's prefix order in Flint, but are translated to SPARQL's infix order. (The exceptions are the unary not operator, as well as SPARQL's unary + and - that are not supported in Flint.)
The in and not-in operations are special boolean operations that are equivalent to (or (= expr expr1) (= expr expr2) ...) and (and (not= expr expr1) (not= expr expr2) ...), respectively.
| Flint | SPARQL Form | Arglist | Reference |
|---|---|---|---|
not | !(expr) | [expr] | |
or | (expr || expr) | [expr & exprs] | 17.4.1.5 |
and | (expr && expr) | [expr & exprs] | 17.4.1.6 |
= | (expr = expr) | [expr expr] | 17.4.1.7 |
not= | (expr != expr) | [expr expr] | |
< | (expr < expr) | [expr expr] | |
> | (expr > expr) | [expr expr] | |
<= | (expr <= expr) | [expr expr] | |
>= | (expr >= expr) | [expr expr] | |
+ | (expr + expr + ...) | [expr & exprs] | |
- | (expr - expr - ...) | [expr & exprs] | |
* | (expr * expr * ...) | [expr & exprs] | |
/ | (expr / expr / ...) | [expr & exprs] | |
in | (expr IN (expr, ...)) | [expr & exprs] | 17.4.1.9 |
not-in | (expr NOT IN (expr, ...)) | [expr & exprs] | 17.4.1.10 |
Built-in Function Expressions
SPARQL accepts a number of built-in functions, which are translated from Flint's (fname expr expr ...) form to SPARQL's FNAME(expr, expr, ...) form. This section contains a list of all non-aggregate built-in expressions, grouped together by argument and return types.
| Flint | SPARQL | Arglist | Reference |
|---|---|---|---|
if | IF | [expr expr expr] | 17.4.1.2 |
coalesce | COALESCE | [& expr] | 17.4.1.3 |
datatype | DATATYPE | [expr] | 17.4.2.7 |
Graph Patterns
Unlike most expressions, exists and not-exists only accept graph patterns as arguments. The following example:
[:filter (exists [[?person :foaf/name ?name]])]
becomes:
FILTER EXISTS {
?person foaf:name ?name .
}
| Flint | SPARQL | Arglist | Reference |
|---|---|---|---|
exists | EXISTS | [graph-pattern] | 17.4.1.4 |
not-exists | NOT EXISTS | [graph-pattern] | 17.4.1.4 |
URIs and IRIs
| Flint | SPARQL name | Arglist | Reference |
|---|---|---|---|
iri | IRI | [expr] | 17.4.2.8 |
uri | URI | [expr] | 17.4.2.8 |
Blank Nodes
| Flint | SPARQL | Arglist | Reference |
|---|---|---|---|
bnode | BNODE | [] or [expr] | 17.4.2.9 |
UUIDs
| Flint | SPARQL | Arglist | Reference |
|---|---|---|---|
uuid | UUID | [] | 17.4.2.12 |
struuid | STRUUID | [] | 17.4.2.13 |
Predicates
String-specific predicates are listed under Strings and Language Maps. Note that bound takes a variable instead of another expression as its argument.
| Flint | SPARQL name | Arglist | Reference |
|---|---|---|---|
bound | BOUND | [var] | 17.4.1.1 |
sameterm | SAMETERM | [expr expr] | 17.4.1.8 |
iri? | isIRI | [expr] | 17.4.2.1 |
uri? | isURI | [expr] | 17.4.2.1 |
blank? | isBLANK | [expr] | 17.4.2.2 |
literal? | isLITERAL | [expr] | 17.4.2.3 |
numeric? | isNUMERIC | [expr] | 17.4.2.4 |
Strings and Language Maps
| Flint | SPARQL | Arglist | Reference |
|---|---|---|---|
str | STR | [expr] | 17.4.2.5 |
lang | LANG | [expr] | 17.4.2.6 |
strdt | STRDT | [expr expr] | 17.4.2.10 |
strlang | STRLANG | [expr expr] | 17.4.2.11 |
strlen | STRLEN | [expr] | 17.4.3.2 |
substr | SUBSTR | [expr expr] or [expr expr expr] | 17.4.3.3 |
ucase | UCASE | [expr] | 17.4.3.4 |
lcase | LCASE | [expr] | 17.4.3.5 |
strstarts | STRSTARTS | [expr expr] | 17.4.3.6 |
strends | STRENDS | [expr expr] | 17.4.3.7 |
contains | CONTAINS | [expr expr] | 17.4.3.8 |
strbefore | STRBEFORE | [expr expr] | 17.4.3.9 |
strafter | STRAFTER | [expr expr] | 17.4.3.10 |
encode-for-uri | ENCODE_FOR_URI | [expr] | 17.4.3.11 |
concat | CONCAT | [& exprs] | 17.4.3.12 |
lang-matches | LANGMATCHES | [expr expr] | 17.4.3.13 |
regex | REGEX | [expr expr] or [expr expr expr] | 17.4.3.14 |
replace | REPLACE | [expr expr expr] or [expr expr expr expr] | 17.4.3.15 |
Numerics
| Flint | SPARQL | Arglist | Reference |
|---|---|---|---|
abs | ABS | [expr] | 17.4.4.1 |
round | ROUND | [expr] | 17.4.4.2 |
ceil | CEIL | [expr] | 17.4.4.3 |
floor | FLOOR | [expr] | 17.4.4.4 |
rand | RAND | [] | 17.4.4.5 |
Dates and Times
| Flint | SPARQL | Arglist | Reference |
|---|---|---|---|
now | NOW | [] | 17.4.5.1 |
year | YEAR | [expr] | 17.4.5.2 |
month | MONTH | [expr] | 17.4.5.3 |
day | DAY | [expr] | 17.4.5.4 |
hours | HOURS | [expr] | 17.4.5.5 |
minutes | MINUTES | [expr] | 17.4.5.6 |
seconds | SECONDS | [expr] | 17.4.5.7 |
timezone | TIMEZONE | [expr] | 17.4.5.8 |
tz | TZ | [expr] | 17.4.5.9 |
Hash Functions
| Flint | SPARQL | Arglist | Reference |
|---|---|---|---|
md5 | MD5 | [expr] | 17.4.6.1 |
sha1 | SHA1 | [expr] | 17.4.6.2 |
sha256 | SHA256 | [expr] | 17.4.6.3 |
sha384 | SHA384 | [expr] | 17.4.6.4 |
sha512 | SHA512 | [expr] | 17.4.6.5 |
Aggregate Expressions
Aggregates are special expressions that can only be used in SELECT (and its DISTINCT and REDUCED variants), ORDER BY and HAVING clauses.
| Flint | SPARQL | Arglist |
|---|---|---|
sum | SUM | [expr & {:keys [distinct?]}] |
min | MIN | [expr & {:keys [distinct?]}] |
max | MAX | [expr & {:keys [distinct?]}] |
avg | AVG | [expr & {:keys [distinct?]}] |
sample | SAMPLE | [expr & {:keys [distinct?]}] |
count | COUNT | [expr-or-wildcard & {:keys [distinct?]}] |
group-concat | GROUP_CONCAT | [expr & {:keys [distinct? separator]}] |
The count aggregate can accept either an expression or a wildcard, e.g. both (count ?x) and (count *) are valid.
Unlike other expressions, aggregates in Flint support keyword arguments. Every aggregate function supports the :distinct? keyword arg, which accepts a boolean value. If :distinct? is true, then DISTINCT is added to the arg list in SPARQL. The example,
(sum ?x :distinct? true)
becomes
SUM(DISTINCT ?x)
group-concat also accepts the :separator keyword arg whose value is a separator string. Both :distinct? and :separator can be supported in the same expression, as so:
(group-concat ?y :distinct? true :separator ";")
which becomes
GROUP_CONCAT(DISTINCT ?y; SEPARATOR = ";")
NOTE: Using aggregates in an invalid clause, e.g. a FILTER clause, will cause a spec error.
NOTE: Using aggregates in a SELECT query, or including a GROUP BY in the query, introduces aggregate restrictions on the SELECT clause. See the SPARQL Queries page for more details.
Custom Functions
Users can write their own custom functions, which consist of using an IRI or prefixed IRI instead of a symbol. The example:
[:filter (:func/isEven ?x)]
becomes
FILTER func:isEven(?x)
NOTE: Flint makes no attempt to validate that custom function IRIs resolve to valid resources, nor does it to attempt to validate input or output.
In :select, :order-by and :having clauses, custom aggregates are allowed, and any custom function can accept the :distinct? keyword arg. During aggregate validation, all custom functions in these clauses are treated as aggregates.
Variable Binding
Variables can be bound to the result of expressions in :bind, :select, and :group-by clauses. In Flint, they are written in as the vector [expr var], such as in this example:
[(+ 2 2) ?four]
which then becomes:
(2 + 2) AS ?four
NOTE: In a :select or :bind clause, the variable being bound to cannot already be in-scope. In a :select clause, the var cannot be already defined in the :where clause, nor be previously projected in that same clause. In the :bind case, it cannot be already defined in previously-listed graph patterns.