Project
A query gives rows, and ordering arranges them.
project shapes each row into the document that the caller needs.
The caller receives that document without a second assembly step.
The examples start from Ada, Bob and Cy. They also add Grace as Ada's manager. A reference points to an existing entity, so create Grace first:
{:#_grace {:name "Grace"}}The first commit returns Grace's ID, 1, in this fresh database. The next patch creates Ada, Bob and Cy with IDs 3, 4 and 5. Ada's manager references Grace:
{:#_ada {:name "Ada"
:role "engineer"
:age 36
:manager {:#link 1}}
:#_bob {:name "Bob"
:role "engineer"
:age 41
:mentor {:#link "#_ada"}}
:#_cy {:name "Cy"
:role "designer"
:age 29}}Root and fields
project has root and fields members. root is an expression that gives the
entity to read for each row. fields is the shape of the output, and it cannot be
empty.
{:find [?e]
:where [
[?e "name" "Ada"]
[?e "age" ?v]]
:project {:root ?e
:fields {:who ".name"
:boss ".manager.name"
:missing ".unknown"
:info {:doubled ["*" ?v 2]}}}}The query selects Ada and gives one row:
{:value [
{:who "Ada"
:boss "Grace"
:missing nil
:info {:doubled 72}}]}Field kinds
The value determines the field kind.
| Value | Kind | Result |
|---|---|---|
Text that starts with . | Path | Reads that field of the root entity |
| An object | Fields | A nested shape with its own fields |
| Anything else | Expression | An expression for each row |
A path follows entity references, so .manager.name reads the name of the
entity that manager refers to. A path that reaches nothing gives null
instead of an error, as .unknown does above.
A nested object gives a nested document. An expression reads the row's
variables, so info.doubled doubles ?v from the where clause.
Building text
An expression field builds text with format. The template
comes first, and each {} accepts the next argument.
{:find [?e]
:where [
[?e "name" "Ada"]
[?e "age" ?v]
[?e "name" ?n]]
:project {:root ?e
:fields {:label ["format" "{} is {}" ?n ?v]}}}{:value [{:label "Ada is 36"}]}An argument is an expression, never a path. A path reads a field only as the
whole value of a field, so .name inside format gives the text .name.
The query above binds ?n in where, which is how a field of the root
entity reaches a template.
Names and paths
A field name cannot be empty, and it cannot start with $. Each segment of a
path must be a name, so .. and a trailing . are errors. A segment cannot
use the reserved array storage prefix stardust/list/.
{:project {:root ?e
:fields {:who ".name."}}}Error
Invalid_Project
The path .name. ends with an empty segment. .name reads that field, and
.manager.name follows the reference in manager to read a field of another
entity.
Result mode
project selects positional rows when the query does not have resultMode.
You can also set resultMode positional.
resultMode object and project give two shapes for one row. A query that
contains both gives an error.
{:find [?e]
:where [
[?e "name" "Ada"]
[?e "age" ?v]]
:resultMode "object"
:project {:root ?e
:fields {:who ".name"}}}Error
Project_Result_Mode_Conflict
Project and page limits
A projected shape is not a page boundary, so project and page together are
an error.
{:find [?e]
:where [
[?e "name" "Ada"]
[?e "age" ?v]]
:orderBy [?v]
:page {:size 1}
:project {:root ?e
:fields {:who ".name"}}}Error
Invalid_Page
A projection reads the current document of each entity, so project with a
temporal read is also an error.