diff --git a/src/content/doc-surrealql/datamodel/idioms.mdx b/src/content/doc-surrealql/datamodel/idioms.mdx index f25bde416..9a8049899 100644 --- a/src/content/doc-surrealql/datamodel/idioms.mdx +++ b/src/content/doc-surrealql/datamodel/idioms.mdx @@ -13,8 +13,6 @@ Idioms in SurrealQL provide a powerful and flexible way to access and manipulate An idiom is composed of a sequence of **parts** that define the path to a value within a record or data structure. Each part specifies how to navigate to the next piece of data. Idioms can be used in various parts of SurrealQL. The most common usecase is in data retrival queries such as `SELECT` statements, but they can also be used in the `WHERE` clause, `SET` clause, and more. -## Parts of an Idiom - An idiom is made up of one or more **parts**, each of which can be one of several types: - [**Field**](#field-access): Access a field by name. @@ -30,7 +28,7 @@ An idiom is made up of one or more **parts**, each of which can be one of severa In this section, we'll explore each part in detail with examples to help you understand how to use idioms in SurrealQL. -### Field Access +## Field Access Since SurrealDB is, at its core, a document database, each record is stored on an underlying key-value store storage engine, with the ability to store arbitrary arrays, objects, and many other types of data. To access a field in an object, use a dot `.` followed by the field name. @@ -80,7 +78,7 @@ SELECT address.city FROM person; ``` In this example, `person.name` is an idiom that accesses the `name` field of the `person` record. -### Index Access +## Index Access To access an element in an array by its index, use square brackets `[]` with the index inside. For example, let's say we have a `school` record with some student results. @@ -142,7 +140,7 @@ SELECT results[0].score FROM student; Here, `results[0].score` accesses the score of the first student in the `results` array. -### All Elements +## All Elements To access all elements in an array or all fields in an object, use `.*`. This is useful when you want to access all the elements in an array or all the fields in an object. @@ -189,13 +187,13 @@ The output for `INFO FOR TABLE person` includes an automatically generated defin } ``` -#### Using `.*` to return values +### Using `.*` to return values The `.*` idiom in SurrealDB allows you to target all values in an object or all entries in an array. It can be used in various contexts such as querying, field definitions, and data manipulation. This section explains the behavior of `.*` with practical examples. -#### Accessing All Values in an Object +### Accessing all values in an object When applied to an object, `.*` returns an array containing all the values of the object's properties. @@ -211,7 +209,7 @@ When applied to an object, `.*` returns an array containing all the values of th In this example, `{ a: 1, b: 2 }.*` extracts the values `1` and `2` from the object and returns them as an array. -##### Defining Fields with `.*` +#### Defining Fields with `.*` You can define fields using `.*` to specify constraints or types for all properties within an object field. @@ -233,7 +231,7 @@ CREATE test:1 SET obj.a = 'a'; Found 'a' for field `obj[*]`, with record `test:1`, but expected a number ``` -##### Using `.*` in Different Contexts +#### Using `.*` in Different Contexts Depending on where `.*` is used, it can have different effects on the order of operations. @@ -317,7 +315,7 @@ SELECT * FROM { id: person:tobie, name: 'tobie' }.*; ``` -### Last Element +## Last Element Addionally to access the last element of an array, use `[$]`. Refereing to the `student` record above, we can access the last element of the `results` array using the following idiom: @@ -336,7 +334,7 @@ SELECT results[$].score FROM student; This idiom accesses the last element of the `score` array. -### Method chaining +## Method chaining @@ -398,7 +396,7 @@ SELECT string::uppercase(name) FROM person; To learn more about string method chaining in SurrealQL, see the [string functions](/docs/surrealql/functions/database/string#method-chaining) section. -### Graph Navigation +## Graph Navigation SurrealDB can also be used in the context of graph databases, where data is stored and navigated using graph traversal idioms. The [`RELATE` statement](/docs/surrealql/statements/relate) is used to create relationships between records. This allows you to traverse related records efficiently without needing to pull data from multiple tables and merging that data together using SQL JOINs. @@ -455,7 +453,7 @@ Explanation: - `<->? AS involved_in`: Retrieves all relationships connected to Drake, regardless of direction, and aliases them as involved_in. -### Destructuring +## Destructuring @@ -549,7 +547,7 @@ FROM explorer:drake; ] ``` -#### Using aliases when destructuring +### Using aliases when destructuring The keyword `AS` is necessary inside `SELECT` statements when [using an alias](/docs/surrealql/statements/select#basic-usage) (a new name for a field). @@ -626,7 +624,7 @@ $town.{ }; ``` -#### Destructuring the current item in a SELECT query +### Destructuring the current item in a SELECT query @@ -676,7 +674,7 @@ FROM planet) Most importantly, however, the `@` operator is often necessary when using [recursive paths](#recursive-paths). -### Optional Parts +## Optional Parts @@ -690,7 +688,7 @@ SELECT person.spouse?.name FROM person; This idiom safely accesses `person.spouse.name` if `spouse` exists; otherwise, it returns `NONE`. -### Using Optional Parts +## Using Optional Parts If some `person` records have a `spouse` field and others do not: @@ -700,7 +698,7 @@ SELECT name, spouse?.name AS spouse_name FROM person; This idiom will return `NONE` for `spouse_name` if the `spouse` field is not present. -### Recursive paths +## Recursive paths @@ -717,7 +715,7 @@ person:tobie.{2}(->friends_with->person).name; As the syntax of recursive queries tends to be complex to the untrained eye, this section will explain them in order of difficulty, beginning with what queries were necessary before recursive paths were added in SurrealDB version 2.1. -#### Overview +### Overview Take the following example that creates one planet, two countries, two states/provinces in each of these countries, and two cities in each of those states/provinces. The `CREATE` statements are followed by `UPDATE` statements to set record links between them, and `RELATE` to create bidirectional graph relations between them. @@ -854,7 +852,7 @@ FROM planet:earth; ] ``` -#### Basics of recursive paths +### Basics of recursive paths Using a recursive path allows you to instead set the number of steps to follow instead of manually typing. A recursive path is made by isolating `{}` braces in between two dots, inside which the number of steps is indicated. @@ -907,7 +905,7 @@ planet:earth.{..}->has->(?); ] ``` -#### Using () to provide instructions at each depth +### Using () to provide instructions at each depth Parentheses can be added to a recursive query. To explain their use, consider the following example that attempts to traverse up to a depth of 3 and return the `name` of the records at that level. @@ -962,7 +960,7 @@ planet:earth .{name, id}; ``` -#### Using `@` to refer to the current record +### Using `@` to refer to the current record The `@` symbol is used in recursive queries to refer to the current document. This is needed in recursive `SELECT` queries, as without it there is no way to know the context. @@ -985,7 +983,7 @@ SELECT @.{1..3}(->has->(?)) AS cities FROM planet; SELECT name.len() AS length FROM planet; ``` -#### Using `{}` and `.@` to combine results +### Using `{}` and `.@` to combine results Inside the structure of a recursive graph query, the `@` symbol is used in the form of `.@` at the end of a path to inform the database that this is the path to be repeated during the recursion. This allows not just the fields on the final depth of the query to be returned, but each one along the way as well. @@ -1111,7 +1109,7 @@ SELECT @{1..4}(->knows->person).name AS names_2nds FROM person; person:tobie.{1..5}(.friend).name; ``` -#### Behaviour of recursive queries +### Behaviour of recursive queries Recursive queries follow a few rules to determine how far to traverse and what to return. They are: @@ -1121,7 +1119,7 @@ Recursive queries follow a few rules to determine how far to traverse and what t * If it has already passed the minimum depth, it returns the last valid value. * During each iteration, if it encounters an array value, all dead end values are automatically filtered out, ensuring no empty paths are included. -#### Filtering recursive fields +### Filtering recursive fields Recursive syntax is not just useful in creating recursive queries, but parsing them as well. Take the following example that creates some `person` records, gives each of them two friends, and then traverses the `friends_with` graph for the first `person` records to find its friends, friends of friends, and friends of friends of friends. Since every level except the last contains another `connections` field, adding a `.{some_number}.connections` to a `RETURN` statement is all that is needed to drill down to a certain depth. @@ -1166,23 +1164,23 @@ Possible output of the final query: } ``` -#### Collecting paths, collecting unique nodes, finding shortest path +### Collecting paths, collecting unique nodes, finding shortest path -SurrealDB has a number of built-in algorithms that allow recursive queries to collect all paths, all unique nodes, and to find the shortest path to a record. The syntax is as follows: +SurrealDB has a number of built-in algorithms that allow recursive queries to collect all paths, all unique nodes, and to find the shortest path to a record. These can be used by adding the following keywords to the recursive syntax specifying the depth to recurse: * `{..+path}`: used to collect all walked paths. * `{..+collect}`: used to collect all unique nodes walked. * `{..+shortest=record:id}`: used to find the shortest path to a specified record id, such as `person:tobie` or `person:one`. -The originating (first) record is excluded from these paths. To include the first record, `+inclusive` can be added to the syntax above. +The originating (first) record is excluded from these paths by default. However, it can be included by adding `+inclusive` to the syntax above. * `{..+path+inclusive}` * `{..+collect+inclusive}` * `{..+shortest=record:id+inclusive}` -To demonstrate the output of these three algorithms, take the following example showing a small network of friends. The network begins with `person:you`, followed by two friends (`person:friend1`, `person:friend2`), then three acquaintances known by these friends (`person:acquaintance1`, `person:acquaintance2`, `person:acquaintance3`), and finally a movie star (`person:star`) who is known by one of the acquaintances. +To demonstrate the output of these three algorithms, take the following example showing a small network of friends. The network begins with `person:you`, followed by two friends (`person:friend1`, `person:friend2`), then three acquaintances known by these friends (`person:acquaintance1`, `person:acquaintance2`, `person:acquaintance3`), and finally a movie star (`person:star`) who is known by only one of the acquaintances. ```surql CREATE @@ -1216,7 +1214,7 @@ person:you │ │ ▲ └───►person:acquaintance3─────────┘ ``` -To use these algorithms, follow them with the path that should be followed, in this case `->knows->person`. +After specifying an algorithm to use, such as `{..+path}`, add the path that should be followed, in this case `->knows->person`. Adding `+path` will output all of the possible paths starting from `person:you`. As the output is fairly short, we can see that there are two ways to get from `person:one` to the movie star at `person:star`, one of which is one step shorter than the other. @@ -1430,12 +1428,10 @@ person:you.{..+path}.knows; person:you.{..+collect}.knows; ``` -## Combining Idiom Parts +# Combining Idiom Parts Idioms can combine multiple parts to navigate complex data structures seamlessly. -### Complex Example - Suppose we have the following data: ```surql title="Create a new person record" @@ -1504,7 +1500,7 @@ SELECT friends[WHERE age > 18].name FROM person WHERE id = r'person:5'; ] ``` -## Notes on Idioms +# Notes on Idioms - **Chaining**: Idioms can be chained to traverse deeply nested structures. - **Performance**: Be mindful of performance when using complex idioms; indexing fields can help. @@ -1512,13 +1508,13 @@ SELECT friends[WHERE age > 18].name FROM person WHERE id = r'person:5'; - **Methods**: Leverage built-in methods for data manipulation within idioms. - **Type Casting**: Use type casting if necessary to ensure data is in the correct format. -## Best Practices +# Best Practices - **Use Destructuring**: When selecting multiple fields, destructuring improves readability. - **Limit Optional Parts**: Use optional parts judiciously to avoid masking data issues. - **Validate Data**: Ensure data conforms to expected structures, especially when dealing with optional fields. - **Index Fields**: Index fields that are frequently accessed or used in `WHERE` clauses for better performance. -## Summary +# Summary Idioms in SurrealQL are a powerful tool for navigating and manipulating data within your database. By understanding and effectively using idiom parts, you can write expressive and efficient queries that handle complex data structures with ease. Whether you're accessing nested fields, filtering arrays, or traversing graph relationships, idioms provide the flexibility you need to interact with your data seamlessly.