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.

edn518 B
{:#_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.

edn118 B
{:find    [?name ?sal]
 :where   [
   [?e "dept" ?dept]
   [?e "salary" ?sal]
   [?e "name" ?name]]
 :orderBy [?name]}
Result / 162 B
{: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.

KindShapePurpose
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.

edn74 B
{: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.

edn60 B
{: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.

edn155 B
{: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.

edn99 B
{:find    [?name]
 :where   [
   [?e "name" ?name]
   ["not" [?e "dept" "eng"]]]
 :orderBy [?name]}
Result / 51 B
{:value [{:name "Cy"} {:name "Dan"} {:name "Eve"}]}

A predicate keeps each solution that its expression accepts.

edn109 B
{:find    [?name]
 :where   [
   [?e "salary" ?sal]
   [?e "name" ?name]
   [">" ?sal 85]]
 :orderBy [?name]}
Result / 51 B
{: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.

edn126 B
{:find    [?name ?double]
 :where   [
   [?e "salary" ?sal]
   [?e "name" ?name]
   [["*" ?sal 2] ?double]]
 :orderBy [?name]}
Result / 185 B
{: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 ?dept is a parameter.
  • a =, <, <=, > or >= predicate on the value of a fact clause, such as [?e age ?age] with [>= ?age 18].
  • an in predicate 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.

edn113 B
{: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:

Result / 38 B
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.

edn148 B
{:parameters {:?floor 95}
 :find       [?name]
 :where      [
   [?e "salary" ?sal]
   [?e "name" ?name]
   [">" ?sal ?floor]]
 :orderBy    [?name]}
Result / 38 B
{: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:

edn50 B
{: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.

edn132 B
{:find       [?name ?sal]
 :where      [
   [?e "salary" ?sal]
   [?e "name" ?name]]
 :orderBy    [?name]
 :resultMode "positional"}
Result / 82 B
{: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.

Run it

database query · query run.