diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 8af51335b..f85390bbe 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -13,7 +13,7 @@
"label": "Coverage Db",
"type": "cargo",
"command": "llvm-cov",
- "args": ["--package", "agdb", "--all-features", "--ignore-filename-regex", "agdb_", "--show-missing-lines", "--hide-instantiations"],
+ "args": ["--package", "agdb", "--all-features", "--ignore-filename-regex", "agdb_", "--show-missing-lines"],
"problemMatcher": ["$rustc"],
"group": "build"
},
@@ -21,7 +21,7 @@
"label": "Coverage Db HTML",
"type": "cargo",
"command": "llvm-cov",
- "args": ["--package", "agdb", "--all-features", "--ignore-filename-regex", "agdb_", "--html", "--open", "--hide-instantiations"],
+ "args": ["--package", "agdb", "--all-features", "--ignore-filename-regex", "agdb_", "--html", "--open"],
"problemMatcher": ["$rustc"],
"group": "build"
},
@@ -29,7 +29,7 @@
"label": "Coverage Server",
"type": "cargo",
"command": "llvm-cov",
- "args": ["--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_derive|agdb_api|api.rs", "--show-missing-lines", "--hide-instantiations"],
+ "args": ["--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_derive|agdb_api|api.rs", "--show-missing-lines"],
"problemMatcher": ["$rustc"],
"group": "build"
},
@@ -37,7 +37,7 @@
"label": "Coverage Server HTML",
"type": "cargo",
"command": "llvm-cov",
- "args": ["--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_derive|agdb_api|api.rs", "--html", "--open", "--hide-instantiations"],
+ "args": ["--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_derive|agdb_api|api.rs", "--html", "--open"],
"problemMatcher": ["$rustc"],
"group": "build"
},
@@ -45,7 +45,7 @@
"label": "Coverage Api",
"type": "cargo",
"command": "llvm-cov",
- "args": ["--package", "agdb_api", "--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_server|agdb_derive", "--show-missing-lines", "--hide-instantiations"],
+ "args": ["--package", "agdb_api", "--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_server|agdb_derive", "--show-missing-lines"],
"problemMatcher": ["$rustc"],
"group": "build"
},
@@ -53,7 +53,7 @@
"label": "Coverage Api HTML",
"type": "cargo",
"command": "llvm-cov",
- "args": ["--package", "agdb_api", "--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_server|agdb_derive", "--html", "--open", "--hide-instantiations"],
+ "args": ["--package", "agdb_api", "--package", "agdb_server", "--all-features", "--ignore-filename-regex", "agdb(.|..)src|agdb_server|agdb_derive", "--html", "--open"],
"problemMatcher": ["$rustc"],
"group": "build"
},
diff --git a/README.md b/README.md
index b4c2d9923..833eb27e8 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@
+
@@ -46,7 +47,7 @@
##
Agnesoft Graph Database
-Why not SQL? | DECISION TREE | Troubleshooting
+Why not SQL? | DECISION TREE | Queries
The Agnesoft Graph Database (aka _agdb_) is persistent, optionally memory mapped graph database with native object 'no-text' queries. It can be used as a main persistent storage, data analytics platform as well as fast in-memory cache. Its typed schema-less data store allows for flexible and seamless data updates with no downtime or costly migrations. All queries are constructed via a builder pattern or directly as objects with no special language or text parsing.
@@ -167,35 +168,36 @@ For database concepts and primitive data types see [concepts](docs/concepts.md).
##
Decision Tree
```mermaid
-graph TD;
+flowchart TD;
A[Embedded or server?] --> Embedded
A --> B[Client or hosting?]
- Embedded --> Queries[Queries]
Embedded --> Studio[Studio]
+ Embedded --> Queries[Queries]
B --> Client
B --> Hosting
Client --> API[API]
Client --> Studio
+ Client --> Queries
Hosting --> Server[Server]
Hosting --> Cloud[Cloud]
```
##
Roadmap
-The following are planned features in no particular order:
-
-| Feature | Description |
-| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Data replication & RAFT protocol | Allow replication by connecting several database nodes together with a RAFT protocol. |
-| #\[no_std] | The `agdb` does not require any dependencies and thus should be (in theory) `no_std` friendly but it will likely require some development & testing. |
-| Agdb Studio | Graphical interface to `agdb` |
-| Python Client | Convenience client using bindings genereated from OpenAPI. |
-| Java Client | Convenience client using bindings genereated from OpenAPI. |
-| C# Client | Convenience client using bindings genereated from OpenAPI. |
-| C Client | Convenience client using bindings genereated from OpenAPI. |
-| C++ Client | Convenience client using bindings genereated from OpenAPI. |
-| Agdb Playground | Free public cloud-based playground to tinker with `agdb`. |
-| Public Cloud Offering | Commercial & supported `agdb` instance hosted in a public cloud. |
+The following are planned features with target versions:
+
+| Feature | Description | Version |
+| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
+| Agdb Studio | Graphical interface to `agdb` | 0.7.0 |
+| Python Client | Convenience client using bindings genereated from OpenAPI. | 0.7.0 |
+| Java Client | Convenience client using bindings genereated from OpenAPI. | 0.8.0 |
+| C# Client | Convenience client using bindings genereated from OpenAPI. | 0.8.0 |
+| C Client | Convenience client using bindings genereated from OpenAPI. | 0.8.0 |
+| C++ Client | Convenience client using bindings genereated from OpenAPI. | 0.8.0 |
+| Data replication & consensus protocol | Allow replication by connecting several database nodes together with a RAFT protocol. | 0.9.0 |
+| Agdb Playground | Free public cloud-based playground to tinker with `agdb`. | 0.9.0 |
+| #\[no_std] | The `agdb` does not require any dependencies and thus should be (in theory) `no_std` friendly but it will likely require some development & testing. | 1.0.0 |
+| Public Cloud Offering | Commercial & supported `agdb` instance hosted in a public cloud. | 1.0.0 |
##
Reference
diff --git a/agdb/src/query_builder.rs b/agdb/src/query_builder.rs
index 15cf6e295..3752d765c 100644
--- a/agdb/src/query_builder.rs
+++ b/agdb/src/query_builder.rs
@@ -82,7 +82,7 @@ impl QueryBuilder {
Remove {}
}
- /// Search teh database by traversing the graph
+ /// Search the database by traversing the graph
/// and returns element ids using breadth first,
/// depth first or A* algorithm:
///
diff --git a/agdb_server/Cargo.toml b/agdb_server/Cargo.toml
index 8853ac4f5..1b17f76e4 100644
--- a/agdb_server/Cargo.toml
+++ b/agdb_server/Cargo.toml
@@ -26,7 +26,7 @@ tower = "0.4"
tracing = "0.1"
tracing-subscriber = "0.3"
utoipa = "4"
-utoipa-rapidoc = { version = "2", features = ["axum"] }
+utoipa-rapidoc = { version = "3", features = ["axum"] }
uuid = { version = "1", features = ["v4"] }
[dev-dependencies]
diff --git a/agdb_server/src/db_pool.rs b/agdb_server/src/db_pool.rs
index f3160b8b4..a74d1698d 100644
--- a/agdb_server/src/db_pool.rs
+++ b/agdb_server/src/db_pool.rs
@@ -38,6 +38,7 @@ use std::sync::RwLockReadGuard;
use std::sync::RwLockWriteGuard;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
+use uuid::Uuid;
const SERVER_DB_NAME: &str = "mapped:agdb_server.agdb";
@@ -842,9 +843,9 @@ impl DbPool {
Ok(())
}
- pub(crate) fn save_token(&self, user: DbId, token: String) -> ServerResult {
+ pub(crate) fn user_token(&self, user: DbId) -> ServerResult {
self.db_mut()?.transaction_mut(|t| {
- let existing = t
+ let mut user_token = t
.exec(
&QueryBuilder::select()
.values(vec!["token".into()])
@@ -855,17 +856,19 @@ impl DbPool {
.values[0]
.value
.to_string();
- if existing.is_empty() {
+
+ if user_token.is_empty() {
+ let token_uuid = Uuid::new_v4();
+ user_token = token_uuid.to_string();
t.exec_mut(
&QueryBuilder::insert()
- .values_uniform(vec![("token", &token).into()])
+ .values_uniform(vec![("token", &user_token.clone()).into()])
.ids(user)
.query(),
)?;
- Ok(token.clone())
- } else {
- Ok(existing)
}
+
+ Ok(user_token)
})
}
diff --git a/agdb_server/src/routes/user.rs b/agdb_server/src/routes/user.rs
index 504da3774..5e93f0442 100644
--- a/agdb_server/src/routes/user.rs
+++ b/agdb_server/src/routes/user.rs
@@ -8,7 +8,6 @@ use agdb_api::UserLogin;
use axum::extract::State;
use axum::http::StatusCode;
use axum::Json;
-use uuid::Uuid;
#[utoipa::path(post,
path = "/api/v1/user/login",
@@ -32,13 +31,7 @@ pub(crate) async fn login(
return Err(ServerError::new(StatusCode::UNAUTHORIZED, "unuauthorized"));
}
- let token = if user.token.is_empty() {
- let token_uuid = Uuid::new_v4();
- let token = token_uuid.to_string();
- db_pool.save_token(user.db_id.unwrap(), token)?
- } else {
- user.token
- };
+ let token = db_pool.user_token(user.db_id.unwrap())?;
Ok((StatusCode::OK, Json(token)))
}
diff --git a/docs/queries.md b/docs/queries.md
index c237f5e50..8a4680f5c 100644
--- a/docs/queries.md
+++ b/docs/queries.md
@@ -1,5 +1,83 @@
# Queries
+```mermaid
+flowchart LR
+
+ QueryBuilder["QueryBuilder"] --> insert("insert")
+ QueryBuilder --> remove("remove")
+ QueryBuilder --> select("select")
+ QueryBuilder --> search("search")
+
+ insert --> i_aliases("aliases") --> i_a_ids("ids") --> InsertAliasesQuery["InsertAliasesQuery"]
+ insert --> i_edges("edges") --> i_e_from("from") --> i_e_to("to") --> InsertEdgesQuery["InsertEdgesQuery"]
+ i_e_to --> each("each") --> InsertEdgesQuery
+ i_e_to --> i_e_values("values")
+ each --> i_e_values_uniform("values_uniform") --> InsertEdgesQuery
+ each --> i_e_values("values") --> InsertEdgesQuery
+ insert --> i_index("index") --> InsertIndexQuery["InsertIndexQuery"]
+ insert --> i_nodes("nodes")
+ i_nodes --> i_n_values("values") --> InsertNodesQuery["InsertNodesQuery"]
+ i_nodes --> i_n_aliases("aliases")
+ i_n_count --> i_n_values_uniform("values_uniform")
+ i_n_aliases --> i_n_values
+ i_n_aliases --> InsertNodesQuery
+ i_n_aliases --> i_n_values_uniform
+ i_nodes --> i_n_count("count") --> InsertNodesQuery
+ insert --> i_element("element") --> InsertValuesQuery["InsertValuesQuery"]
+ insert --> i_elements("elements") --> InsertValuesQuery
+ insert --> i_values("values")
+ i_values --> i_v_ids("ids") --> InsertValuesQuery
+ insert --> i_values_uniform("values_uniform") --> InsertValuesQuery
+
+ remove --> r_aliases("aliases") --> RemoveAliasesQuery["RemoveAliasesQuery"]
+ remove --> r_ids("ids") --> RemoveQuery["RemoveQuery"]
+ remove --> r_index("index") --> RemoveIndexQuery["RemoveIndexQuery"]
+ remove --> r_values("values") --> r_v_ids("ids") --> RemoveValuesQuery["RemoveValuesQuery"]
+
+ select --> s_aliases("aliases") --> SelectAllAliasesQuery["SelectAllAliasesQuery"]
+ s_aliases --> s_a_ids("ids") --> SelectAliasesQuery["SelectAliasesQuery"]
+ select --> s_ids("ids") --> SelectQuery["SelectQuery"]
+ select --> s_indexes("indexes") --> SelectIndexesQuery["SelectIndexesQuery"]
+ select --> s_keys("keys") --> s_k_ids("ids") --> SelectKeysQuery["SelectKeysQuery"]
+ select --> key_count("key_count") --> s_k_c_ids("ids") --> SelectKeyCountQuery["SelectKeyCountQuery"]
+ select --> values("values") --> s_v_ids("ids") --> SelectValuesQuery["SelectValuesQuery"]
+
+ search --> index("index") --> s_i_value("value") --> SearchQuery["SearchQuery"]
+ search --> from("from") --> SearchQuery
+ from --> limit("limit") --> SearchQuery
+ from --> offset("offset")
+ offset --> limit
+ from --> order_by("order_by")
+ order_by --> offset
+ order_by --> limit
+ order_by --> SearchQuery
+ from --> where --> SearchQuery
+ from --> to("to")
+ to --> order_by
+ to --> offset
+ to --> limit
+ search --> breadth_first("breadth_first") --> from
+ search --> depth_first("depth_first") --> from
+ search --> to
+ depth_first --> to
+ breadth_first --> to
+ to --> where(("where"))
+ order_by --> where
+ offset --> where
+ limit --> where
+
+ condition --> SearchQuery
+ end_where("end_where") --> SearchQuery
+ where --> condition
+ modifier --> where
+ condition --> end_where
+ end_where --> logic
+ where --> modifier("not/beyond")
+ modifier --> condition[["distance
edge
edge_count
edge_count_from
edge_count_to
ids
key.value
keys
node"]]
+ condition --> logic("and/or")
+ logic --> where
+```
+
- [Queries](#queries)
- [DbUserValue](#dbuservalue)
- [QueryResult](#queryresult)
@@ -9,25 +87,25 @@
- [QueryValues](#queryvalues)
- [Mutable queries](#mutable-queries)
- [Insert](#insert)
- - [Insert nodes](#insert-nodes)
- - [Insert edges](#insert-edges)
- [Insert aliases](#insert-aliases)
- - [Insert indexes](#insert-indexes)
+ - [Insert edges](#insert-edges)
+ - [Insert index](#insert-index)
+ - [Insert nodes](#insert-nodes)
- [Insert values](#insert-values)
- [Remove](#remove)
- - [Remove elements](#remove-elements)
- [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 elements](#select-elements)
- - [Select values](#select-values)
+ - [Select indexes](#select-indexes)
- [Select keys](#select-keys)
- [Select key count](#select-key-count)
- - [Select indexes](#select-indexes)
- - [Select aliases](#select-aliases)
- - [Select all aliases](#select-all-aliases)
+ - [Select values](#select-values)
- [Search](#search)
- [Conditions](#conditions)
- [Truth tables](#truth-tables)
@@ -111,8 +189,6 @@ The `QueryResult` is the universal result type for all successful queries. It ca
```Rust
pub struct QueryResult {
pub result: i64,
- pub from: Option,
- pub to: Option,
pub elements: Vec,
}
```
@@ -124,6 +200,8 @@ The `elements` field hold the [database elements](concepts.md#graph) returned. E
```Rust
pub struct DbElement {
pub id: DbId,
+ pub from: Option,
+ pub to: Option,
pub values: Vec,
}
```
@@ -249,42 +327,52 @@ The `insert` queries are used for both insert and updating data while `remove` q
There are 5 distinct insert queries:
-- insert nodes
-- insert edges
- insert aliases
+- insert edges
+- insert nodes
- insert index
- insert values
-### Insert nodes
+### Insert aliases
+
+Struct | Result |
+
```Rust
-pub struct InsertNodesQuery {
- pub count: u64,
- pub values: QueryValues,
+pub struct InsertAliasesQuery {
+ pub ids: QueryIds,
pub aliases: Vec,
}
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::insert().nodes().count(2).query();
-QueryBuilder::insert().nodes().count(2).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query();
-QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).query();
-QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query();
-QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query();
-QueryBuilder::insert().nodes().values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query();
+pub struct QueryResult {
+ pub result: i64, // number of inserted/updated aliases
+ pub elements: Vec, // empty
+}
```
-The `count` is the number of nodes to be inserted into the database. It can be omitted (left `0`) if either `values` or `aliases` (or both) are provided. If the `values` is [`QueryValues::Single`](#queryvalues) you must provide either `count` or `aliases`. It is a logic error if the count cannot be inferred and is set to `0`. If both `values` [`QueryValues::Multi`](#queryvalues) and `aliases` are provided their lengths must match, otherwise it will result in a logic error. Empty alias (`""`) are not allowed. The values can be inferred from user defined types if they implement `DbUserValue` trait (`#derive(agdb::UserValue)`). Both singular nad vectorized versions are supported.
+ |
Builder |
+
+```Rust
+QueryBuilder::insert().aliases("a").ids(1).query();
+QueryBuilder::insert().aliases("a").ids("b").query(); // alias "b" is replaced with "a"
+QueryBuilder::insert().aliases(vec!["a", "b"]).ids(vec![1, 2]).query();
+```
-The result will contain:
+ |
-- number of nodes inserted
-- list of elements inserted with their ids (positive) but without the inserted values or aliases
+Inserts or updates aliases of existing nodes (and only nodes, edges cannot have aliases) through this query. It takes `ids` [`QueryIds`](#queryids--queryid) and list of `aliases` as arguments. The number of aliases must match the `ids` (even if they are a search query). Empty alias (`""`) are not allowed.
+
+Note that this query is also used for updating existing aliases. Byt inserting a different alias of an id that already has one that alias will be overwritten with the new one.
### Insert edges
+Struct | Result |
+
+
```Rust
pub struct InsertEdgesQuery {
pub from: QueryIds,
@@ -294,7 +382,16 @@ pub struct InsertEdgesQuery {
}
```
-Builder pattern:
+ |
+
+```Rust
+pub struct QueryResult {
+ pub result: i64, // number of inserted edges
+ pub elements: Vec, // list of inserted edges (only ids)
+}
+```
+
+ |
Builder |
```Rust
QueryBuilder::insert().edges().from(1).to(2).query();
@@ -309,60 +406,80 @@ QueryBuilder::insert().edges().from(QueryBuilder::search().from("a").where_().no
QueryBuilder::insert().edges().from(QueryBuilder::search().from("a").where_().node().query()).to(QueryBuilder::search().from("b").where_().node().query()).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query();
```
+ |
+
The `from` and `to` represents list of origins and destinations of the edges to be inserted. As per [`QueryIds`](#queryids--queryid) it can be a list, single value, search query or even a result of another query (e.g. [insert nodes](#insert-nodes)) through the call of convenient `QueryResult::ids()` method. All ids must be `node`s and all must exist in the database otherwise data error will occur. If the `values` is [`QueryValues::Single`](#queryvalues) all edges will be associated with the copy of the same properties. If `values` is [`QueryValues::Multi`](#queryvalues) then the number of edges being inserted must match the provided values otherwise a logic error will occur. By default the `from` and `to` are expected to be of equal length specifying at each index the pair of nodes to connect with an edge. If all-to-all is desired set the `each` flag to `true`. The rule about the `values` [`QueryValues::Multi`](#queryvalues) still applies though so there must be enough values for all nodes resulting from the combination. The values can be inferred from user defined types if they implement `DbUserValue` trait (`#derive(agdb::UserValue)`). Both singular nad vectorized versions are supported.
-The result will contain:
+### Insert index
-- number of edges inserted
-- list of elements inserted with their ids (negative) but without the inserted values
+Struct | Result |
+
-### Insert aliases
+```Rust
+pub struct InsertIndexQuery(pub DbValue);
+```
+
+ |
```Rust
-pub struct InsertAliasesQuery {
- pub ids: QueryIds,
- pub aliases: Vec,
+pub struct QueryResult {
+ pub result: i64, // number of indexed values
+ pub elements: Vec, // empty
}
```
-Builder pattern:
+ |
Builder |
```Rust
-QueryBuilder::insert().aliases("a").ids(1).query();
-QueryBuilder::insert().aliases("a").ids("b").query(); // alias "b" is replaced with "a"
-QueryBuilder::insert().aliases(vec!["a", "b"]).ids(vec![1, 2]).query();
+QueryBuilder::insert().index("key").query();
```
-Inserts or updates aliases of existing nodes (and only nodes, edges cannot have aliases) through this query. It takes `ids` [`QueryIds`](#queryids--queryid) and list of `aliases` as arguments. The number of aliases must match the `ids` (even if they are a search query). Empty alias (`""`) are not allowed.
-
-Note that this query is used also for updating existing aliases. Byt inserting a different alias of an id that already has one the alias will be overwritten with the new one.
+ |
-The result will contain:
+Creates an index for a key. The index is valid for the entire database including any and all existing values in the database. The purpose of the index is to provide faster lookup for data that is not modelled on the graph itself. Example can be looking up users by their username or token.
-- number of aliases inserted or updated
-- empty list of elements
+### Insert nodes
-### Insert indexes
+Struct | Result |
+
```Rust
-pub struct InsertIndexQuery(pub DbValue);
+pub struct InsertNodesQuery {
+ pub count: u64,
+ pub values: QueryValues,
+ pub aliases: Vec,
+}
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::insert().index("key").query();
+pub struct QueryResult {
+ pub result: i64, // number of inserted nodes
+ pub elements: Vec, // list of inserted nodes (only ids)
+}
```
-Creates an index for a key. The index is valid for the entire database including any and all existing values in the database. The purpose of the index is to provide faster lookup for data that is not modelled on the graph itself. Example can be looking up users by their username or token.
+ |
Builder |
+
+```Rust
+QueryBuilder::insert().nodes().count(2).query();
+QueryBuilder::insert().nodes().count(2).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query();
+QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).query();
+QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query();
+QueryBuilder::insert().nodes().aliases(vec!["a", "b"]).values_uniform(vec![("k", "v").into(), (1, 10).into()]).query();
+QueryBuilder::insert().nodes().values(vec![vec![("k", 1).into()], vec![("k", 2).into()]]).query();
+```
-The result will contain:
+ |
-- number of indexed values (0 for empty indexes)
-- empty list of elements
+The `count` is the number of nodes to be inserted into the database. It can be omitted (left `0`) if either `values` or `aliases` (or both) are provided. If the `values` is [`QueryValues::Single`](#queryvalues) you must provide either `count` or `aliases`. It is a logic error if the count cannot be inferred and is set to `0`. If both `values` [`QueryValues::Multi`](#queryvalues) and `aliases` are provided their lengths must match, otherwise it will result in a logic error. Empty alias (`""`) are not allowed. The values can be inferred from user defined types if they implement `DbUserValue` trait (`#derive(agdb::UserValue)`). Both singular nad vectorized versions are supported.
### Insert values
+Struct | Result |
+
+
```Rust
pub struct InsertValuesQuery {
pub ids: QueryIds,
@@ -370,7 +487,16 @@ pub struct InsertValuesQuery {
}
```
-Builder pattern:
+ |
+
+```Rust
+pub struct QueryResult {
+ pub result: i64, // number of inserted key-value pairs
+ pub elements: Vec, // empty
+}
+```
+
+ |
Builder |
```Rust
QueryBuilder::insert().element(&T { ... }).query(); //Where T: DbUserValue (i.e. #derive(UserValue))
@@ -381,106 +507,144 @@ QueryBuilder::insert().values_uniform(vec![("k", "v").into(), (1, 10).into()]).i
QueryBuilder::insert().values_uniform(vec![("k", "v").into(), (1, 10).into()]).ids(QueryBuilder::search().from("a").query()).query();
```
-Inserts or updates key-value pairs (properties) of existing elements. You need to specify the `ids` [`QueryIds`](#queryids--queryid) and the list of `values`. The `values` can be either [`QueryValues::Single`](#queryvalues) that will insert the single set of properties to all elements identified by `ids` or [`QueryValues::Multi`](#queryvalues) that will insert to each `id` its own set of properties but their number must match the number of `ids`. If the user defined type contains `db_id` field of type `Option` you can use the shorthand `insert().element() / .insert().elements()` that will infer the values and ids from your objects. All the rules as if specified manually still apply (e.g. the ids must exist in the database). The `values()` can be inferred from user defined types if they implement `DbUserValue` trait (`#derive(agdb::UserValue)`). Both singular nad vectorized versions are supported.
-
-Note that this query is used also for updating existing values. By inserting the same `key` its old value will be overwritten with the new one.
+ |
-The result will contain:
+Inserts or updates key-value pairs (properties) of existing elements. You need to specify the `ids` [`QueryIds`](#queryids--queryid) and the list of `values`. The `values` can be either [`QueryValues::Single`](#queryvalues) that will insert the single set of properties to all elements identified by `ids` or [`QueryValues::Multi`](#queryvalues) that will insert to each `id` its own set of properties but their number must match the number of `ids`. If the user defined type contains `db_id` field of type `Option` you can use the shorthand `insert().element() / .insert().elements()` that will infer the values and ids from your types. All the rules as if specified manually still apply (e.g. the ids must exist in the database). The `values()` can be inferred from user defined types if they implement `DbUserValue` trait (`#derive(agdb::UserValue)`). Both singular nad vectorized versions are supported.
-- number of key-value pairs (properties) inserted
-- empty list of elements
+Note that this query is also used for updating existing values. By inserting the same `key` its old value will be overwritten with the new one.
## Remove
-There are 3 distinct remove queries:
+There are 4 distinct remove queries:
-- remove (elements)
- remove aliases
+- remove (elements)
+- remove index
- remove values
-### Remove elements
+### Remove aliases
+
+Struct | Result |
+
```Rust
-pub struct RemoveQuery(pub QueryIds);
+pub struct RemoveAliasesQuery(pub Vec);
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::remove().ids(1).query();
-QueryBuilder::remove().ids("a").query();
-QueryBuilder::remove().ids(vec![1, 2]).query();
-QueryBuilder::remove().ids(vec!["a", "b"]).query();
-QueryBuilder::remove().ids(QueryBuilder::search().from("a").query()).query();
+pub struct QueryResult {
+ pub result: i64, // negative number of removed aliases
+ pub elements: Vec, // empty
+}
```
-The elements identified by [`QueryIds`](#queryids--queryid) will be removed from the database if they exist. It is NOT an error if the elements to be removed do not exist in the database. All associated properties (key-value pairs) are also removed from all elements. Removing nodes will also remove all their edges (incoming and outgoing) and their properties.
+ |
Builder |
+
+```Rust
+QueryBuilder::remove().aliases("a").query();
+QueryBuilder::remove().aliases(vec!["a", "b"]).query();
+```
-The result will contain:
+ |
-- negative number of elements removed (edges not explicitly listed or those listed but removed as part of one of their node's removal do not contribute to the result counter)
-- empty list of elements
+The aliases listed will be removed from the database if they exist. It is NOT an error if the aliases do not exist in the database.
-### Remove aliases
+### Remove elements
+
+Struct | Result |
+
```Rust
-pub struct RemoveAliasesQuery(pub Vec);
+pub struct RemoveQuery(pub QueryIds);
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::remove().aliases("a").query();
-QueryBuilder::remove().aliases(vec!["a", "b"]).query();
+pub struct QueryResult {
+ pub result: i64, // negative number of removed ids
+ // (does not include removed edges
+ // unless listed in query ids)
+ pub elements: Vec, // empty
+}
```
-The aliases listed will be removed from the database if they exist. It is NOT an error if the aliases do not exist in the database.
+ |
Builder |
-The result will contain:
+```Rust
+QueryBuilder::remove().ids(1).query();
+QueryBuilder::remove().ids("a").query();
+QueryBuilder::remove().ids(vec![1, 2]).query();
+QueryBuilder::remove().ids(vec!["a", "b"]).query();
+QueryBuilder::remove().ids(QueryBuilder::search().from("a").query()).query();
+```
-- negative number of aliases removed
-- empty list of elements
+ |
+
+The elements identified by [`QueryIds`](#queryids--queryid) will be removed from the database if they exist. It is NOT an error if the elements to be removed do not exist in the database. All associated properties (key-value pairs) are also removed from all elements. Removing nodes will also remove all their edges (incoming and outgoing) and their properties.
### Remove index
+Struct | Result |
+
+
```Rust
pub struct RemoveIndexQuery(pub DbValue);
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::remove().index("key").query()
+pub struct QueryResult {
+ pub result: i64, // negative number of values removed
+ // from the index
+ pub elements: Vec, // empty
+}
```
-Removes an index from the database. It is NOT an error if the index does not exist in the database.
+ |
Builder |
-The result will contain:
+```Rust
+QueryBuilder::remove().index("key").query()
+```
+
+ |
-- negative number of removed indexed values
-- empty list of elements
+Removes an index from the database. It is NOT an error if the index does not exist in the database.
### Remove values
+Struct | Result |
+
+
```Rust
pub struct RemoveValuesQuery(pub SelectValuesQuery);
```
-NOTE: See [`SelectValuesQuery`](#select-values) for more details.
+ |
-Builder pattern:
+```Rust
+pub struct QueryResult {
+ pub result: i64, // negative number of actually removed
+ // key-value pairs
+ pub elements: Vec, // empty
+}
+```
+
+ |
Builder |
```Rust
QueryBuilder::remove().values(vec!["k1".into(), "k2".into()]).ids(vec![1, 2]).query();
QueryBuilder::remove().values(vec!["k1".into(), "k2".into()]).ids(QueryBuilder::search().from("a").query()).query();
```
-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 a data 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`.
+ |
-The result will contain:
+NOTE: See [`SelectValuesQuery`](#select-values) for more details.
-- Number of actually removed key-value pairs
-- empty list of elements
+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
@@ -493,163 +657,241 @@ The `select` queries are used to read the data from the database using known `id
## Select
-There are 6 select queries:
+There are 7 select queries:
+- select aliases
+- select all aliases
- select (elements)
-- select values
+- select indexes
- select keys
- select key count
-- select aliases
-- select all aliases
+- select values
-### Select elements
+### Select aliases
+
+Struct | Result |
+
```Rust
-pub struct SelectQuery(pub QueryIds);
+pub struct SelectAliasesQuery(pub QueryIds);
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::select().ids("a").query();
-QueryBuilder::select().ids(vec![1, 2]).query();
-QueryBuilder::select().ids(QueryBuilder::search().from(1).query()).query();
+pub struct QueryResult {
+ pub result: i64, // number of returned elements
+ pub elements: Vec, // list of elements each with
+ // a single property
+ // (`String("alias")`: `String`)
+}
```
-Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search query with all their properties. If any of the ids does not exist in the database running the query will return an error. The search query is most commonly used to find, filter or otherwise limit what elements to select.
+ |
Builder |
-The result will contain:
+```Rust
+QueryBuilder::select().aliases().ids(vec![1, 2]).query();
+QueryBuilder::select().aliases().ids(QueryBuilder::search().from(1).query()).query();
+```
-- number of returned elements
-- list of elements with all properties
+ |
-### Select values
+Selects aliases of the `ids` [`QueryIds`](#queryids--queryid) or a search. If any of the ids does not have an alias running the query will return an error.
+
+### Select all aliases
+
+Struct | Result |
+
```Rust
-pub struct SelectValuesQuery {
- pub keys: Vec,
- pub ids: QueryIds,
+pub struct SelectAllAliases {}
+```
+
+ |
+
+```Rust
+pub struct QueryResult {
+ pub result: i64, // number of elements with aliases
+ pub elements: Vec, // list of elements with an
+ // alias each with a single
+ // property (`String("alias"): String`)
}
```
-Builder pattern:
+ |
Builder |
```Rust
-QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids("a").query();
-QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids(vec![1, 2]).query();
-QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids(QueryBuilder::search().from(1).query()).query();
+QueryBuilder::select().aliases().query()
```
-Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search query with only selected properties (identified by the list of keys). If any of the ids does not exist in the database or does not have all the keys associated with it then running the query will return an error. While the search query is most commonly used to find, filter or otherwise limit what elements to select, using this particular query can limit what properties will be returned. If you plan to convert the result into your user defined type(s) you should use `T::db_keys()` provided through the `DbUserValue` trait (`#derive(UserValue)`) as argument to `values()`.
+ |
-The result will contain:
+Selects all aliases in the database.
-- number of returned elements
-- list of elements with only selected properties
+### Select elements
-### Select keys
+Struct | Result |
+
```Rust
-pub struct SelectKeysQuery(pub QueryIds);
+pub struct SelectQuery(pub QueryIds);
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::select().keys().ids("a").query();
-QueryBuilder::select().keys().ids(vec![1, 2]).query();
-QueryBuilder::select().keys().ids(QueryBuilder::search().from(1).query()).query();
+pub struct QueryResult {
+ pub result: i64, // number of returned elements
+ pub elements: Vec, // list of elements with
+ // all properties
+}
```
-Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search query with only keys returned. If any of the ids does not exist in the database running the query will return an error. This query is most commonly used for establishing what data is available in on the graph elements (e.g. when transforming the data into a table this query could be used to populate the column names).
+ |
Builder |
-The result will contain:
+```Rust
+QueryBuilder::select().ids("a").query();
+QueryBuilder::select().ids(vec![1, 2]).query();
+QueryBuilder::select().ids(QueryBuilder::search().from(1).query()).query();
+```
-- number of returned elements
-- list of elements with only keys and default (empty `I64(0)` values)
+ |
-### Select key count
+Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search query with all their properties. If any of the ids does not exist in the database running the query will return an error. The search query is most commonly used to find, filter or otherwise limit what elements to select.
+
+### Select indexes
+
+Struct | Result |
+
```Rust
-pub struct SelectKeyCountQuery(pub QueryIds);
+pub struct SelectIndexesQuery {};
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::select().key_count().ids("a").query();
-QueryBuilder::select().key_count().ids(vec![1, 2]).query();
-QueryBuilder::select().key_count().ids(QueryBuilder::search().from(1).query()).query();
+pub struct QueryResult {
+ pub result: i64, // number of indexes in the database
+ pub elements: Vec, // single element with id 0 and list of
+ // properties representing each index
+ // (`DbValue`: `u64`) where the key is
+ // the indexed key and the value is number
+ // of indexed values in the index.
+}
```
-Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search query with only key count returned. If any of the ids does not exist in the database running the query will return an error. This query is most commonly used for establishing how many properties there are associated with the graph elements.
+ |
Builder |
+
+```Rust
+QueryBuilder::select().indexes().query();
+```
+
+ |
-The result will contain:
+Selects all indexes in the database.
-- number of returned elements
-- list of elements each with a single property (`String("key_count")`: `u64`)
+### Select keys
-### Select indexes
+Struct | Result |
+
```Rust
-pub struct SelectIndexesQuery {};
+pub struct SelectKeysQuery(pub QueryIds);
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::select().indexes().query();
+pub struct QueryResult {
+ pub result: i64, // number of returned elements
+ pub elements: Vec, // list of elements with only keys
+ // defaulted values will be `I64(0)`
+}
```
-Selects all indexes in the database.
+ |
Builder |
-The result will contain:
+```Rust
+QueryBuilder::select().keys().ids("a").query();
+QueryBuilder::select().keys().ids(vec![1, 2]).query();
+QueryBuilder::select().keys().ids(QueryBuilder::search().from(1).query()).query();
+```
-- number of indexes
-- single element with id 0 and list of properties representing each index (`DbValue`: `u64`) where the key is the indexed key and the value is number of indexed values in the index.
+ |
-### Select aliases
+Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search query with only keys returned. If any of the ids does not exist in the database running the query will return an error. This query is most commonly used for establishing what data is available in on the graph elements (e.g. when transforming the data into a table this query could be used to populate the column names).
+
+### Select key count
+
+Struct | Result |
+
```Rust
-pub struct SelectAliasesQuery(pub QueryIds);
+pub struct SelectKeyCountQuery(pub QueryIds);
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::select().aliases().ids(vec![1, 2]).query();
-QueryBuilder::select().aliases().ids(QueryBuilder::search().from(1).query()).query();
+pub struct QueryResult {
+ pub result: i64, // number of returned elements
+ pub elements: Vec, // list of elements each with a
+ // single property
+ // (`String("key_count")`: `u64`)
+}
```
-Selects aliases of the `ids` [`QueryIds`](#queryids--queryid) or a search. If any of the ids does not have an alias running the query will return an error.
+ |
Builder |
+
+```Rust
+QueryBuilder::select().key_count().ids("a").query();
+QueryBuilder::select().key_count().ids(vec![1, 2]).query();
+QueryBuilder::select().key_count().ids(QueryBuilder::search().from(1).query()).query();
+```
+
+ |
-The result will contain:
+Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search query with only key count returned. If any of the ids does not exist in the database running the query will return an error. This query is most commonly used for establishing how many properties there are associated with the graph elements.
-- number of returned elements
-- list of elements each with a single property (`String("alias")`: `String`)
+### Select values
-### Select all aliases
+Struct | Result |
+
```Rust
-pub struct SelectAllAliases {}
+pub struct SelectValuesQuery {
+ pub keys: Vec,
+ pub ids: QueryIds,
+}
```
-Builder pattern:
+ |
```Rust
-QueryBuilder::select().aliases().query()
+pub struct QueryResult {
+ pub result: i64, // number of returned elements
+ pub elements: Vec, // list of elements with only
+ // selected properties
+}
```
-Selects all aliases in the database.
+ |
Builder |
-The result will contain:
+```Rust
+QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids("a").query();
+QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids(vec![1, 2]).query();
+QueryBuilder::select().values(vec!["k".into(), "k2".into()]).ids(QueryBuilder::search().from(1).query()).query();
+```
-- number of elements with aliases
-- list of elements with an alias each with a single property (`String("alias"): String`)
+ |
+
+Selects elements identified by `ids` [`QueryIds`](#queryids--queryid) or search query with only selected properties (identified by the list of keys). If any of the ids does not exist in the database or does not have all the keys associated with it then running the query will return an error. While the search query is most commonly used to find, filter or otherwise limit what elements to select, using this particular query can limit what properties will be returned. If you plan to convert the result into your user defined type(s) you should use `T::db_keys()` provided through the `DbUserValue` trait (`#derive(UserValue)`) as argument to `values()`.
## Search
-There is only a single search query that provides the ability to search the graph or indexes. When searching the graph it examines connected elements and their properties. While it is possible to construct the search queries manually, specifying conditions manually in particular can be excessively difficult and therefore **using the builder pattern is recommended**. The default search algorithm is `breadth first` however you can choose to use `depth first`. For path search the `A*` algorithm is used. For searching an index the algorithm is `index`.
+Struct | Result |
+
```Rust
pub struct SearchQuery {
@@ -661,7 +903,20 @@ pub struct SearchQuery {
pub order_by: Vec,
pub conditions: Vec,
}
+```
+
+ |
+```Rust
+pub struct QueryResult {
+ pub result: i64, // number of elements found
+ pub elements: Vec, // list of elements found (only ids)
+}
+```
+
+ |
+
+```Rust
pub enum SearchQueryAlgorithm {
BreadthFirst,
DepthFirst,
@@ -674,18 +929,15 @@ pub enum DbKeyOrder {
}
```
-Builder pattern:
+ | |
Builder |
```Rust
QueryBuilder::search().from("a").query();
QueryBuilder::search().to(1).query(); //reverse search
-QueryBuilder::search().from("a").to("b").query(); //path search, A*
-
-QueryBuilder::search().breadth_first().from("a").query(); //breadth first is the default and can be omitted however
+QueryBuilder::search().from("a").to("b").query(); //path search using A* algorithm
+QueryBuilder::search().breadth_first().from("a").query(); //breadth first is the default and can be omitted
QueryBuilder::search().depth_first().from("a").query();
-
-QueryBuilder::search().index("age").value(20).query();
-
+QueryBuilder::search().index("age").value(20).query(); //index search
//limit, offset and order_by can be applied similarly to all the search variants
QueryBuilder::search().from(1).order_by(vec![DbKeyOrder::Desc("age".into()), DbKeyOrder::Asc("name".into())]).query()
QueryBuilder::search().from(1).offset(10).query();
@@ -696,6 +948,10 @@ QueryBuilder::search().from(1).order_by(vec![DbKeyOrder::Desc("k".into())]).offs
QueryBuilder::search().from(1).offset(10).limit(5).query();
```
+ |
+
+There is only a single search query that provides the ability to search the graph or indexes. When searching the graph it examines connected elements and their properties. While it is possible to construct the search queries manually, specifying conditions manually in particular can be excessively difficult and therefore **using the builder pattern is recommended**. The default search algorithm is `breadth first` however you can choose to use `depth first`. For path search the `A*` algorithm is used. For searching an index the algorithm is `index`.
+
If the index search is done the graph traversal is skipped entirely as are most of the parameters including like limit, offset, ordering and conditions.
The graph search query is made up of the `origin` and `destination` of the search and the algorithm. Specifying only `origin` (from) will result in a search along `from->to` edges. Specifying only `destination` (to) will result in the reverse search along the `to<-from` edges. When both `origin` and `destination` are specified the search algorithm becomes a path search and the algorithm used will be `A*`. Optionally you can specify a `limit` (0 = unlimited) and `offset` (0 = no offset) to the returned list of graph element ids. If specified (!= 0) the `origin` and the `destination` must exist in the database, otherwise an error will be returned. The elements can be optionally ordered with `order_by` list of keys allowing ascending/descending ordering based on multiple properties.
@@ -706,30 +962,8 @@ Finally the list of `conditions` that each examined graph element must satisfy t
### Conditions
-The currently supported conditions are:
-
-- Where (opens nested list of conditions)
-- Edge (if the element is an `edge`)
-- Node (if the element is a `node`)
-- Distance (if the current distance of the search satisfies the numerical comparison, each graph element away from the start increases the distance, including edges, i.e. second node from start is at distance `2`)
-- EdgeCount (if the element is a node and total number of edges (in and out) satisfies the numerical comparison - self-referential edges are counted twice)
-- EdgeCountFrom (if the element is a node and total number of outgoing edges satisfies the numerical comparison)
-- EdgeCountTo (if the element is a node and total number of incoming edges satisfies the numerical comparison)
-- Ids (if the element id is in the list)
-- KeyValue (if the element's property has the `key` and its value satisfies `value` comparison)
-- Keys (if the element has all the `keys` regardless of their values)
-- EndWhere (closes nested list of conditions)
-
-All conditions can be further modified as follows:
-
-- Beyond (continues the search only beyond this element)
-- Not (reverses the condition result)
-- NotBeyond (stops the search beyond this element)
-
-The conditions can be changed with logic operators:
-
-- And (logical `and`)
-- Or (logical `or`)
+Struct |
+
```Rust
pub struct QueryCondition {
@@ -783,7 +1017,7 @@ pub enum Comparison {
}
```
-Builder pattern:
+ |
Builder |
```Rust
//the where_() can be applied to any of the basic search queries after order_by/offset/limit
@@ -808,6 +1042,33 @@ QueryBuilder::search().from(1).where_().node().or().where_().edge().and().key("k
QueryBuilder::search().from(1).where_().node().or().where_().edge().and().key("k").value(Comparison::Contains(vec![1, 2].into())).end_where().query();
```
+ |
+
+The currently supported conditions are:
+
+- Where (opens nested list of conditions)
+- Edge (if the element is an `edge`)
+- Node (if the element is a `node`)
+- Distance (if the current distance of the search satisfies the numerical comparison, each graph element away from the start increases the distance, including edges, i.e. second node from start is at distance `2`)
+- EdgeCount (if the element is a node and total number of edges (in and out) satisfies the numerical comparison - self-referential edges are counted twice)
+- EdgeCountFrom (if the element is a node and total number of outgoing edges satisfies the numerical comparison)
+- EdgeCountTo (if the element is a node and total number of incoming edges satisfies the numerical comparison)
+- Ids (if the element id is in the list)
+- KeyValue (if the element's property has the `key` and its value satisfies `value` comparison)
+- Keys (if the element has all the `keys` regardless of their values)
+- EndWhere (closes nested list of conditions)
+
+All conditions can be further modified as follows:
+
+- Beyond (continues the search only beyond this element)
+- Not (reverses the condition result)
+- NotBeyond (stops the search beyond this element)
+
+The conditions can be changed with logic operators:
+
+- And (logical `and`)
+- Or (logical `or`)
+
NOTE: The use of `where_` with an underscore as the method name is necessary to avoid conflict with the Rust keyword.
The conditions are applied one at a time to each visited element and chained using logic operators `AND` and `OR`. They can be nested using `where_` and `end_where` (in place of brackets). The condition evaluator supports short-circuiting not evaluating conditions further if the logical outcome cannot change. The condition comparators are type strict meaning that they do not perform type conversions nor coercion (e.g. `Comparison::Equal(1_i64).compare(1_u64)` will evaluate to `false`). Slight exception to this rule is the `Comparison::Contains` as it allows vectorized version of the base type (e.g. `Comparison::Contains(vec!["bc", "ef"]).compare("abcdefg")` will evaluate to `true`).