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:

dust21B
#_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:

dust226B
#_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.

dust227B
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:

Result / 69B
[{who     Ada
  boss    Grace
  missing null
  info    {doubled 72}}]

Field kinds

The value determines the field kind.

ValueKindResult
Text that starts with .PathReads that field of the root entity
An objectFieldsA nested shape with its own fields
Anything elseExpressionAn 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.

dust222B
find    [?e]
where   [[?e name Ada]
         [?e age ?v]
         [?e name ?n]]
project {root   ?e
         fields {label [format
                        '{} is {}'
                        ?n
                        ?v]}}
Result / 21B
[{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/.

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

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

dust136B
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. Queries covers the temporal forms.

Run it

database query · query run.