Queries
A query is a document that reads facts and gives rows. It writes nothing. A
query needs find, and most queries add where.
Query execution measures CPU work on its thread, in 100-nanosecond ticks. Its budget includes planning, scans, expressions and result construction. Bounds describes the limits. The public API returns rows. Optional explain records give measurements for individual calls.
The examples with result blocks read these five entities. This merge patch creates them in a fresh database: Ada has ID 1, Bob 2, Cy 3, Dan 4 and Eve 5. The returned commit records these IDs for the patch's temporary names.
{:#_ada {:name "Ada"
:role "engineer"
:age 36
:dept "eng"
:salary 120}
:#_bob {:name "Bob"
:role "engineer"
:age 41
:mentor {:#link "#_ada"}
:dept "eng"
:salary 100}
:#_cy {:name "Cy"
:role "designer"
:age 29
:dept "sales"
:salary 90}
:#_dan {:name "Dan"
:dept "sales"
:salary 70}
:#_eve {:name "Eve"
:dept "ops"
:salary 80}}Find and where
find gives the columns. where gives the clauses that bind them. A variable
that appears in more than one clause joins those clauses.
{:find [?name ?sal]
:where [
[?e "dept" ?dept]
[?e "salary" ?sal]
[?e "name" ?name]]
:orderBy [?name]}{:value [
{:name "Ada"
:sal 120}
{:name "Bob"
:sal 100}
{:name "Cy"
:sal 90}
{:name "Dan"
:sal 70}
{:name "Eve"
:sal 80}]}Clauses
A where clause has one of these forms.
| Kind | Shape | Purpose |
|---|---|---|
| Fact | [?e field ?v] | Matches stored facts |
| Member | [member ?e field ?key ?v] | Matches members of an object field |
| Negation | [not [...]] | Drops each solution that matches inside |
| Predicate | [> ?v 5] | Keeps each solution the expression accepts |
| Derivation | [[expression] ?out] | Binds a calculated value to a new variable |
| Schema | [schema name ?e] | Keeps entities that match a stored schema |
A fact clause has an entity, a field and a value. Each position accepts a
variable, a literal, or _ for a position that you do not read. An entity
position also accepts an entity reference such as {#link 1}, the id of
that entity. A fourth term binds the transaction of the fact.
A member clause reads each immediate member of an object field. It binds the member name as a string and the member value as stored. A sixth term can bind the transaction of the field.
{:find [?key ?artist]
:where [["member" ?track "artists" ?key ?artist]]}For example, this field has one member. Its name is a MusicBrainz GID. Its value
is a link to artist entity 8306.
{:artists {:af1e76f062864cd4b4989a72a8df7de1 {:#link 8306}}}If you do not need the member name, use _. A bound link value and a known field
use the reference index for current reads. The engine then checks the current
object to exclude removed or changed links.
{:find [?title]
:where [
[?artist "name" "John Lennon"]
["member" ?track "artists" "_" ?artist]
[?track "track" "_"]
[?track "name" ?title]]}Member clauses accept objects. Arrays and tagged scalar values have no matching
members. Two keys with the same value produce two matches. Use distinct true
to remove repeated result rows. An ordinary fact clause still reads the complete
field value.
A negation drops each solution that matches the clause inside it.
{:find [?name]
:where [
[?e "name" ?name]
["not" [?e "dept" "eng"]]]
:orderBy [?name]}{:value [{:name "Cy"} {:name "Dan"} {:name "Eve"}]}A predicate keeps each solution that its expression accepts.
{:find [?name]
:where [
[?e "salary" ?sal]
[?e "name" ?name]
[">" ?sal 85]]
:orderBy [?name]}{:value [{:name "Ada"} {:name "Bob"} {:name "Cy"}]}A derivation calculates a value and binds it to a new variable. The expression comes first, and the new variable follows it.
{:find [?name ?double]
:where [
[?e "salary" ?sal]
[?e "name" ?name]
[["*" ?sal 2] ?double]]
:orderBy [?name]}{:value [
{:name "Ada"
:double 240}
{:name "Bob"
:double 200}
{:name "Cy"
:double 180}
{:name "Dan"
:double 140}
{:name "Eve"
:double 160}]}A schema clause keeps the entities whose current document matches a stored
schema. It names the schema by its path below definitions/schemas, without a
suffix. A stored schema supplies the validation rule.
Exact indexes
An exact index maps the values of a field to the entities that hold them. Stardust makes one for each field that a stored query or mutation compares with a value:
- a fact clause with a constant or parameter value, such as
[?e dept sales]or[?e dept ?dept]when?deptis a parameter. - a
=,<,<=,>or>=predicate on the value of a fact clause, such as[?e age ?age]with[>= ?age 18]. - an
inpredicate on the value of a fact clause, with a list of constants or a list parameter. For example,[?e name ?name]with[in ?name ?names]reads only the listed values.
The database maintains exact indexes for fields that stored definitions or ad-hoc native runs compare. A scan gives the same rows while an index is not yet active. The engine tracks index status internally.
Parameters
A ?variable that no clause binds becomes a required input when only a
predicate, a derivation or a having entry reads it. Stardust rejects an evaluation without a value for it.
{:find [?name]
:where [
[?e "salary" ?sal]
[?e "name" ?name]
[">" ?sal ?floor]]
:orderBy [?name]}Error
Missing_Parameter
The caller gives the value with the query. With floor set to 95:
- floor
- 95
{:value [{:name "Ada"} {:name "Bob"}]}A parameters block gives a default instead, which makes the input optional.
A declaration key uses ?name, like every other binding.
{:parameters {:?floor 95}
:find [?name]
:where [
[?e "salary" ?sal]
[?e "name" ?name]
[">" ?sal ?floor]]
:orderBy [?name]}{:value [{:name "Ada"} {:name "Bob"}]}A variable that no clause binds, and that no predicate reads, is an error. In
this query, no clause binds ?dept:
{:find [?name ?dept]
:where [[?e "name" ?name]]}Error
Unbound_Variable
Result mode
A query gives one object for each row, keyed by the find columns. With
resultMode positional, it gives one list for each row, in find order.
{:find [?name ?sal]
:where [
[?e "salary" ?sal]
[?e "name" ?name]]
:orderBy [?name]
:resultMode "positional"}{:value [
["Ada" 120]
["Bob" 100]
["Cy" 90]
["Dan" 70]
["Eve" 80]]}Project shapes each row into a document instead. Grouping adds aggregates and window functions.