Shapes

A shape is a powerful syntactic construct that can be used to dynamically describe a portion of an object graph. Shapes are used to describe views, insert and update data and to specify the format of statement output.

Shapes always follow an expression, and are a list of shape elements enclosed in curly braces:

expr "{"
    shape_element [, ...]
"}"

Shape element has the following syntax:

[ "[" IS object-type "]" ] pointer-spec

If an optional object-type filter is used, pointer-spec will only apply to those objects in the expr set that are instances of object-type.

pointer-spec is one of the following:

  • a name of an existing link or property of a type produced by expr;

  • a declaration of a computable link or property in the form [@]<name> := <ptrexpr>;

  • a subshape in the form pointer-name: [target-type] "{" ... "}", where pointer-name is the name of an existing link or property, and target-type is an optional object type that specifies the type of target objects selected or inserted, depending on the context.

A shape in an UPDATE statement is used to specify how links and properties of an object are updated.

UPDATE Issue
FILTER .name = 'Issue #1'
SET
# Update shape follows
{
    name := 'Issue #1 (important)',
    comments := Issue.comments UNION (INSERT Comment {
                    body := 'Issue #1 updated'
                })
};

The above statement updates the name property and adds a comments link to a new comment for a given Issue object.

See UPDATE for more information on the use of shapes in UPDATE statements.

A shape in a SELECT clause (or the UNION clause of a FOR statement) determines the output format for the objects in a set computed by an expression annotated by the shape.

For example, the below query returns a set of Issue objects and includes a number and an associated owner User object, which in turn includes the name and the email for that user.

db> 
... 
... 
... 
... 
... 
... 
... 
SELECT
    Issue {
        number,
        owner: {  # sub-shape, selects Issue.owner objects
           name,
           email
        }
    };

{
    'number': 1,
    'owner': {
        'name': 'Alice',
        'email': 'alice@example.com'
    }
}

Typically the cardinality of an expression can be statically determined from the individual parts. Sometimes it is necessary to specify the cardinality explicitly. For example, when using computables in shapes it may be desirable to specify the cardinality of the computable because it affects serialization.

WITH
    MODULE example
SELECT User {
    name,
    multi nicknames := (SELECT 'Foo')
};

Cardinality is normally statically inferred from the query, so overruling this inference may only be done to relax the cardinality, so it is not valid to specify the single qualifier for a computable expression that may return multiple items.