From 4a9f347555a7eec2cc8554808c3b712a23944e73 Mon Sep 17 00:00:00 2001 From: Yury Selivanov Date: Wed, 19 Feb 2025 15:54:12 -0800 Subject: [PATCH] Properties & link properties --- docs/cheatsheets/index.rst | 3 +- docs/cheatsheets/link_properties.rst | 328 -------------- docs/datamodel/index.rst | 1 + docs/datamodel/linkprops.rst | 335 +++++++++++++++ docs/datamodel/links.rst | 5 +- docs/datamodel/properties.rst | 619 +++++++++++++++++++++++++-- docs/edgeql/sets.rst | 2 +- docs/reference/ddl/index.rst | 1 - docs/reference/ddl/properties.rst | 399 ----------------- docs/reference/sdl/index.rst | 6 - docs/reference/sdl/properties.rst | 188 -------- 11 files changed, 920 insertions(+), 967 deletions(-) delete mode 100644 docs/cheatsheets/link_properties.rst create mode 100644 docs/datamodel/linkprops.rst delete mode 100644 docs/reference/ddl/properties.rst delete mode 100644 docs/reference/sdl/properties.rst diff --git a/docs/cheatsheets/index.rst b/docs/cheatsheets/index.rst index 1992201f521..3296753151c 100644 --- a/docs/cheatsheets/index.rst +++ b/docs/cheatsheets/index.rst @@ -15,7 +15,6 @@ Cheatsheets insert update delete - link_properties boolean objects functions @@ -52,7 +51,7 @@ Schema new types and modify existing ones on the fly. * :ref:`Schema Annotations ` -- Add human readable descriptions to items in your schema. -* :ref:`Link Properties ` -- Links can contain properties +* :ref:`Link Properties ` -- Links can contain properties used to store metadata about the link. CLI/Admin diff --git a/docs/cheatsheets/link_properties.rst b/docs/cheatsheets/link_properties.rst deleted file mode 100644 index 194fe55f4c1..00000000000 --- a/docs/cheatsheets/link_properties.rst +++ /dev/null @@ -1,328 +0,0 @@ -.. _ref_guide_linkprops: - -===================== -Using link properties -===================== - -:index: property - - -Links can contain **properties**. These are distinct from links themselves -(which we refer to as simply "links") and are used to store metadata about a -link. Due to how they're persisted under the hood, link properties have a few -additional constraints: they're always *single* and *optional*. - -In thinking about how to use link properties, keep in mind that they are link -**properties**, not link **links**. This means they can contain only primitive -data (scalars, enums, arrays, or tuples). - -.. note:: - - In practice, link properties are best used with many-to-many relationships - (``multi`` links without any exclusive constraints). For one-to-one, - one-to-many, and many-to-one relationships the same data should be stored in - object properties instead. - - -Declaration ------------ - -Let's a create a ``Person.friends`` link with a ``strength`` property -corresponding to the strength of the friendship. - -.. code-block:: sdl - - type Person { - required name: str { constraint exclusive }; - - multi friends: Person { - strength: float64; - } - } - -Constraints ------------ - -.. code-block:: sdl - - type Person { - required name: str { constraint exclusive }; - - multi friends: Person { - strength: float64; - constraint expression on ( - __subject__@strength >= 0 - ); - } - } - -Indexes -------- - -To index on a link property, you must declare an abstract link and extend it. - -.. code-block:: sdl - - abstract link friendship { - strength: float64; - index on (__subject__@strength); - } - - type Person { - required name: str { constraint exclusive }; - multi friends: Person { - extending friendship; - }; - } - -Conceptualizing link properties -------------------------------- - -A good way to conceptualize the difference between a regular property and -a link property for object types is that regular properties are used to -*construct* expressions that return object types, while link properties are -*appended* to expressions that return object types to qualify the link. - -For example, the properties ``name`` and ``email`` are used to construct a -``Person`` object that is inserted, also returning the same ``Person`` object. - -.. code-block:: edgeql - - insert Person { - name := "Jane", - email := "jane@jane.com" - } - -If this ``Person`` object is inserted while linking it to another ``Person`` -object we are inserting, we can append a ``@strength`` property to the link. -``@strength`` is not used to construct a ``Person``, but to quantify the link. - -.. code-block:: edgeql - - insert Person { - name := "Bob", - email := "bob@bob.com", - friends := ( - insert Person { - name := "Jane", - email := "jane@jane.com", - @strength := 3.14 - } - ) - } - -Keep this in mind when reading through the following examples. - -Inserting ---------- - -The ``@strength`` property is specified in the *shape* of the ``select`` -subquery. This is only valid in a subquery *inside* an ``insert`` statement. - -.. code-block:: edgeql - - insert Person { - name := "Bob", - friends := ( - select detached Person { - @strength := 3.14 - } - filter .name = "Alice" - ) - } - - -.. note:: - - We are using the :eql:op:`detached` operator to unbind the - ``Person`` reference from the scope of the ``insert`` query. - - -When doing a nested insert, link properties can be directly included in the -inner ``insert`` subquery. The query below creates a link to a ``Person`` -object that is being inserted in the same query, along with a link property -``strength`` that has a value of 3.14. - -.. code-block:: edgeql - - insert Person { - name := "Bob", - friends := ( - insert Person { - name := "Jane", - @strength := 3.14 - } - ) - } - -Similarly, ``with`` can be used to capture an expression returning an -object type, after which a link property can be added when linking it to -another object type: - -.. code-block:: edgeql - - with - _friends := ( - insert Person { - name := "Alice" - } unless conflict on .name - else (select Person filter .name = "Alice" limit 1 ) - ) - insert Person { - name := "Bob", - friends := _friends { - @strength := 3.14 - } - }; - -Updating --------- - -.. code-block:: edgeql - - update Person - filter .name = "Bob" - set { - friends += ( - select .friends { - @strength := 3.7 - } - filter .name = "Alice" - ) - }; - -The example updates the ``@strength`` property of Bob's friends link to -Alice to 3.7. - -In the context of multi links the += operator works like an an insert/update -operator. - -To update one or more links in a multi link, you can select from the current -linked objects, as the example does. Use a ``detached`` selection if you -want to insert/update a wider selection of linked objects instead. - - -Querying --------- - -.. code-block:: edgeql-repl - - gel> select Person { - .... name, - .... friends: { - .... name, - .... @strength - .... } - .... }; - { - default::Person {name: 'Alice', friends: {}}, - default::Person { - name: 'Bob', - friends: { - default::Person {name: 'Alice', @strength: 3.7} - } - }, - } - -.. warning:: - - A link property cannot be referenced in a set union *except* in the case of - a :ref:`for loop `. That means this will *not* work: - - .. code-block:: edgeql - - # 🚫 Does not work - insert Movie { - title := 'The Incredible Hulk', - actors := {( - select Person { - @character_name := 'The Hulk' - } filter .name = 'Mark Ruffalo' - ), - ( - select Person { - @character_name := 'Abomination' - } filter .name = 'Tim Roth' - )} - }; - - That query will produce an error: ``QueryError: invalid reference to link - property in top level shape`` - - You can use this workaround instead: - - .. code-block:: edgeql - - # ✅ Works! - insert Movie { - title := 'The Incredible Hulk', - actors := assert_distinct(( - with characters := { - ('The Hulk', 'Mark Ruffalo'), - ('Abomination', 'Tim Roth') - }, - for character in characters union ( - select Person { - @character_name := character.0 - } filter .name = character.1 - ) - )) - }; - - Note that we are also required to wrap the ``actors`` query with - :eql:func:`assert_distinct` here to assure the compiler that the result set - is distinct. - - Specifying link properties of a computed backlink in your shape is also - supported. If you have this schema: - - .. code-block:: sdl - - type Person { - required name: str; - multi follows: Person { - followed: datetime { - default := datetime_of_statement(); - }; - }; - multi link followers := . Links > Link properties - ` - * - :ref:`SDL > Properties ` - * - :ref:`DDL > Properties ` - * - :ref:`Introspection > Object Types - ` diff --git a/docs/datamodel/index.rst b/docs/datamodel/index.rst index 54a9d372e1a..23ef1ddfad3 100644 --- a/docs/datamodel/index.rst +++ b/docs/datamodel/index.rst @@ -27,6 +27,7 @@ Schema triggers mutation_rewrites inheritance + linkprops extensions future comparison diff --git a/docs/datamodel/linkprops.rst b/docs/datamodel/linkprops.rst new file mode 100644 index 00000000000..6cb6213698f --- /dev/null +++ b/docs/datamodel/linkprops.rst @@ -0,0 +1,335 @@ +.. _ref_datamodel_linkprops: + +=============== +Link properties +=============== + +:index: property, link property + + +Links, like objects, can also contain **properties**. These are used to store metadata about the link. Due to how they're persisted under the hood, +link properties have a few additional constraints: they're always ``single`` +and ``optional``. + +Link properties require non-trivial syntax to use them, so they are considered +to be an advanced feature. In many cases, regular properties should be used +instead. To paraphrase a famous quote: "Link properties are like a parachute, +you don't need them very often, but when you do, they can be clutch." + +.. note:: + + In practice, link properties are best used with many-to-many relationships + (``multi`` links without any exclusive constraints). For one-to-one, + one-to-many, and many-to-one relationships the same data should be stored in + object properties instead. + + +Declaration +=========== + +Let's a create a ``Person.friends`` link with a ``strength`` property +corresponding to the strength of the friendship. + +.. code-block:: sdl + + type Person { + required name: str { constraint exclusive }; + + multi friends: Person { + strength: float64; + } + } + +Constraints +=========== + +Now let's ensure that the ``@strength`` property is always non-negative: + +.. code-block:: sdl + + type Person { + required name: str { constraint exclusive }; + + multi friends: Person { + strength: float64; + + constraint expression on ( + __subject__@strength >= 0 + ); + } + } + +Indexes +======= + +To add an index on a link property, we have to refactor our code and define +an abstract link ``friendship`` that will contain the ``strength`` property +with an index on it: + + +.. code-block:: sdl + + abstract link friendship { + strength: float64; + index on (__subject__@strength); + } + + type Person { + required name: str { constraint exclusive }; + + multi friends: Person { + extending friendship; + }; + } + +Conceptualizing link properties +=============================== + +A way to conceptualize the difference between a regular property and +a link property is that regular properties are used to construct an object, +while link properties are used to construct the link between objects. + +For example, here the ``name`` and ``email`` properties are used to construct a +``Person`` object: + +.. code-block:: edgeql + + insert Person { + name := "Jane", + email := "jane@jane.com" + } + +Now let's insert a ``Person`` object linking it to another ``Person`` object +setting the ``@strength`` property to the link between them: + +.. code-block:: edgeql + + insert Person { + name := "Bob", + email := "bob@bob.com", + friends := ( + insert Person { + name := "Jane", + email := "jane@jane.com", + @strength := 3.14 + } + ) + } + +So we're not using ``@strength`` to construct a particular ``Person`` object, +but to quantify a link between two ``Person`` objects. + +Inserting +========= + +What if we want to insert a ``Person`` object while linking it to another +``Person`` that's already in the database? + +The ``@strength`` property then will be specified in the *shape* of a +``select`` subquery: + +.. code-block:: edgeql + + insert Person { + name := "Bob", + friends := ( + select detached Person { + @strength := 3.14 + } + filter .name = "Alice" + ) + } + +.. note:: + + We are using the :eql:op:`detached` operator to unbind the + ``Person`` reference from the scope of the ``insert`` query. + +When doing a nested insert, link properties can be directly included in the +inner ``insert`` subquery: + +.. code-block:: edgeql + + insert Person { + name := "Bob", + friends := ( + insert Person { + name := "Jane", + @strength := 3.14 + } + ) + } + +Similarly, ``with`` can be used to capture an expression returning an +object type, after which a link property can be added when linking it to +another object type: + +.. code-block:: edgeql + + with + alice := ( + + insert Person { + name := "Alice" + } + unless conflict on .name + else ( + select Person + filter .name = "Alice" limit 1 + ) + ) + + insert Person { + name := "Bob", + friends := alice { + @strength := 3.14 + } + }; + +Updating +======== + +.. code-block:: edgeql + + update Person + filter .name = "Bob" + set { + friends += ( + select .friends { + @strength := 3.7 + } + filter .name = "Alice" + ) + }; + +The example updates the ``@strength`` property of Bob's friends link to +Alice to 3.7. + +In the context of multi links the ``+=`` operator works like an +an insert/update operator. + +To update one or more links in a multi link, you can select from the current +linked objects, as the example does. Use a ``detached`` selection if you +want to insert/update a wider selection of linked objects instead. + + +Selecting +========= + +To select a link property, you can use the ``@<>name`` syntax inside the +select *shape*. Keep in mind, that you're not selecting a property on +an object with this syntax, but rather on the link, in this case ``friends``: + +.. code-block:: edgeql-repl + + gel> select Person { + .... name, + .... friends: { + .... name, + .... @strength + .... } + .... }; + { + default::Person {name: 'Alice', friends: {}}, + default::Person { + name: 'Bob', + friends: { + default::Person {name: 'Alice', @strength: 3.7} + } + }, + } + +Unions +====== + +A link property cannot be referenced in a set union *except* in the case of +a :ref:`for loop `. That means this will *not* work: + +.. code-block:: edgeql + + # 🚫 Does not work + insert Movie { + title := 'The Incredible Hulk', + actors := {( + select Person { + @character_name := 'The Hulk' + } filter .name = 'Mark Ruffalo' + ), + ( + select Person { + @character_name := 'Iron Man' + } filter .name = 'Robert Downey Jr.' + )} + }; + +That query will produce an error: ``QueryError: invalid reference to link +property in top level shape`` + +You can use this workaround instead: + +.. code-block:: edgeql + + # ✅ Works! + insert Movie { + title := 'The Incredible Hulk', + + actors := assert_distinct(( + with characters := { + ('The Hulk', 'Mark Ruffalo'), + ('Iron Man', 'Robert Downey Jr.') + } + for character in characters union ( + select Person { + @character_name := character.0 + } filter .name = character.1 + ) + )) + }; + +Note that we are also required to wrap the ``actors`` query with +:eql:func:`assert_distinct` here to assure the compiler that the result set +is distinct. + +With computed backlinks +======================= + +Specifying link properties of a computed backlink in your shape is also +supported. If you have this schema: + +.. code-block:: sdl + + type Person { + required name: str; + + multi follows: Person { + followed: datetime { + default := datetime_of_statement(); + }; + }; + + multi link followers := .` + * - :ref:`Properties in schema ` + * - :ref:`Properties with DDL ` diff --git a/docs/datamodel/links.rst b/docs/datamodel/links.rst index 253bb454b6d..4cb661917ad 100644 --- a/docs/datamodel/links.rst +++ b/docs/datamodel/links.rst @@ -445,6 +445,9 @@ the link itself is a distinct concept with its own data. For relations like one-to-one or one-to-many, it's often clearer to store data in the object type itself instead of in a link property. +Read more about link properties in the :ref:`dedicated link properties article +`. + Inserting and updating link properties -------------------------------------- @@ -556,7 +559,7 @@ in the shape: .. note:: For a full guide on modeling, inserting, updating, and querying link - properties, see the :ref:`Using Link Properties ` + properties, see the :ref:`Using Link Properties ` guide. diff --git a/docs/datamodel/properties.rst b/docs/datamodel/properties.rst index 59c3071c860..5678e3e8553 100644 --- a/docs/datamodel/properties.rst +++ b/docs/datamodel/properties.rst @@ -6,53 +6,76 @@ Properties .. index:: property, primitive types, fields, columns -Properties are used to associate primitive data with an :ref:`object type -` or :ref:`link `. +Properties are used to associate primitive data with an :ref:`object type ` or :ref:`link `. .. code-block:: sdl type Player { - email: str; + property email: str; points: int64; is_online: bool; } -Properties are associated with a *key* (e.g. ``first_name``) and a primitive -type (e.g. ``str``). The term *primitive type* is an umbrella term that -encompasses :ref:`scalar types ` like ``str`` and -``bool``, :ref:`enums `, :ref:`arrays -`, and :ref:`tuples `. +Properties are associated with a *name* (e.g. ``email``) and a primitive +type (e.g. ``str``). + +The term *primitive type* is an umbrella term that +encompasses :ref:`scalar types ` like ``str``, +:ref:`arrays ` and :ref:`tuples `, +:ref:`and more `. + +Properties can be declared using the ``property`` keyword if that improves +readability, or it can be ommitted. Required properties -------------------- +=================== .. index:: required, optional, not null Properties can be either ``optional`` (the default) or ``required``. +E.g. here we have a ``User`` type that's guaranteed to have an ``email``, +but ``name`` is optional and can be empty: + +.. code-block:: sdl + + type User { + required email: str; + optional name: str; + } + +Since ``optional`` keyword is the default, we can omit it: + .. code-block:: sdl type User { required email: str; + name: str; } .. _ref_datamodel_props_cardinality: -Property cardinality --------------------- +Cardinality +=========== .. index:: cardinality, single, multi -Properties have a **cardinality**, either ``single`` (the default) or -``multi``. A ``multi`` property of type ``str`` points to an *unordered set* of -strings. +Properties have a **cardinality**: + +* ``prop: type``, short for ``single prop: type``, can either hold zero or + one value (that's the default). + +* ``multi prop: type`` can hold an *unordered set* of values, which can + be zero, one, or more values of type ``type``. + +For example: .. code-block:: sdl type User { - # single isn't necessary here + # "single" keyword isn't necessary here: # properties are single by default single name: str; @@ -63,18 +86,36 @@ strings. multi set_of_arrays: array; } -**Comparison to arrays** +multi vs. arrays +================ + +``multi`` properties are stored differently than arrays under the hood. +Essentially they are stored in a separate table ``(owner_id, value)``. + +.. rubric:: Pros of multi properties vs. arrays + +* ``multi`` properties allow efficient search and mutation of large sets. + Arrays are much slower for those operations. + +* ``multi`` properties can have indexes and constraints appied to + individual elements; arrays, in general, cannot. + +* It's easier to aggregate sets and operate on them than on arrays. + In many cases arrays would require :ref:`unpacking them into a set + ` first. + +.. rubric:: Cons of multi properties vs. arrays + +* On small sets, arrays are faster to retrieve. + +* It's easier to retain the original order in arrays. Arrays are ordered, + but sets are not. -The values associated with a ``multi`` property are stored in no -particular order. If order is important, use an :ref:`array -`. Otherwise, ``multi`` properties are recommended. For a -more involved discussion, see :ref:`EdgeQL > Sets -`. .. _ref_datamodel_props_default_values: Default values --------------- +============== .. index:: default @@ -93,8 +134,9 @@ arbitrary EdgeQL expression, which will be evaluated upon insertion. } } + Readonly properties -------------------- +=================== .. index:: readonly, immutable @@ -110,8 +152,9 @@ modified thereafter. } } + Constraints ------------ +=========== .. index:: constraint @@ -138,9 +181,9 @@ subset of Gel's built-in constraints. } } -You can constrain properties with arbitrary :ref:`EdgeQL ` -expressions returning ``bool``. To reference the value of the property, use the -special scope keyword ``__subject__``. +You can constrain properties with arbitrary :ref:`EdgeQL ` expressions +returning ``bool``. To reference the value of the property, use the special scope +keyword ``__subject__``. .. code-block:: sdl @@ -161,7 +204,7 @@ reference `. Annotations ------------ +=========== .. index:: annotation, metadata, title, description, deprecated @@ -174,20 +217,19 @@ declare :ref:`custom annotation types `. type User { email: str { annotation title := 'Email address'; - annotation description := "The user's email address."; - annotation deprecated := 'Use NewUser instead.'; } } Abstract properties -------------------- +=================== .. index:: abstract property Properties can be *concrete* (the default) or *abstract*. Abstract properties are declared independent of a source or target, can contain :ref:`annotations -`, and can be marked as ``readonly``. +`, constraints, indexes, and can be marked as +``readonly``. .. code-block:: sdl @@ -205,18 +247,513 @@ are declared independent of a source or target, can contain :ref:`annotations Link properties ---------------- +=============== .. index:: linkprops, relations, link table Properties can also be defined on **links**. For a full guide, refer to -:ref:`Guides > Using link properties `. +:ref:`the dedicated page `. + + +Overloading properties +====================== + +Any time we want to amend an inherited property (e.g. to add a constraint), +the ``overloaded`` keyword must be used. This is to prevent unintentional +overloading due to a name clash: + +.. code-block:: sdl + + abstract type Named { + optional name: str; + } + + type User extending Named { + # make "name" required + overloaded required name: str; + } + + +.. _ref_eql_sdl_props: +.. _ref_eql_sdl_props_syntax: + +Declaring properties +==================== + +Syntax +------ + +This section describes the syntax to declare properties in your schema. + +.. sdl:synopsis:: + + # Concrete property form used inside type declaration: + [ overloaded ] [{required | optional}] [{single | multi}] + [ property ] : + [ "{" + [ extending [, ...] ; ] + [ default := ; ] + [ readonly := {true | false} ; ] + [ ] + [ ] + ... + "}" ] + + # Computed property form used inside type declaration: + [{required | optional}] [{single | multi}] + [ property ] := ; + + # Computed property form used inside type declaration (extended): + [ overloaded ] [{required | optional}] [{single | multi}] + property [: ] + [ "{" + using () ; + [ extending [, ...] ; ] + [ ] + [ ] + ... + "}" ] + + # Abstract property form: + abstract property [::] + [ "{" + [extending [, ...] ; ] + [ readonly := {true | false} ; ] + [ ] + ... + "}" ] + + +Description +^^^^^^^^^^^ + +There are several forms of ``property`` declaration, as shown in the +syntax synopsis above. The first form is the canonical definition +form, the second and third forms are used for defining a +:ref:`computed property `, and the last +one is a form to define an ``abstract property``. + +The abstract form allows declaring the property directly inside +a :ref:`module `. + +Concrete property forms are always used as sub-declarations +for an :ref:`object type ` or +a :ref:`link `. + +The following options are available: + +:eql:synopsis:`overloaded` + If specified, indicates that the property is inherited and that some + feature of it may be altered in the current object type. It is an + error to declare a property as *overloaded* if it is not inherited. + +:eql:synopsis:`required` + If specified, the property is considered *required* for the parent + object type. It is an error for an object to have a required + property resolve to an empty value. Child properties **always** + inherit the *required* attribute, i.e it is not possible to make a + required property non-required by extending it. + +:eql:synopsis:`optional` + This is the default qualifier assumed when no qualifier is + specified, but it can also be specified explicitly. The property + is considered *optional* for the parent object type, i.e. it is + possible for the property to resolve to an empty value. + +:eql:synopsis:`multi` + Specifies that there may be more than one instance of this + property in an object, in other words, ``Object.property`` may + resolve to a set of a size greater than one. + +:eql:synopsis:`single` + Specifies that there may be at most *one* instance of this + property in an object, in other words, ``Object.property`` may + resolve to a set of a size not greater than one. ``single`` is + assumed if nether ``multi`` nor ``single`` qualifier is specified. + +:eql:synopsis:`extending [, ...]` + Optional clause specifying the *parents* of the new property item. + + Use of ``extending`` creates a persistent schema relationship + between the new property and its parents. Schema modifications + to the parent(s) propagate to the child. + +:eql:synopsis:`` + The type must be a valid :ref:`type expression ` + denoting a non-abstract scalar or a container type. + +The valid SDL sub-declarations are listed below: + +:eql:synopsis:`default := ` + Specifies the default value for the property as an EdgeQL expression. + The default value is used in an ``insert`` statement if an explicit + value for this property is not specified. + + The expression must be :ref:`Stable `. + +:eql:synopsis:`readonly := {true | false}` + If ``true``, the property is considered *read-only*. + Modifications of this property are prohibited once an object is + created. All of the derived properties **must** preserve the + original *read-only* value. + +:sdl:synopsis:`` + Set property :ref:`annotation ` + to a given *value*. + +:sdl:synopsis:`` + Define a concrete :ref:`constraint ` on + the property. + + +.. _ref_eql_ddl_props: + +DDL commands +============ + +This section describes the low-level DDL commands for creating, altering, and +dropping properties. You typically don't need to use these commands directly, +but knowing about them is useful for reviewing migrations. + + +.. _ref_eql_ddl_props_syntax: + +Create property +--------------- + +:eql-statement: +:eql-haswith: + +Define a new property. + +.. eql:synopsis:: + + [ with [, ...] ] + {create|alter} {type|link} "{" + [ ... ] + create [{required | optional}] [{single | multi}] + property + [ extending [, ...] ] -> + [ "{" ; [...] "}" ] ; + [ ... ] + "}" + + # Computed property form: + + [ with [, ...] ] + {create|alter} {type|link} "{" + [ ... ] + create [{required | optional}] [{single | multi}] + property := ; + [ ... ] + "}" + + # Abstract property form: + + [ with [, ...] ] + create abstract property [::] [extending [, ...]] + [ "{" ; [...] "}" ] + + # where is one of + + set default := + set readonly := {true | false} + create annotation := + create constraint ... + +Parameters +^^^^^^^^^^ + +Most sub-commands and options of this command are identical to the +:ref:`SDL property declaration `. The +following subcommands are allowed in the ``create property`` block: + +:eql:synopsis:`set default := ` + Specifies the default value for the property as an EdgeQL expression. + Other than a slight syntactical difference this is the same as the + corresponding SDL declaration. + +:eql:synopsis:`set readonly := {true | false}` + Specifies whether the property is considered *read-only*. Other + than a slight syntactical difference this is the same as the + corresponding SDL declaration. + +:eql:synopsis:`create annotation := ` + Set property :eql:synopsis:`` to + :eql:synopsis:``. + + See :eql:stmt:`create annotation` for details. + +:eql:synopsis:`create constraint` + Define a concrete constraint on the property. + See :eql:stmt:`create constraint` for details. + + +Examples +^^^^^^^^ + +Define a new link ``address`` on the ``User`` object type: + +.. code-block:: edgeql + + alter type User { + create property address -> str + }; + +Define a new :ref:`computed property ` +``number_of_connections`` on the ``User`` object type counting the +number of interests: + +.. code-block:: edgeql + + alter type User { + create property number_of_connections := + count(.interests) + }; + +Define a new abstract link ``orderable`` with ``weight`` property: + +.. code-block:: edgeql + + create abstract link orderable { + create property weight -> std::int64 + }; + + +Alter property +-------------- + +:eql-statement: +:eql-haswith: + +Change the definition of a property. + +.. eql:synopsis:: + + [ with [, ...] ] + {create | alter} {type | link} "{" + [ ... ] + alter property + [ "{" ] ; [...] [ "}" ]; + [ ... ] + "}" + + + [ with [, ...] ] + alter abstract property [::] + [ "{" ] ; [...] [ "}" ]; + + # where is one of + + set default := + reset default + set readonly := {true | false} + reset readonly + rename to + extending ... + set required [using ( [using () + create annotation := + alter annotation := + drop annotation + create constraint ... + alter constraint ... + drop constraint ... + + +Parameters +^^^^^^^^^^ + +:eql:synopsis:`` + The name of an object type or link on which the property is defined. + May be optionally qualified with module. + +:eql:synopsis:`` + The unqualified name of the property to modify. + +:eql:synopsis:`` + Optional name of the module to create or alter the abstract property in. + If not specified, the current module is used. + +The following subcommands are allowed in the ``alter link`` block: + +:eql:synopsis:`rename to ` + Change the name of the property to :eql:synopsis:``. + All concrete properties inheriting from this property are + also renamed. + +:eql:synopsis:`extending ...` + Alter the property parent list. The full syntax of this subcommand is: + + .. eql:synopsis:: + + extending [, ...] + [ first | last | before | after ] + + This subcommand makes the property a child of the specified list + of parent property items. The requirements for the parent-child + relationship are the same as when creating a property. + + It is possible to specify the position in the parent list + using the following optional keywords: + + * ``first`` -- insert parent(s) at the beginning of the + parent list, + * ``last`` -- insert parent(s) at the end of the parent list, + * ``before `` -- insert parent(s) before an + existing *parent*, + * ``after `` -- insert parent(s) after an existing + *parent*. + +:eql:synopsis:`set required [using ( [using (`. The optional ``using`` clause specifies + a conversion expression that computes the new property value from the old. + The conversion expression must return a singleton set and is evaluated + on each element of ``multi`` properties. A ``using`` clause must be + provided if there is no implicit or assignment cast from old to new type. + +:eql:synopsis:`reset type` + Reset the type of the property to the type inherited from properties + of the same name in supertypes. It is an error to ``reset type`` on + a property that is not inherited. + +:eql:synopsis:`using ()` + Change the expression of a :ref:`computed property + `. Only valid for concrete properties. + +:eql:synopsis:`alter annotation ;` + Alter property annotation :eql:synopsis:``. + See :eql:stmt:`alter annotation` for details. + +:eql:synopsis:`drop annotation ;` + Remove property annotation :eql:synopsis:``. + See :eql:stmt:`drop annotation` for details. + +:eql:synopsis:`alter constraint ...` + Alter the definition of a constraint for this property. See + :eql:stmt:`alter constraint` for details. + +:eql:synopsis:`drop constraint ;` + Remove a constraint from this property. See + :eql:stmt:`drop constraint` for details. + +:eql:synopsis:`reset default` + Remove the default value from this property, or reset it to the value + inherited from a supertype, if the property is inherited. + +:eql:synopsis:`reset readonly` + Set property writability to the default value (writable), or, if the + property is inherited, to the value inherited from properties in + supertypes. + +All the subcommands allowed in the ``create property`` block are also +valid subcommands for ``alter property`` block. + +Examples +^^^^^^^^ + +Set the ``title`` annotation of property ``address`` of object type +``User`` to ``"Home address"``: + +.. code-block:: edgeql + + alter type User { + alter property address + create annotation title := "Home address"; + }; + +Add a maximum-length constraint to property ``address`` of object type +``User``: + +.. code-block:: edgeql + + alter type User { + alter property address { + create constraint max_len_value(500); + }; + }; + +Rename the property ``weight`` of link ``orderable`` to ``sort_by``: + +.. code-block:: edgeql + + alter abstract link orderable { + alter property weight rename to sort_by; + }; + +Redefine the :ref:`computed property ` +``number_of_connections`` to be the number of friends: + +.. code-block:: edgeql + + alter type User { + alter property number_of_connections using ( + count(.friends) + ) + }; + + +Drop property +------------- + +:eql-statement: +:eql-haswith: + +Remove a property from the schema. + +.. eql:synopsis:: + + [ with [, ...] ] + {create|alter} type "{" + [ ... ] + drop link + [ ... ] + "}" + + + [ with [, ...] ] + drop abstract property ; + + +Example +^^^^^^^ + +Remove property ``address`` from type ``User``: -.. list-table:: - :class: seealso +.. code-block:: edgeql - * - **See also** - * - :ref:`SDL > Properties ` - * - :ref:`DDL > Properties ` - * - :ref:`Introspection > Object types - ` + alter type User { + drop property address; + }; diff --git a/docs/edgeql/sets.rst b/docs/edgeql/sets.rst index 9105194609c..061b85ee489 100644 --- a/docs/edgeql/sets.rst +++ b/docs/edgeql/sets.rst @@ -511,7 +511,7 @@ transform than arrays. db> select str_trim([' hello', 'world ']); error: QueryError: function "str_trim(arg0: array)" does not exist -Most :ref:`aggregate ` operations have +Some :ref:`aggregate ` operations have analogs that operate on arrays. For instance, the set function :eql:func:`count` is analogous to the array function :eql:func:`len`. diff --git a/docs/reference/ddl/index.rst b/docs/reference/ddl/index.rst index b16578327c6..2a80060371d 100644 --- a/docs/reference/ddl/index.rst +++ b/docs/reference/ddl/index.rst @@ -7,7 +7,6 @@ DDL :maxdepth: 3 :hidden: - properties migrations :edb-alt-title: Data Definition Language diff --git a/docs/reference/ddl/properties.rst b/docs/reference/ddl/properties.rst deleted file mode 100644 index 886ac77a3f7..00000000000 --- a/docs/reference/ddl/properties.rst +++ /dev/null @@ -1,399 +0,0 @@ -.. _ref_eql_ddl_props: - -========== -Properties -========== - -This section describes the DDL commands pertaining to -:ref:`properties `. - - -Create property -=============== - -:eql-statement: -:eql-haswith: - -:ref:`Define ` a new property. - -.. eql:synopsis:: - - [ with [, ...] ] - {create|alter} {type|link} "{" - [ ... ] - create [{required | optional}] [{single | multi}] - property - [ extending [, ...] ] -> - [ "{" ; [...] "}" ] ; - [ ... ] - "}" - - # Computed property form: - - [ with [, ...] ] - {create|alter} {type|link} "{" - [ ... ] - create [{required | optional}] [{single | multi}] - property := ; - [ ... ] - "}" - - # Abstract property form: - - [ with [, ...] ] - create abstract property [::] [extending [, ...]] - [ "{" ; [...] "}" ] - - # where is one of - - set default := - set readonly := {true | false} - create annotation := - create constraint ... - - -Description ------------ - -The combination :eql:synopsis:`{create|alter} {type|link} ... create property` -defines a new concrete property for a given object type or link. - -There are three forms of ``create property``, as shown in the syntax synopsis -above. The first form is the canonical definition form, the second -form is a syntax shorthand for defining a -:ref:`computed property `, and the third -is a form to define an abstract property item. The abstract form -allows creating the property in the specified -:eql:synopsis:``. Concrete property forms are always -created in the same module as the containing object or property. - -.. _ref_eql_ddl_props_syntax: - -Parameters ----------- - -Most sub-commands and options of this command are identical to the -:ref:`SDL property declaration `. The -following subcommands are allowed in the ``create property`` block: - -:eql:synopsis:`set default := ` - Specifies the default value for the property as an EdgeQL expression. - Other than a slight syntactical difference this is the same as the - corresponding SDL declaration. - -:eql:synopsis:`set readonly := {true | false}` - Specifies whether the property is considered *read-only*. Other - than a slight syntactical difference this is the same as the - corresponding SDL declaration. - -:eql:synopsis:`create annotation := ` - Set property :eql:synopsis:`` to - :eql:synopsis:``. - - See :eql:stmt:`create annotation` for details. - -:eql:synopsis:`create constraint` - Define a concrete constraint on the property. - See :eql:stmt:`create constraint` for details. - - -Examples --------- - -Define a new link ``address`` on the ``User`` object type: - -.. code-block:: edgeql - - alter type User { - create property address -> str - }; - -Define a new :ref:`computed property ` -``number_of_connections`` on the ``User`` object type counting the -number of interests: - -.. code-block:: edgeql - - alter type User { - create property number_of_connections := - count(.interests) - }; - -Define a new abstract link ``orderable`` with ``weight`` property: - -.. code-block:: edgeql - - create abstract link orderable { - create property weight -> std::int64 - }; - - -Alter property -============== - -:eql-statement: -:eql-haswith: - - -Change the definition of a :ref:`property `. - -.. eql:synopsis:: - - [ with [, ...] ] - {create | alter} {type | link} "{" - [ ... ] - alter property - [ "{" ] ; [...] [ "}" ]; - [ ... ] - "}" - - - [ with [, ...] ] - alter abstract property [::] - [ "{" ] ; [...] [ "}" ]; - - # where is one of - - set default := - reset default - set readonly := {true | false} - reset readonly - rename to - extending ... - set required [using ( [using () - create annotation := - alter annotation := - drop annotation - create constraint ... - alter constraint ... - drop constraint ... - - -Description ------------ - -The combination :eql:synopsis:`{create|alter} {type|link} ... create property` -defines a new concrete property for a given object type or link. - -The command :eql:synopsis:`alter abstract property` changes the -definition of an abstract property item. - - -Parameters ----------- - -:eql:synopsis:`` - The name of an object type or link on which the property is defined. - May be optionally qualified with module. - -:eql:synopsis:`` - The unqualified name of the property to modify. - -:eql:synopsis:`` - Optional name of the module to create or alter the abstract property in. - If not specified, the current module is used. - -The following subcommands are allowed in the ``alter link`` block: - -:eql:synopsis:`rename to ` - Change the name of the property to :eql:synopsis:``. - All concrete properties inheriting from this property are - also renamed. - -:eql:synopsis:`extending ...` - Alter the property parent list. The full syntax of this subcommand is: - - .. eql:synopsis:: - - extending [, ...] - [ first | last | before | after ] - - This subcommand makes the property a child of the specified list - of parent property items. The requirements for the parent-child - relationship are the same as when creating a property. - - It is possible to specify the position in the parent list - using the following optional keywords: - - * ``first`` -- insert parent(s) at the beginning of the - parent list, - * ``last`` -- insert parent(s) at the end of the parent list, - * ``before `` -- insert parent(s) before an - existing *parent*, - * ``after `` -- insert parent(s) after an existing - *parent*. - -:eql:synopsis:`set required [using ( [using (`. The optional ``using`` clause specifies - a conversion expression that computes the new property value from the old. - The conversion expression must return a singleton set and is evaluated - on each element of ``multi`` properties. A ``using`` clause must be - provided if there is no implicit or assignment cast from old to new type. - -:eql:synopsis:`reset type` - Reset the type of the property to the type inherited from properties - of the same name in supertypes. It is an error to ``reset type`` on - a property that is not inherited. - -:eql:synopsis:`using ()` - Change the expression of a :ref:`computed property - `. Only valid for concrete properties. - -:eql:synopsis:`alter annotation ;` - Alter property annotation :eql:synopsis:``. - See :eql:stmt:`alter annotation` for details. - -:eql:synopsis:`drop annotation ;` - Remove property annotation :eql:synopsis:``. - See :eql:stmt:`drop annotation` for details. - -:eql:synopsis:`alter constraint ...` - Alter the definition of a constraint for this property. See - :eql:stmt:`alter constraint` for details. - -:eql:synopsis:`drop constraint ;` - Remove a constraint from this property. See - :eql:stmt:`drop constraint` for details. - -:eql:synopsis:`reset default` - Remove the default value from this property, or reset it to the value - inherited from a supertype, if the property is inherited. - -:eql:synopsis:`reset readonly` - Set property writability to the default value (writable), or, if the - property is inherited, to the value inherited from properties in - supertypes. - -All the subcommands allowed in the ``create property`` block are also -valid subcommands for ``alter property`` block. - - -Examples --------- - -Set the ``title`` annotation of property ``address`` of object type -``User`` to ``"Home address"``: - -.. code-block:: edgeql - - alter type User { - alter property address - create annotation title := "Home address"; - }; - -Add a maximum-length constraint to property ``address`` of object type -``User``: - -.. code-block:: edgeql - - alter type User { - alter property address { - create constraint max_len_value(500); - }; - }; - -Rename the property ``weight`` of link ``orderable`` to ``sort_by``: - -.. code-block:: edgeql - - alter abstract link orderable { - alter property weight rename to sort_by; - }; - -Redefine the :ref:`computed property ` -``number_of_connections`` to be the number of friends: - -.. code-block:: edgeql - - alter type User { - alter property number_of_connections using ( - count(.friends) - ) - }; - - -Drop property -============= - -:eql-statement: -:eql-haswith: - -Remove a :ref:`property ` from the schema. - -.. eql:synopsis:: - - [ with [, ...] ] - {create|alter} type "{" - [ ... ] - drop link - [ ... ] - "}" - - - [ with [, ...] ] - drop abstract property ; - -Description ------------ - -The combination :eql:synopsis:`alter {type|link} drop property` -removes the specified property from its containing object type or -link. All properties that inherit from this property are also -removed. - -The command :eql:synopsis:`drop abstract property` removes the -specified abstract property item from the schema. - -Example -------- - -Remove property ``address`` from type ``User``: - -.. code-block:: edgeql - - alter type User { - drop property address; - }; - - -.. list-table:: - :class: seealso - - * - **See also** - * - :ref:`Schema > Properties ` - * - :ref:`SDL > Properties ` - * - :ref:`Introspection > Object types - ` diff --git a/docs/reference/sdl/index.rst b/docs/reference/sdl/index.rst index 266329183d2..213d89ba4be 100644 --- a/docs/reference/sdl/index.rst +++ b/docs/reference/sdl/index.rst @@ -65,9 +65,3 @@ to the previous migration: required first_name: str; required last_name: str; } - -.. toctree:: - :maxdepth: 3 - :hidden: - - properties diff --git a/docs/reference/sdl/properties.rst b/docs/reference/sdl/properties.rst deleted file mode 100644 index a835fb0a964..00000000000 --- a/docs/reference/sdl/properties.rst +++ /dev/null @@ -1,188 +0,0 @@ -.. _ref_eql_sdl_props: - -========== -Properties -========== - -This section describes the SDL declarations pertaining to -:ref:`properties `. - - -Examples --------- - -Declare an *abstract* property "address_base" with a helpful title: - -.. code-block:: sdl - - abstract property address_base { - # declare a specific title for the link - annotation title := 'Mailing address'; - } - -Declare *concrete* properties "name" and "address" within a "User" type: - -.. code-block:: sdl - - type User { - # define concrete properties - required name: str; - address: str { - extending address_base; - }; - - multi friends: User; - - index on (__subject__.name); - } - -Any time that the SDL declaration refers to an inherited property that -is being overloaded (by adding more constraints, for example), the -``overloaded`` keyword must be used. This is to prevent unintentional -overloading due to name clashes: - -.. code-block:: sdl - - abstract type Named { - name: str; - } - - type User extending Named { - # define concrete properties - overloaded required name: str; - # ... other links and properties - } - -.. _ref_eql_sdl_props_syntax: - -Syntax ------- - -Define a new property corresponding to the :ref:`more explicit DDL -commands `. - -.. sdl:synopsis:: - - # Concrete property form used inside type declaration: - [ overloaded ] [{required | optional}] [{single | multi}] - [ property ] : - [ "{" - [ extending [, ...] ; ] - [ default := ; ] - [ readonly := {true | false} ; ] - [ ] - [ ] - ... - "}" ] - - # Computed property form used inside type declaration: - [{required | optional}] [{single | multi}] - [ property ] := ; - - # Computed property form used inside type declaration (extended): - [ overloaded ] [{required | optional}] [{single | multi}] - property [: ] - [ "{" - using () ; - [ extending [, ...] ; ] - [ ] - [ ] - ... - "}" ] - - # Abstract property form: - abstract property [::] - [ "{" - [extending [, ...] ; ] - [ readonly := {true | false} ; ] - [ ] - ... - "}" ] - - -Description ------------ - -There are several forms of ``property`` declaration, as shown in the -syntax synopsis above. The first form is the canonical definition -form, the second and third forms are used for defining a -:ref:`computed property `, and the last -one is a form to define an ``abstract property``. The abstract -form allows declaring the property directly inside a :ref:`module -`. Concrete property forms are always used -as sub-declarations for an :ref:`object type -` or a :ref:`link `. - -The following options are available: - -:eql:synopsis:`overloaded` - If specified, indicates that the property is inherited and that some - feature of it may be altered in the current object type. It is an - error to declare a property as *overloaded* if it is not inherited. - -:eql:synopsis:`required` - If specified, the property is considered *required* for the parent - object type. It is an error for an object to have a required - property resolve to an empty value. Child properties **always** - inherit the *required* attribute, i.e it is not possible to make a - required property non-required by extending it. - -:eql:synopsis:`optional` - This is the default qualifier assumed when no qualifier is - specified, but it can also be specified explicitly. The property - is considered *optional* for the parent object type, i.e. it is - possible for the property to resolve to an empty value. - -:eql:synopsis:`multi` - Specifies that there may be more than one instance of this - property in an object, in other words, ``Object.property`` may - resolve to a set of a size greater than one. - -:eql:synopsis:`single` - Specifies that there may be at most *one* instance of this - property in an object, in other words, ``Object.property`` may - resolve to a set of a size not greater than one. ``single`` is - assumed if nether ``multi`` nor ``single`` qualifier is specified. - -:eql:synopsis:`extending [, ...]` - Optional clause specifying the *parents* of the new property item. - - Use of ``extending`` creates a persistent schema relationship - between the new property and its parents. Schema modifications - to the parent(s) propagate to the child. - -:eql:synopsis:`` - The type must be a valid :ref:`type expression ` - denoting a non-abstract scalar or a container type. - -The valid SDL sub-declarations are listed below: - -:eql:synopsis:`default := ` - Specifies the default value for the property as an EdgeQL expression. - The default value is used in an ``insert`` statement if an explicit - value for this property is not specified. - - The expression must be :ref:`Stable `. - -:eql:synopsis:`readonly := {true | false}` - If ``true``, the property is considered *read-only*. - Modifications of this property are prohibited once an object is - created. All of the derived properties **must** preserve the - original *read-only* value. - -:sdl:synopsis:`` - Set property :ref:`annotation ` - to a given *value*. - -:sdl:synopsis:`` - Define a concrete :ref:`constraint ` on - the property. - -.. list-table:: - :class: seealso - - * - **See also** - * - :ref:`Schema > Properties ` - * - :ref:`DDL > Properties ` - * - :ref:`Introspection > Object types - `