raql is a small relational algebra query language implemented in Clojure. A
query is represented as a vector in prefix notation where the first element is
an operator followed by its arguments. The language can be compiled to an
abstract syntax tree (AST) and analysed or rewritten programmatically.
The following operators are available:
relation– reference a base relation by keywordrestrict– filter a relation using boolean expressions (=,<>,<,<=,>=,>,and,or,in)project– keep the listed attributesproject-away– remove the listed attributesrename– rename attributes using[old new]pairsdistinct– remove duplicate tuplesjoin,full-join,left-join,right-join– relational joinsunion– combine two relations with identical headingsorder-by– order tuples by attributeslimit– limit the number of tuples, optionally with an offsetoffset– skip the specified number of tuples
A query can be compiled with fooheads.raql.core/compile which returns an AST
enriched with heading information. The AST can be transformed back with
fooheads.raql.core/decompile.
(raql/compile
heading-relmap
inferrers
'[project
[restrict
[join
[relation :artist]
[relation :album]
[= :artist/id :album/artist-id]]
[= :artist/name "Jimi Hendrix"]]
[:artist/name]])The function fooheads.raql.syntactic-sugar/expand-threads expands ->
threading forms to normal prefix expressions. This makes it possible to write
queries in a more natural flow.
(sugar/expand-threads
'[-> [relation :artist]
[restrict [< :artist/id 1000]]
[rename {:artist/id :id}]
[project [:id]]
[offset 100]
[limit 10]])
;; => '[limit
;; [offset
;; [project
;; [rename
;; [restrict [relation :artist] [< :artist/id 1000]]
;; {:artist/id :id}]
;; [:id]]
;; 100]
;; 10]fooheads.raql.rewrite/apply-views replaces [relation <name>] expressions with
predefined query expressions. This enables view like abstractions when building
larger queries.
Limit and offset are separate, heading-preserving operators:
'[-> [relation :artist]
[order-by [:artist/artist-id]]
[offset 20]
[limit 10]]This skips 20 rows, then returns at most 10. The existing [limit 10 20]
threading step is an equivalent shorthand. Either operator can be used alone.
Ordering by a unique attribute, or a combination of attributes that is unique,
is needed for predictable pages.
Operator order matters: [limit 10] followed by [offset 3] leaves at most
seven rows from the original first ten, not ten rows starting at the fourth.
Pagination can also appear inside subqueries and other relational expressions.
An order-by after pagination sorts the selected subset instead of changing
which rows were originally selected.
Counts must be nonnegative integers or named parameter symbols such as
?page-size and ?start. Zero limit produces an empty relation; zero offset
skips nothing. Invalid counts and operator arities are rejected during
compilation. Named counts are validated when their values are bound by the
translator.
'[-> [relation :artist]
[order-by [:artist/artist-id]]
[offset ?start]
[limit ?page-size]]RAQL syntax and ASTs do not select a database. For SQL, see
raql-honeysql for database
configuration, named parameter binding, and database-specific ordering rules.
This project uses Malli for validation and Clojure for implementation. Run the test suite with:
clojure -X:testDistributed under the [Eclipse Public License 2.0](https://www.eclipse.org/ legal/epl-2.0/).