Appendix D: Datalog Built-Ins
This appendix lists the functions and predicates that Datalevin resolves without namespace qualification inside Datalog query clauses. It also lists the built-in aggregate functions available in :find and :having, plus the special forms that appear in :find and :where.
Most of these functions mirror clojure.core. Datalevin-specific functions are called out separately because they interact with the database, full-text search, vector indexes, idoc indexes, or runtime UDFs.
1. Where Built-Ins Can Appear
Built-ins can be used in predicate clauses:
[(> ?age 18)]
[(like ?name "A%")]
They can also be used in binding clauses:
[(+ ?subtotal ?tax) ?total]
[(get-some $ ?e :profile/email :user/email) [?attr ?email]]
Aggregate functions appear in :find and :having:
[:find ?city (count ?e)
:where [?e :user/city ?city]
:having [(> (count ?e) 10)]]
Fully qualified Clojure functions can also be used when they are available to the runtime, but they are not part of this built-in table.
On the JVM, a symbol whose name starts with . is treated as a reflective instance-method call on its first argument:
[(.toUpperCase ?name) ?upper]
This is useful for local Clojure/JVM queries, but it is not a portable cross-language pattern. Prefer documented built-ins or runtime UDFs when the query will cross process or language boundaries.
Find-only forms appear in :find, not as ordinary :where clauses:
| Form | Purpose |
|---|---|
(pull ?e pattern) |
Pull a shaped map for each entity found by the query. |
(pull $source ?e pattern) |
Pull from an explicit query source. |
(sum ?x), (count ?x), etc. |
Built-in aggregate calls. |
(+ (sum ?x) (sum ?y)) |
Arithmetic expression over aggregate results. |
(aggregate ?f ?x) |
Custom aggregate form, where ?f is an aggregate function input. |
:where also has special clause forms. These are parsed as Datalog clauses, not resolved as ordinary functions:
| Form | Purpose |
|---|---|
(or clause...) |
Match one of several alternative branches. |
(or-join [?var ...] branch...) |
Match alternatives with explicit branch output variables. |
(not clause...) |
Exclude rows for which the nested clauses match. |
(not-join [?var ...] clause...) |
Exclude rows with explicit variables connecting the nested clauses to the surrounding query. |
(and clause...) |
Group multiple clauses inside an or or or-join branch. |
Do not confuse these clause forms with predicate calls such as [(not ?flag)] or [(or ?a ?b)]. Bracketed predicate clauses call functions; the forms above contain nested Datalog clauses. Chapter 8 covers their evaluation rules and examples.
2. Predicates and Comparisons
| Function | Purpose |
|---|---|
= |
Equality predicate. |
== |
Numeric equality predicate. |
not= |
Inequality predicate. |
!= |
Alias for not=. |
< |
Datalevin typed less-than comparison. |
<= |
Datalevin typed less-than-or-equal comparison. |
> |
Datalevin typed greater-than comparison. |
>= |
Datalevin typed greater-than-or-equal comparison. |
compare |
Clojure-style comparison function. |
true? |
True only for the boolean value true. |
false? |
True only for the boolean value false. |
nil? |
True for nil. |
some? |
True for non-nil values. |
not |
Logical negation. |
and |
Logical conjunction over arguments. |
or |
Logical disjunction over arguments. |
complement |
Return a function that negates another predicate. |
identical? |
Reference identity predicate. |
zero? |
Numeric zero predicate. |
pos? |
Positive-number predicate. |
neg? |
Negative-number predicate. |
even? |
Even integer predicate. |
odd? |
Odd integer predicate. |
in |
Membership predicate, e.g. [(in ?status [:open :ready])]. |
not-in |
Negated membership predicate. |
like |
SQL LIKE-style string predicate using % and _ wildcards; accepts {:escape ?esc} as an optional third argument. |
not-like |
Negated like predicate; also accepts {:escape ?esc}. |
re-find |
Regex search. |
re-matches |
Regex full-match test. |
3. Numeric Functions
| Function | Purpose |
|---|---|
+ |
Addition. |
- |
Subtraction or numeric negation. |
* |
Multiplication. |
/ |
Division. |
quot |
Integer quotient. |
rem |
Remainder. |
mod |
Modulus. |
inc |
Increment. |
dec |
Decrement. |
min |
Minimum value using Datalevin typed comparison. |
max |
Maximum value using Datalevin typed comparison. |
rand |
Random floating-point number. |
rand-int |
Random integer below a bound. |
4. General Value and Collection Functions
| Function | Purpose |
|---|---|
identity |
Return its argument unchanged. |
ground |
Return a literal or otherwise-bound argument unchanged; an unbound argument is invalid. |
quote |
Return its argument unchanged; useful for nested query literals in EDN strings. |
keyword |
Create a keyword. |
meta |
Return metadata. |
name |
Return the name part of a keyword or symbol. |
namespace |
Return the namespace part of a keyword or symbol. |
type |
Return the Java/Clojure runtime type. |
vector |
Construct a vector. |
list |
Construct a list. |
set |
Construct a set. |
hash-map |
Construct a hash map. |
array-map |
Construct an array map. |
count |
Count items in a collection or characters in a string. |
range |
Produce a numeric range. |
not-empty |
Return a collection if non-empty, otherwise nil. |
empty? |
True for empty collections. |
contains? |
True when a key/index is present. |
get |
Look up a value in a collection or map. |
apply |
Apply a built-in or fully qualified function to a sequence of arguments. |
tuple |
Construct one vector from its arguments; equivalent to vector in query context. |
untuple |
Return its argument unchanged so a tuple binding can destructure it. |
Examples:
;; Bind each vowel from a literal collection.
(d/q '[:find ?vowel
:where [(ground [:a :e :i :o :u]) [?vowel ...]]])
;; Construct one tuple, then bind its positions.
(d/q '[:find ?key ?sku2 ?region2
:where [(ground "book") ?sku]
[(ground "us") ?region]
[(tuple ?sku ?region) ?key]
[(untuple ?key) [?sku2 ?region2]]])
;; => #{[["book" "us"] "book" "us"]}
;; Build transaction data in a query.
(d/q '[:find [?tx-data ...]
:where [(ground :db/add) ?op]
[(vector ?op -1 :user/name "Alice") ?tx-data]])
;; Sum a tuple stored as a value.
(d/q '[:find ?e ?sum
:where [?e :nums ?nums]
[(apply + ?nums) ?sum]]
db)
ground and untuple are identity functions at runtime: neither expands nor decomposes its argument. ground accepts a literal or a variable bound by another clause or query input; an unbound variable produces an insufficient- binding error. The binding shape on the right performs the expansion or destructuring. For example, [?vowel ...] expands a collection, while [?sku2 ?region2] binds positions from one tuple. tuple constructs the vector that represents one query-time tuple value. These helpers are distinct from schema tuple features such as :db.type/tuple and :db/tupleAttrs.
5. String and Regex Functions
| Function | Purpose |
|---|---|
str |
Concatenate values as strings. |
pr-str |
Print values readably to a string. |
print-str |
Print values to a string using print semantics. |
println-str |
Print values with a trailing newline. |
prn-str |
Print values readably with a trailing newline. |
subs |
Return a substring. |
re-pattern |
Compile a regex pattern. |
re-find |
Find a regex match. |
re-matches |
Match an entire string against a regex. |
re-seq |
Return a sequence of regex matches. |
6. Database-Aware Functions
These functions take a Datalevin DB object, usually $, or interact with Datalevin indexes and runtime facilities.
| Function | Purpose |
|---|---|
get-else |
Return an entity attribute value or a non-nil default. |
get-some |
Return the first present attribute and value from a list of attributes. |
get-some-else |
Return the first present attribute and value, or [nil fallback]. |
missing? |
True when an entity has no value for an attribute. |
q |
Run a nested Datalog query; useful for factoring an aggregate or small result relation into a later join. |
fulltext |
Search full-text indexes and bind matching [e a v] tuples. |
vec-neighbors |
Search vector indexes with a vector query and bind matching tuples. |
embedding-neighbors |
Embed a text query and search embedding indexes. |
idoc-match |
Search indexed document attributes and bind matching tuples. |
idoc-get |
Extract a nested value from a bound idoc document by path. |
udf |
Invoke a registered runtime UDF descriptor in query context. |
6.1 Attribute Fallbacks
;; Bind either the user's height or a default.
(d/q '[:find ?e ?height
:where [?e :user/name]
[(get-else $ ?e :user/height 0) ?height]]
db)
;; Bind the first present contact attribute.
(d/q '[:find ?e ?attr ?value
:where [?e :user/name]
[(get-some $ ?e :user/email :user/phone) [?attr ?value]]]
db)
;; Keep the entity when neither contact attribute is present.
(d/q '[:find ?e ?attr ?value
:where [?e :user/name]
[(get-some-else $ ?e "unavailable"
:user/email :user/phone)
[?attr ?value]]]
db)
;; Filter entities that do not have an attribute.
(d/q '[:find ?e
:where [?e :user/name]
[(missing? $ ?e :user/deleted-at)]]
db)
get-else does not accept nil as its default value. Use a concrete sentinel or handle the absence with missing?.
get-some and get-some-else try the listed cardinality-one attributes in order. Both return [attribute value] for the first one present. When none is present, get-some returns no value, so its binding clause removes that input row. get-some-else instead returns [nil fallback], preserving one result tuple for the input row. Its fallback appears before the attribute list and may itself be nil:
[(get-some-else $ ?e nil :user/email :user/phone) [?attr ?value]]
6.2 Nested Queries
(d/q '[:find ?e ?age
:where [(q (quote [:find (min ?age)
:where [_ :user/age ?age]])
$) [[?age]]]
[?e :user/age ?age]]
db)
Nested q is available, but it can be expensive when executed per candidate tuple. It is useful when the query genuinely has separate stages, as above: the uncorrelated inner query computes the global minimum, and the outer query joins that aggregate result back to every matching entity. Inner variables are local to the nested query; its result binding is the explicit interface to the outer query. Prefer direct joins, not-join, rules, or aggregates when they express the same operation without a per-candidate subquery.
6.3 Full-Text Search
;; Search all full-text domains.
(d/q '[:find ?e ?a ?v
:in $ ?query
:where [(fulltext $ ?query) [[?e ?a ?v]]]]
db "red fox")
;; Search one attribute-specific domain.
(d/q '[:find ?e ?a ?v
:in $ ?query
:where [(fulltext $ :article/body ?query) [[?e ?a ?v]]]]
db "red fox")
;; Search explicit full-text domains.
(d/q '[:find ?e ?a ?v
:in $ ?query
:where [(fulltext $ ?query {:domains ["articles"]})
[[?e ?a ?v]]]]
db "red fox")
By default, fulltext returns tuples that destructure as [e a v]. With {:display :refs+scores}, bind [e a v score]; with :texts, bind [e a v text]; with :offsets, bind [e a v offsets]; and with :texts+offsets, bind [e a v text offsets]. Attribute-specific search requires :db.fulltext/autoDomain true on the attribute. Phrase search and offset display require the relevant search domain to be configured with :index-position? true before indexing. Text display requires :include-text? true; full-text indexes store document references by default, not a duplicate raw-text copy.
For a finite relation query, fulltext can be an optimizer-selected complete, resumable access path. A single-domain :refs+scores result also provides descending score order when the score is the leading :order-by term. The function's own :top, :limit, and :offset remain source-local; query-level bounds apply after the rest of the Datalog query.
6.4 Vector and Embedding Search
Vector and embedding search is always domain-scoped. Unlike fulltext and idoc-match, vec-neighbors and embedding-neighbors do not search all domains when no domain is supplied. Use an attribute-specific call or pass an options map with :domains.
;; Vector query against an attribute-specific vector domain.
(d/q '[:find ?e ?a ?v
:in $ ?query-vector
:where [(vec-neighbors $ :item/embedding ?query-vector)
[[?e ?a ?v]]]]
db query-vector)
;; Vector query against explicit vector domains.
(d/q '[:find ?e ?a ?v
:in $ ?query-vector
:where [(vec-neighbors $ ?query-vector {:domains ["items"]})
[[?e ?a ?v]]]]
db query-vector)
;; Text query against an attribute-specific embedding domain.
(d/q '[:find ?e ?a ?v
:in $ ?text
:where [(embedding-neighbors $ :article/body ?text)
[[?e ?a ?v]]]]
db "database internals")
With {:display :refs+dists}, vector and embedding searches can bind [e a v distance]. Attribute-specific embedding search requires :db.embedding/autoDomain true on the attribute. Explicit embedding-domain search uses the provider configured for each named domain.
For a finite relation query, either neighbor function can provide a complete, resumable access path over its existing approximate result. A single-domain :refs+dists result provides ascending distance order when distance is the leading :order-by term. The optimizer does not widen :top or turn the HNSW result into exact nearest-neighbor search.
6.5 Indexed Documents
;; Search idoc documents by query map.
(d/q '[:find ?e ?a ?doc
:where [(idoc-match $ {:status "active"}) [[?e ?a ?doc]]]]
db)
;; Extract a nested field from the matched document.
(d/q '[:find ?e ?email
:where [(idoc-match $ :person/profile {:status "active"})
[[?e ?a ?doc]]]
[(idoc-get ?doc :contact :email) ?email]]
db)
;; Search explicit idoc domains.
(d/q '[:find ?e ?a ?doc
:where [(idoc-match $ {:status "active"} {:domains ["profiles"]})
[[?e ?a ?doc]]]]
db)
Attribute-specific idoc-match requires an attribute with :db/valueType :db.type/idoc. idoc-get accepts path segments as separate arguments or a single vector path.
An uncorrelated idoc-match can provide a complete, resumable access path. Finite unordered relation queries may consume it in batches until enough final tuples survive later joins and filters. Idoc matching advertises no ranked order, so a limited result without :order-by has no stable ordering guarantee.
6.6 Runtime UDFs
(d/q '[:find ?normalized
:in $ ?email
:where [(udf :normalize-email ?email) ?normalized]]
db
"ALICE@EXAMPLE.COM")
The descriptor must be installed in the database or available from the runtime UDF registry, and its kind must be usable as a query function or predicate. Clojure, Java, Python, and JavaScript use the same Datalog udf form; only the registry helper names differ by host language.
In server mode, query resolution is server-safe: remote queries can call Datalevin built-ins and descriptor-backed UDFs registered on the server, but cannot resolve arbitrary host-language functions supplied by the client. Use udf for custom query logic that must run in a server process.
7. Aggregate Functions
Aggregate functions are used in :find, aggregate expressions, and :having.
| Function | Forms | Purpose |
|---|---|---|
sum |
(sum ?x) |
Sum numeric values. |
avg |
(avg ?x) |
Arithmetic mean. |
median |
(median ?x) |
Median value. |
variance |
(variance ?x) |
Population variance. |
stddev |
(stddev ?x) |
Population standard deviation. |
count |
(count ?x) |
Count values. |
count-distinct |
(count-distinct ?x) |
Count distinct values. |
distinct |
(distinct ?x) |
Return a set of distinct values. |
vec |
(vec ?x) |
Return a vector of grouped values. |
min |
(min ?x), (min n ?x) |
Smallest value, or vector of the n smallest values. |
max |
(max ?x), (max n ?x) |
Largest value, or vector of the n largest values. |
rand |
(rand ?x), (rand n ?x) |
Random value, or vector of n random draws. |
sample |
(sample n ?x) |
Vector of up to n sampled values. |
The table above lists built-in aggregates that are available without resolving host functions. A query can also use custom aggregates when the runtime permits them. The custom aggregate form (aggregate ?f ?x) receives the collector function as an input. Host-resolved aggregate symbols beyond the built-ins above depend on resolver mode and are not portable to server-safe query execution.
Aggregate expressions can combine aggregate results:
(d/q '[:find ?name (+ (sum ?debits) (sum ?credits))
:in [[?name ?debits ?credits]]]
rows)
Aggregate expressions are evaluated after grouping. The supported expression operators are +, -, *, /, mod, rem, and quot; arguments may be aggregate calls, constants, or nested aggregate expressions.
Custom aggregate functions are also supported with the special aggregate form:
(d/q '[:find ?group (aggregate ?agg ?x)
:in [[?group ?x]] ?agg]
rows
my-aggregate-fn)
8. Public Name Index
This index lists the public query names documented for application use. Internal resolver entries, such as implementation helper predicates with leading hyphens, are intentionally omitted.
Query functions and predicates:
!=, *, +, -, /, <, <=, =, ==, >, >=, and, apply, array-map, compare, complement, contains?, count, dec, embedding-neighbors, empty?, even?, false?, fulltext, get, get-else, get-some, get-some-else, ground, hash-map, identical?, identity, idoc-get, idoc-match, in, inc, keyword, like, list, max, meta, min, missing?, mod, name, namespace, neg?, nil?, not, not-empty, not-in, not-like, not=, odd?, or, pos?, pr-str, print-str, println-str, prn-str, q, quot, quote, rand, rand-int, range, re-find, re-matches, re-pattern, re-seq, rem, set, some?, str, subs, true?, tuple, type, udf, untuple, vec-neighbors, vector, zero?.
Find-only forms:
pull, aggregate, arithmetic aggregate expressions using +, -, *, /, mod, rem, and quot.
JVM method-call forms:
Symbols beginning with ., such as .toUpperCase.
Aggregate functions:
avg, count, count-distinct, distinct, max, median, min, rand, sample, stddev, sum, variance, vec.
Other host-resolved aggregate functions are allowed only when the resolver mode permits host function resolution, or when supplied through an explicit custom aggregate mechanism.
User Examples
Log in to create examplesNo examples for this chapter yet.
