Skip to content

Commit

Permalink
[web] Add all headings to content in queries reference #1251 (#1252)
Browse files Browse the repository at this point in the history
Update queries.en-US.md
  • Loading branch information
michaelvlach authored Sep 12, 2024
1 parent 76e8cbe commit f2da398
Showing 1 changed file with 45 additions and 81 deletions.
126 changes: 45 additions & 81 deletions agdb_web/pages/docs/references/queries.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,44 +95,6 @@ flowchart LR
logic --> where
```

- [Queries](#queries)
- [DbUserValue](#dbuservalue)
- [QueryResult](#queryresult)
- [QueryError](#queryerror)
- [Transactions](#transactions)
- [QueryIds \& QueryId](#queryids--queryid)
- [QueryValues](#queryvalues)
- [Mutable queries](#mutable-queries)
- [Insert](#insert)
- [Insert aliases](#insert-aliases)
- [Insert edges](#insert-edges)
- [Insert index](#insert-index)
- [Insert nodes](#insert-nodes)
- [Insert values](#insert-values)
- [Remove](#remove)
- [Remove aliases](#remove-aliases)
- [Remove elements](#remove-elements)
- [Remove index](#remove-index)
- [Remove values](#remove-values)
- [Immutable queries](#immutable-queries)
- [Select](#select)
- [Select aliases](#select-aliases)
- [Select all aliases](#select-all-aliases)
- [Select edge count](#select-edge-count)
- [Select indexes](#select-indexes)
- [Select keys](#select-keys)
- [Select key count](#select-key-count)
- [Select node count](#select-node_count)
- [Select values](#select-values)
- [Search](#search)
- [Conditions](#conditions)
- [Truth tables](#truth-tables)
- [And](#and)
- [Or](#or)
- [Modifiers](#modifiers)
- [Results](#results)
- [Paths](#paths)

All interactions with the `agdb` are realized through queries. There are two kinds of queries:

- Immutable queries
Expand Down Expand Up @@ -160,7 +122,9 @@ Alternatively you can run a series of queries as a [transaction](#transactions).

All queries return `Result<QueryResult, QueryError>`. The [`QueryResult`](#queryresult) is the universal data structure holding results of all queries in an uniform structure. The [`QueryError`](#queryerror) is the singular error type holding information of any failure or problem encountered when running the query.

# DbUserValue
## Types

### DbUserValue

The `DbUserValue` trait is an interface that can be implemented for user defined types so that they can be seamlessly used with the database:

Expand Down Expand Up @@ -207,7 +171,7 @@ Types not directly used in the database but for which the conversions are suppor
- f64: any value except `0.0` will be `true`
- string: only `"true"` or `"1"` will be `true`

# QueryResult
### QueryResult

The `QueryResult` is the universal result type for all successful queries. It can be converted to user defined types that implement [`DbUserValue`](#dbuservalue) with `try_into()`. It looks like this:

Expand Down Expand Up @@ -279,33 +243,11 @@ fn vec_bool(&self) -> Result<Vec<bool>, DbError>;

The numerical variants (`I64`, `U64`, `DbF64`) will attempt loss-less conversions where possible. To avoid copies all other variants return `&` where conversions are not possible even if they could be done in theory. The special case is `to_string()` provided by the `Display` trait. It converts any values into string (it also copies the `String` variant) and performs possibly lossy conversion from `Bytes` to UTF-8 string. For bool conversion details refer to [DbUserValue](#dbuservalue) section.

# QueryError

Failure when running a query is reported through a single `QueryError` object which can optionally hold internal error (or chain of errors) that led to the failure. Most commonly it will represent **data error** or **logic error** in your query. Less commonly it may also report a failure to perform the requested operation due to underlying infrastructure issue (e.g. out of memory). It is up to the client code to handle the error.

# Transactions

You can run a series of queries as a transaction invoking corresponding methods on the database object:

```rs
impl Db {
// immutable transactions
pub fn transaction<T, E>(&self, mut f: impl FnMut(&Transaction) -> Result<T, E>) -> Result<T, E>

// mutable transactions
pub fn transaction_mut<T, E: From<QueryError>>(&mut self, mut f: impl FnMut(&mut TransactionMut) -> Result<T, E>) -> Result<T, E>
}
```

The transaction methods take a closure that itself takes a transaction object as an argument. This is to prevent long lived transactions and force them to be as concise as possible. The transaction objects implement much the same methods as the `Db` itself (`exec` / `exec_mut`). It is not possible to nest transactions but you can run immutable queries within a mutable transaction `TransactionMut`.
### QueryError, DbError

Note that you cannot manually abort, rollback or commit the transaction. These are handled by the database itself based on the result of the closure. If it's `Ok` the transaction will be committed (in case `mutable` queries as there is nothing to commit for `immutable` queries). If the result is `Err` the transaction will be rolled back.
Failure when running a query is reported through a single `QueryError` object which can optionally hold internal error (or chain of errors) that led to the failure. Most commonly it will represent **data error** or **logic error** in your query. Less commonly it may also report a failure to perform the requested operation due to underlying infrastructure issue (e.g. out of memory) in which case the nested error would be of type `DbError`. It is up to the client code to handle the errors.

In both cases the result will be returned and the signature of the transaction methods allows for custom mapping of the default `Result<QueryResult, QueryError>` to an arbitrary `<T, E>` result-error pair.

Worth noting is that regular `exec / exec_mut` methods on the `Db` object are actually implemented as transactions.

# QueryIds & QueryId
### QueryId, QueryIds

Most queries operate over a set of database ids. The `QueryIds` type is actually an enum:

Expand All @@ -327,7 +269,7 @@ pub enum QueryId {

This is because you can refer to the database elements via their numerical identifier or by the `string` alias (name). The `DbId` is then just a wrapper type: `pub struct DbId(pub i64)`. Both `QueryIds` and `QueryId` can be constructed from large number of different types like raw `i64`, `&str`, `String` or vectors of those etc.

# QueryValues
### QueryValues

The `QueryValues` is a an enum type that makes a distinction between singular and multiple values like so:

Expand All @@ -338,9 +280,9 @@ pub enum QueryValues {
}
```

This is especially important because it can change the meaning of query making use of this type. For example when inserting elements into the database and supplying `QueryValues::Single` all the elements will have the copy of the single set of properties associated with them. Conversely `QueryValues::Multi` will initialize each element with a different provided set of properties bu the number of inserted elements and the number of property sets must then match (it would be a query logic error if they did not match and the query would fail with such an error).
This is especially important because it can change the meaning of a query making use of this type. For example when inserting elements into the database and supplying `QueryValues::Single` all the elements will have the copy of the single set of properties associated with them. Conversely `QueryValues::Multi` will initialize each element with a different provided set of properties but the number of inserted elements and the number of property sets must then match (it would be a query logic error if they did not match and the query would fail with such an error).

# Mutable queries
## Mutable queries

Mutable queries are the way to modify the data in the database. Remember there can only be a mutable query running against the database at any one time preventing all other mutable or immutable queries running concurrently. There are two types of mutable queries:

Expand All @@ -349,6 +291,37 @@ Mutable queries are the way to modify the data in the database. Remember there c

The `insert` queries are used for both insert and updating data while `remove` queries are used to delete data from the database.

## Immutable queries

Immutable queries read the data from the database and there can be unlimited number of concurrent queries running against the database at the same time. There are two types of immutable queries:

- select
- search

The `select` queries are used to read the data from the database using known `id`s of elements. The `search` queries are used to find the `id`s and the result of search queries is thus often combined with the `select` queries.

## Transactions

You can run a series of queries as a transaction invoking corresponding methods on the database object:

```rs
impl Db {
// immutable transaction
pub fn transaction<T, E>(&self, mut f: impl FnMut(&Transaction) -> Result<T, E>) -> Result<T, E>

// mutable transaction
pub fn transaction_mut<T, E: From<QueryError>>(&mut self, mut f: impl FnMut(&mut TransactionMut) -> Result<T, E>) -> Result<T, E>
}
```

The transaction methods take a closure that itself takes a transaction object as an argument. This is to prevent long lived transactions and force them to be as concise as possible. The transaction objects implement the same execution methods as the `Db` itself (`exec` / `exec_mut`). It is not possible to nest transactions but you can run immutable queries within a mutable transaction `TransactionMut`.

Note that you cannot manually abort, rollback or commit the transaction. These are handled by the database itself based on the result of the closure. If it's `Ok` the transaction will be committed (in case of the `mutable` queries as there is nothing to commit for `immutable` queries). If the result is `Err` the transaction will be rolled back.

In both cases the result will be returned and the signature of the transaction methods allows for custom mapping of the default `Result<QueryResult, QueryError>` to an arbitrary `<T, E>` result-error pair.

Worth noting is that regular `exec / exec_mut` methods on the `Db` object are actually implemented as transactions.

## Insert

There are 5 distinct insert queries:
Expand Down Expand Up @@ -690,15 +663,6 @@ NOTE: See [`SelectValuesQuery`](#select-values) for more details.

The properties (key-value pairs) identified by `keys` and associated with `ids` [`QueryIds`](#queryids--queryid) will be removed from the database if they exist. It is an error if any of the `ids` do not exist in the database but it is NOT an error if any of the keys does not exist or is not associated as property to any of the `ids`.

# Immutable queries

Immutable queries read the data from the database and there can be unlimited number of concurrent queries running against the database at the same time. There are two types of immutable queries:

- select
- search

The `select` queries are used to read the data from the database using known `id`s of elements. The `search` queries are used to find the `id`s and the result of search queries is thus often combined with the `select` queries.

## Select

There are following select queries:
Expand Down Expand Up @@ -1050,6 +1014,10 @@ Finally the list of `conditions` that each examined graph element must satisfy t

**NOTE:** When both `origin` and `destination` are specified and the algorithm is switched to the `A*` the `limit` and `offset` are applied differently. In regular (open-ended) search the search will end when the `limit` is reached but with the path search (A\*) the `destination` must be reached first before they are applied.

### Paths

Path search (`from().to()`) uses A\* algorithm. Every element (node or edge) has a cost of `1` by default. If it passes all the conditions (the `SearchControl` value `true`) the cost will remain `1` and would be included in the result (if the path it is on would be selected). If it fails any of the conditions (the `SearchControl` value `false`) its cost will be `2`. This means that the algorithm will prefer paths where elements match the conditions rather than the absolutely shortest path (that can be achieved with no conditions). If the search is not to continue beyond certain element (through `beyond()`, `not_beyond()` or `distance()` conditions) its cost will be `0` and the paths it is on will no longer be considered for that search.

### Conditions

<table><tr><td><b>Struct</b></td></tr>
Expand Down Expand Up @@ -1229,10 +1197,6 @@ Most conditions result in `Continue(bool)` except for `distance()` and nested `w
| Key(Value) | YES | NO |
| Keys | YES | NO |

### Paths

Path search (`from().to()`) uses A\* algorithm. Every element (node or edge) has a cost of `1` by default. If it passes all the conditions (the `SearchControl` value `true`) the cost will remain `1` and would be included in the result (if the path it is on would be selected). If it fails any of the conditions (the `SearchControl` value `false`) its cost will be `2`. This means that the algorithm will prefer paths where elements match the conditions rather than the absolutely shortest path (that can be achieved with no conditions). If the search is not to continue beyond certain element (through `beyond()`, `not_beyond()` or `distance()` conditions) its cost will be `0` and the paths it is on will no longer be considered for that search.

---

For further examples and use cases see the [efficient agdb](/docs/references/efficient-agdb).

0 comments on commit f2da398

Please sign in to comment.