Introduction
WooriDB is a general purpose (EXPERIMENTAL) time serial database, which means it contains all entity registries indexed by DateTime and Uuid. It is schemaless, deep key-value storage and uses its own query syntax that is similar to SparQL and Crux's Datalog.
Name origin
Woori means our, in Korean, and although I developed this DB initially alone, it is in my culture and my hunsband's culture to call everything that is done for our communities and by our communities ours. I chose Woori instead of shelanu or bizdin because it easier to pronunce.
Project inspirations
- Crux a general purpose bitemporal graph query database with support for SQL and Datalog. It was the ideallogical source of WooriDB. I had developed a rust client called Transistor which gave me the basic concepts of what I wanted WooriDB to have.
- Datomic is a transactional database with a flexible data model, temporality and rich queries. I worked with Datomic at Nubank and it is the reason I found Juxt/Crux.
- Prometheus An open-source monitoring system with a dimensional data model, flexible query language, efficient time series database and modern alerting approach.
- SparQL SPARQL is a query language for RDF graph databases, it is flexible enough for query information based on datetime indexes.
- Book Database Internals
- Book Database System Concept
- Book Designing Data Intensive Application
- Professor Andy Pavlo Database Design Course.
- Zero Trust in Time Series Data?
Naming conventions:
- Entity Tree is similar to a SQL table, it is the data structure that contains all ids and entities map relations.
- Entity ID is the id of an entity inside of an Entity Tree.
- Entity map is the content of and entity associated with the entity id.
Installation and Important Information
Installation
To run WooriDB it is necessary to have Rust installed in the machine. There are two ways to do this:
- Go to rustup.rs and copy the command there, for unix it is
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh. - Clone WooriDB and execute
make setup.
Executing WooriDB
Release mode performance:make releasein project root for performance optimization.Release mode size:make runin project root for size optimization.Debug mode:make debugin project root.
Docker
You can find the latest docker image at naomijub/wooridb. Currently the most stable tag is beta-8. To execute the docker container run:
docker run -p 1438:1438 naomijubs/wooridb:beta-8 debugfor debug mode.docker run -p 1438:1438 -e AUTH_HASHING_COST=8 -e ADMIN=your-admin-id -e ADMIN_PASSWORD=your-admin-pswd naomijubs/wooridb:beta-8 runfor size optimization.docker run -p 1438:1438 -e AUTH_HASHING_COST=8 -e ADMIN=your-admin-id -e ADMIN_PASSWORD=your-admin-pswd naomijubs/wooridb:beta-8 releasefor performance optimization.- All
-e/--envcan be replaced by a--env-file path/to/your/.env. Your.envfile should contain the following fields:
HASHING_COST=16
PORT=1438
SESSION_EXPIRATION_TIME=3600
MAX_CONNECTIONS=1000
CLIENT_SHUTDOWN=5000
AUTH_HASHING_COST=8
ADMIN=your-admin-id
ADMIN_PASSWORD=your-admin-pswd
Important Information
- Responses are in
RONformat. BLOBwill not be supported. Check out To BLOB or Not To BLOB: Large Object Storage in a Database or a Filesystem.
Configurations
- To run the project in
debugmode it is important to export the following environment variablesHASHING_COST, PORT, MAX_CONNECTIONS, CLIENT_SHUTDOWN. Default values are:
HASHING_COST=16
PORT=1438
MAX_CONNECTIONS=1000
CLIENT_SHUTDOWN=5000
MAX_CONNECTIONSis the maximum number of connections supported simultaneously.CLIENT_SHUTDOWNis the time to drop a client's connection, use 0 to disable it.- To run the project in
releasemode it is important to export the following environment variablesHASHING_COST, PORT, MAX_CONNECTIONS, CLIENT_SHUTDOWN, AUTH_HASHING_COST, ADMIN, ADMIN_PASSWORD, SESSION_EXPIRATION_TIME. There are no default values forAUTH_HASHING_COST, ADMIN, ADMIN_PASSWORD, SESSION_EXPIRATION_TIME.
Features
- Sintax inspired by SparQL and Crux's datalog.
- Entities are indexed by date(time).
- Schemaless.
- Deep Key-value storage, which means that you can have key values inside hashmap values.
- Hashing entity map keys content with
ENCRYPT. - Unique entity maps keys content with
UNIQUEfor an entity tree. - Hashed values are filtered out of
SELECTand can only be checked withCHECKkeyword. Ronschemas for input and output.- JSON is supported via feature.
- EDN to be supported via feature.
- Entities are also indexed by
entity_name(entity tree key) andUuid(entity id). Entity map format is a HashMap where keys are strings and values are supportedTypes. - Stores persistent data locally.
-
S3as a backend is to be developed. -
DynamoDBas a backend is to be developed.
-
- Able to handle very large numbers when using the
Psuffix.- Ex:
98347883122138743294728345738925783257325789353593473247832493483478935673.9347324783249348347893567393473247832493483478935673P.
- Ex:
- Configuration is done via environment variables.
- CORS
- Authentication and Authorization via session token
- Users created and deleted by ADMIN user.
- Conditional Update
- File compression done with
zstd. - Entity id history
-
Possible Relation Algebra
- Union by entity_id
- Intersection by entity_id
- Difference by entity_id
- Join
- Dedup
- Sort
- Aggregate
Naming conventions:
- Entity Tree is similar to SQL table, it is the data structure that contains all ids and entities map relations.
- Entity ID is the id of an entity inside Entity tree.
- Entity map is the content of and entity associated with the entity id.
Woori Query Language
Woori Query language or WQL is WooriDB's Query Language and it is inspired by SparQL, Datalog and SQL. Its main features are:
Transactions
CREATEentity tree key by name.UNIQUE: With unique values for entity map keys inside entity tree.ENCRYPTS: With encrypted values for defined key-values inside entity map.
INSERTentity map into entity tree.UPDATEs withSETorCONTENTentity map.- SET UPDATE replaces the sent entity map as the entity's map content.
- CONTENT UPDATE updates numerical and string the current entity's map content with the sent entity map value and the other values work the same way as SET.
MATCH UPDATEupdates entity map content with new content if match condition is satisfied.DELETEs the last entity map content for an entity id.EVICT- Evicts a specific entity id and entity map
- Evicts all entities in the entity tree key.
Queries
SELECTthe only way to retrieve an entity's content.WHEREclause is available.WHENclause is available
CHECKthe only way to verify keys that are encrypted.
ALL DATA STRUCTURES HASHMAPS, HASHSETS AND LIST MUST CONTAIN A
,AFTER EACH ELEMENT. Example#{name, ssn,}is valid but#{name, ssn}is not valid.
Examples
CREATE
Creates an entity tree key.
CREATE ENTITY my_entitythis will create an entity tree key namedmy_entity, in SQL terms it meansCREATE TABLE my_entity.CREATE ENTITY my_entity UNIQUES #{name, ssn,}the entity tree key namedmy_entitywill only allow unique values for the entities keysnameandssnin its maps.CREATE ENTITY my_entity ENCRYPTS #{pswd,}the entity tree key namedmy_entitywill encrypt the entities keys that arepswd. The hashing cost of the encrypt is defined by the environment variableHASHING_COST, the recommended is between 10 and 14.- Encrypted keys cannot be unique so
CREATE ENTITY my_entity UNIQUES #{name, ssn, pswd,} ENCRYPTS #{pswd,}is invalid butCREATE ENTITY my_entity UNIQUES #{name, ssn,} ENCRYPTS #{pswd,}is valid.
INSERT
Inserts an entity id and an entity map into entity tree key.
INSERT {a: 123, b: "hello julia",} INTO entity_keythis will insert the entity map{a: 123, b: "hello julia",}(keyacontaining asType::Integer(123)and keybcontaining aType::String("hello julia")) and a random Uuid for entity ID into entity tree keyentity_key.
To INSERT entity with a predefined Uuid it is necessary to use the keyword WITH after the entity tree key followed by as Uuid-V4. INSERT {a: 123, b: "hello julia",} INTO entity_key WITH 4f6fccb0-20fb-4d8e-af7c-65db30f4954a.
UPDATE SET
Updates the content by replacing the previous entity map in entity tree key my_entity_name with the entity id 48c7640e-9287-468a-a07c-2fb00da5eaed.
UPDATE my_entity_name SET {a: -4, b: 32,} INTO 48c7640e-9287-468a-a07c-2fb00da5eaedthis will replace the current entity map stored in entity id48c7640e-9287-468a-a07c-2fb00da5eaed.
UPDATE CONTENT
Updates the content by numerical addition or string concatenation of the previous entity map in entity tree key my_entity_name with the entity id 48c7640e-9287-468a-a07c-2fb00da5eaed. Non numerical or non string value will just be replaced. If the key doesn't exist it will be created.
UPDATE my_entity_name CONTENT {a: -4, b: 32,} INTO 48c7640e-9287-468a-a07c-2fb00da5eaedthis will add-4to entity map keyaand add32to entity map keybin the current entity map stored in entity id48c7640e-9287-468a-a07c-2fb00da5eaed.
MATCH UPDATE
Similar to SET, but it requires a precondition to be satisfied.
MATCH ALL(a == 1, b >= 3, c != \"hello\", d < 7,) UPDATE this_entity SET {a: 123, g: NiL,} INTO d6ca73c0-41ff-4975-8a60-fc4a061ce536if all conditions defined insideALLare satisfied the set update will happen.ALLis an logicalandbetween all conditions, meaning that all of them must be true.ANYis an logicalorbetween all conditions, meaning that at least one of them must be true.- NULL KEYS,
ALLreturns an error if a null key is present andANYjust ignores null keys. - Possible conditions are:
==means equals, so ifa == 100, this means that the entity map keyamust equal to100.!=means not equal, so ifa != 100, this means that the entity map keyamust not equal to100.>=means greater or equal, so ifa >= 100, this means that the entity map keyamust br greater or equal to100.<=means lesser or equal, so ifa <= 100, this means that the entity map keyamust be lesser or equal to100.>means greater, so ifa > 100, this means that the entity map keyamust be greater than100.<means lesser, so ifa < 100, this means that the entity map keyamust be less than100.
DELETE
Deletes the last entity map event for an entity ID in entity tree key, that is, it deletes the last state of an entity map.
DELETE 48c7640e-9287-468a-a07c-2fb00da5eaed FROM my_entity_namethis will delete the last state of entity id48c7640e-9287-468a-a07c-2fb00da5eaedin entity tree keymy_entity_namefrom entity history.
EVICT
Removes all occurrences of an entity from the entity tree. It can be just the entity id or the whole entity tree key.
EVICT 48c7640e-9287-468a-a07c-2fb00da5eaed FROM my_entity_nameremoves all occurrences of the entity id48c7640e-9287-468a-a07c-2fb00da5eaedfrom the entity tree keymy_entity_name, they cannot be queried anymore.EVICT my_entityremoves the keymy_entityfrom the entity tree. It cannot be queried anymore. It is similar to SQL'sDROP TABLE my_entity.
CHECK
Checks for encrypted key data validity. This transaction only works with keys that are encrypted and it serves as a way to verify if the passed values are true of false against encrypted data.
CHECK {pswd: "my-password", ssn: 3948453,} FROM my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaedthis will check if keyspsdwandssnfrom entity id48c7640e-9287-468a-a07c-2fb00da5eaedin entity tree keymy_entity_namehave the values"my-password"for pswd and3948453for ssn.
SELECT
This is the way to query entities from WooriDB. Similar to SQL and SparQL SELECT.
Possible SELECT combinations:
SELECT * FROM my_entity_nameselects all entity ids and entity maps for the entity tree keymy_entity_namewith all the possible entities map keys.SELECT #{name, last_name, age,} FROM my_entity_nameselects all entity ids and entity maps for the entity tree keymy_entity_namewith only the keysname, last_name, agefor the entities map.SELECT * FROM my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaedselects the entity map containing the entity id48c7640e-9287-468a-a07c-2fb00da5eaedfrom the entity tree keymy_entity_namewith all the possible entities map keys.SELECT #{name, last_name, age,} FROM my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaedselects the entity map containing the entity id48c7640e-9287-468a-a07c-2fb00da5eaedfrom the entity tree keymy_entity_namewith only the keysname, last_name, agefor the entities map.SELECT * FROM my_entity_name IDS IN #{48c7640e-9287-468a-a07c-2fb00da5eaed, 57c7640e-9287-448a-d07c-3db01da5earg, 54k6640e-5687-445a-d07c-5hg61da5earg,}this will return the entities map containing the entities ids#{48c7640e-9287-468a-a07c-2fb00da5eaed, 57c7640e-9287-448a-d07c-3db01da5earg, 54k6640e-5687-445a-d07c-5hg61da5earg,}from entity tree keymy_entity_name. Keys set is available.Select * FROM my_entity ID 0a1b16ed-886c-4c99-97c9-0b977778ec13 WHEN AT 2014-11-28T21:00:09+09:00this will select the last entity map state for the entity id0a1b16ed-886c-4c99-97c9-0b977778ec13in entity tree keymy_entityat date2014-11-28. Requires to use DateTime UTC, for now.SELECT * FROM entity_name ID <uuid> WHEN START 2014-11-28T09:00:09Z END 2014-11-28T21:00:09Zthis will select the all entity map states for the entity id0a1b16ed-886c-4c99-97c9-0b977778ec13in entity tree keymy_entityin the time range starting at2014-11-28T09:00:09Zand ending at2014-11-28T21:00:09Z.SELECT * FROM my_entity WHERE { ?* my_entity:a ?a, ?* my_entity:c ?c, (== ?a 123),(or (>= ?c 4300.0), (< ?c 6.9),),}this will select all entities ids and entities maps from entity tree keymy_entitythat satisfy the where clause.?* my_entity:a ?aand?* my_entity:c ?cdefine that the entity keysaandcfrom entity tree keymy_entitywill receive the attributed value?aand?crespectively.(== ?a 123)selects all entities which entity map keyais equal to123.(or (>= ?c 4300.0), (< ?c 6.9),)selects all entities which entity map keycis greater than or equal to4300.0or is smaller than6.9.
WHERE Clause
Possible functions for the where clause:
in:(in ?k1 123 34543 7645 435),?k1must be present in the set containing123 34543 7645 435. NOTE: for now, please don't use,.between:(between ?k1 0 435),?k1must be between starting value0and ending value435. If you set more than 2 arguments it will return aClauseError.like:(like ?k2 "%naomi%"), like is comparing?k2with the string"%naomi%"considering that%are wildcards."%naomi"meansend_with("naomi"),"naomi%"meansstarts_with("naomi")and"%naomi%"meanscontains("naomi"). Possible regex support in the future.==,>=,>,<,<=,!=->(>= ?k1 0)which means get all values that?k1is greater than or equal to0.or: All arguments inside theorfunction call will be evaluated totrueif any of them istrue.
Relation Algebra
Some relation algebra may be implemented:
- Projection
- Union
- Intersection
- Difference (SQL's EXCEPT?)
- Join
- Product (SQL's CROSS JOIN?)
- Assign
- Dedup
- Sort
- Aggregate
- Division
Entity map value TYPES
Types Notes
Precise floats or numbers larger than f64::MAX/i128::MAX can be defined with an UPPERCASE
Pat the end.
- Note: This type cannot be updated with
UPDATE CONTENT.- Ex.:
INSERT {a: 98347883122138743294728345738925783257325789353593473247832493483478935673.9347324783249348347893567393473247832493483478935673P, } INTO my_entity.
BLOBwill not be supported. Check out To BLOB or Not To BLOB: Large Object Storage in a Database or a Filesystem
-
Char(char)contains the type char defined by'c', -
Integer(isize)contains the type isize, just a number without., -
String(String)contains any value passed with"this is a string", -
Uuid(Uuid)contains anUuid V4, -
Float(f64)contains the type f64, any number containing., -
Boolean(bool)contains type booleantrueoffalse, -
Vector(Vec<Types>)contains a vector ofTypes, -
Map(HashMap<String, Types>)contains a HashMap of keyStringand valueTypes, -
Hash(String)contains a Hash generated byENCRYPTS, -
Precise(String)contains a very large integer or a very large float, -
Nilcontains anull/nilvalue, -
DateTimecontains a DateTime
Authorization and Authentication
Authentication and authorization only work with release mode, so cargo run --release is required.
Some environment variables are also required:
AUTH_HASHING_COST=8
ADMIN=your_admin
ADMIN_PASSWORD=your_password
AUTH_HASHING_COSTis the hashing cost for password in the authentication system.ADMINis the admin's user id.ADMIN_PASSWORDis the admin's user password.
One optional environment variable can be used to configure the time to expirate a session token. SESSION_EXPIRATION_TIME has a default value of 3600 seconds, or 1 hour.
Creating new users
ADMINis the only user role capable of creating new users. For now there can only be oneADMIN.
To create a new user, POST at /auth/createUser with your admin credentials and the new user info as follows (in RON format):
(
admin_id: "your_admin",
admin_password: "your_password",
user_info: (
user_password: "my_password",
role: [User,],
),
)
User information consists of the user's password to be used and the user's roles. Remember to always put , at the end.
Response to this request will be (user_id: \"<some-uuid>\",), containing the user's unique ID.
Available user roles are:
ADMIN- works primarily at/auth/createUser.USER- works on all/wql/query,/wql/txand/auth/putUserSession.WRITE- only works on/wql/txand/auth/putUserSession.READ- only works on/wql/queryand/auth/putUserSession.HISTORY- only works on/entity-history.- New roles to be added as needed.
Getting a session token
To make a request at WQL endpoints you need a session token that will expire within 3600 seconds. To retrieve a session token you need to PUT at endpoint /auth/putUserSession your user credentials as follows (in RON format):
(id: "<user_id>", user_password: "<user_password>",)
Response will be a plain/text with your token.
Making auth requests to /wql/tx and /wql/query.
To avoid authentication and authorization errors, add your token to the authorization bearer header, Authorization: Bearer <your session token>.
Your user needs the correct session token and the correct role for this request.
Deleting users
ADMINis the only user role capable of creating new users. For now there can only be oneADMIN.
To delete a list of users, POST at /auth/deleteUsers with your admin credentials and the users ids you want to delete as follows (in RON format):
(
admin_id: "your_admin",
admin_password: "your_password",
users_ids: [
"<uui1>",
"<uui2>",
"<uui3>",
],
)
The response will be a vector containing all Uuids sent or an error FailedToDeleteUsers.
TODOs:
- Adding other admins and removing admins is not yet implemented.
Transactions
Transaction is the name of all operations that change the database state, like CREATE, INSERT, UPDATE, MATCH, DELETE, EVICT. This is done by sending a POST request to endpoint <ip>:1438/wql/tx. An example request would be curl -X POST -H "Content-Type: application/wql" <ip>:1438/wql/tx -d 'CREATE ENTITY my_entity'. In release mode it is necessary to use header Authorization: Bearer <your session token> for this endpoint.
Reminder A comma is required at the end of every data structure representation. Ex.:
{a: 123, b: 456,},#{a, b, c,},(a, b, c,). No need for;at the end of each expression.
CREATE ENTITY
Similar to CREATE TABLE in SQL, it creates an entity tree which matches the table name. It requires an entity name like my_entity_name after CREATE ENTITY.
Example request:
CREATE ENTITY my_entity_name
Example response:
#![allow(unused)] fn main() { ( tx_type: Create, entity: "my_entity_name", uuid: None, state: "", message: "Entity `my_entity_name` created", ) }
CREATE ENTITY <entity> ENCRYPT #{keys...}: When the system has encrypted keys, theinsertanduploadrequests take longer due to hashing function and the verify function. This is determined by the hashing cost:
bench_cost_10 ... bench: 51,474,665 ns/iter (+/- 16,006,581)
bench_cost_14 ... bench: 839,109,086 ns/iter (+/- 274,507,463)
bench_cost_4 ... bench: 795,814 ns/iter (+/- 42,838)
bench_cost_default ... bench: 195,344,338 ns/iter (+/- 8,329,675)
* Note that I don't go above 14 as it takes too long. However, it is way safer, it is a trade-off.
INSERT
Inserts a HashMap<String, Types> into the entity tree key previously created (my_entity_name). This request returns a Uuid containing the entity id.
Example request:
INSERT {a: 123, c: \"hello\", d: \"world\",}
INTO my_entity_name
Example response:
#![allow(unused)] fn main() { ( tx_type: Insert, entity: "my_entity_name", uuid: Some("00d025c9-eda8-4190-a33a-29998bd77bd3"), state: "", message: "Entity my_entity_name inserted with Uuid 00d025c9-eda8-4190-a33a-29998bd77bd3", ) }
The request above will insert an entity with the following structure in my_entity_name:
#![allow(unused)] fn main() { {"my_entity_name": { Uuid(00d025c9-eda8-4190-a33a-29998bd77bd3): {a: 123, c: "hello", d: "world", tx_time: DateTime("2014-11-28T12:00:09Z"),}, }} }
- To pre-define the Uuid of the entity to insert use
WITHkeyword followed by an Uuid-v4. This uuid will be used to identify the entity map in the entity tree. As follows:
INSERT {a: 123, c: \"hello\", d: \"world\",}
INTO my_entity_name
WITH <ENTITY-UUID>
UPDATE
Updates the content of an entity map for an entity tree key and an entity id. There are two possible updates:
UPDATE SET
UPDATE SET WQL Reference
SET updates defines the current value of the entity map to the ones being passed, so if your entity map is {a: 123, b: 12.5,} and your set update has the hashmap {a: 432, c: \"hello\",}, the current state of the entity map will be {a: 432, b: 12.5, c: \"hello\", tx_time: DateTime(\"2014-11-28T12:00:09Z\"),}.
Example request:
UPDATE my_entity_name
SET {a: -4, b: 32,}
INTO 00d025c9-eda8-4190-a33a-29998bd77bd3
Example response:
#![allow(unused)] fn main() { ( tx_type: UpdateSet, entity: "my_entity_name", uuid: Some("00d025c9-eda8-4190-a33a-29998bd77bd3"), state: "{\"b\": Integer(32),\"a\": Integer(-4), \"tx_time\": DateTime(\"2014-11-28T12:00:09Z\"),}", message: "Entity my_entity_name with Uuid 00d025c9-eda8-4190-a33a-29998bd77bd3 updated", ) }
UPDATE CONTENT
UPDATE CONTENT WQL Reference
CONTENT updates are a way to add numerical values and concatenate Strings, so if your entity map is {a: 432, c: \"hello\",} and your content update has the hashmap {a: -5, c: \"world\", b: 12.5, tx_time: DateTime(\"2014-11-28T12:00:09Z\"),} the current state of the entity map will be {a: 427, c: \"helloworld\", b: 12.5, tx_time: DateTime(\"2014-11-28T12:00:09Z\"),}.
Example request:
UPDATE my_entity_name
CONTENT {a: -34, b: 7,}
INTO 00d025c9-eda8-4190-a33a-29998bd77bd3
Example response:
#![allow(unused)] fn main() { ( tx_type: UpdateContent, entity: "my_entity_name", uuid: "00d025c9-eda8-4190-a33a-29998bd77bd3", state: "{\"b\": Integer(39),\"a\": Integer(-38), \"tx_time\": DateTime(\"2014-11-28T12:00:09Z\"),}", message: "Entity my_entity_name with Uuid 00d025c9-eda8-4190-a33a-29998bd77bd3 updated", ) }
MATCH UPDATE
Updates entity only if precondition condition is matched. This transaction is significantly slower than other updates.
Example request:
MATCH ALL(a < 0, b >= 3)
UPDATE my_entity_name
SET {a: 123, g: NiL,}
INTO 00d025c9-eda8-4190-a33a-29998bd77bd3
Example response:
#![allow(unused)] fn main() { ( tx_type: UpdateSet, entity: "my_entity_name", uuid: "00d025c9-eda8-4190-a33a-29998bd77bd3", state: "{\"b\": Integer(39),\"a\": Integer(123),\"g\": Nil, \"tx_time\": DateTime(\"2014-11-28T12:00:09Z\"),}", message: "Entity my_entity_name with Uuid 00d025c9-eda8-4190-a33a-29998bd77bd3 updated", ) }
DELETE
Deletes the last entity event for an ID, that is, it deletes the last state of an entity map. The deleted state is preserved in the database but cannot be queried anymore.
If you have, for example, one update on your entity, it will roll back to the INSERT event.
However, if you have only an INSERT event then your state will become an empty hashmap.
Example request:
DELETE 00d025c9-eda8-4190-a33a-29998bd77bd3 FROM my_entity_name
Example response:
#![allow(unused)] fn main() { ( tx_type: Delete, entity: "my_entity", uuid: Some("00d025c9-eda8-4190-a33a-29998bd77bd3"), state: "", message: "Entity my_entity with Uuid 00d025c9-eda8-4190-a33a-29998bd77bd3 deleted", ) }
TODOs:
- Delete entity with ID at transaction-time
EVICT
EVICT ENTITY ID:
Removes all occurrences of an entity map with the given ID.
Example request
EVICT 00d025c9-eda8-4190-a33a-29998bd77bd3 from my_entity_name
Example response:
#![allow(unused)] fn main() { ( tx_type: EvictEntity, entity: "my_entity", uuid: Some("00d025c9-eda8-4190-a33a-29998bd77bd3"), state: "", message: "Entity my_entity with id 6ac9d1bb-2b0c-4631-bc05-682ab4ae8306 evicted", ) }
For now it only deletes the access to the entity history.
EVICT ENTITY:
Evicts all entity ids registries from entity tree and removes entity tree key, which means entity tree does not contain the key for the evicted entity: Similar to SQL DROP TABLE <entity>.
Example request:
EVICT my_entity
Example response:
#![allow(unused)] fn main() { ( tx_type: EvictEntityTree, entity: "my_entity", uuid: None, state: "", message: "Entity my_entity evicted", ) }
TX_TIME
Whenever you make a transaction to WooriDB (INSERT, UPDATES, MATCH) a field named tx_time will be added to the entity map, this field is of type Types::Datetime(chrono::Datetime<Utc>).
Queries
Query is the name of all operations that read the database, like SELECT, CHECK. This is done by sending a POST request to endpoint <ip>:1438/wql/query. An example request would be curl -X POST -H "Content-Type: application/wql" <ip>:1438/wql/query -d 'SELECT * FROM my_entity'. In release mode it is necessary to use header Authorization: Bearer <your session token> for this endpoint.
Reminder A comma is required at the end of every data structure representation. Ex.:
{a: 123, b: 456,},#{a, b, c,},(a, b, c,). No need for;at the end of each expression.
CHECK
Checks for encrypted data, in entity map, validity. It requires an entity tree name after FROM and an entity id as Uuid after ID. This transaction only works with keys that are encrypted and it serves to verify if the passed values are true of false against encrypted data.
Considering entity tree key my_entity_name with entity id 48c7640e-9287-468a-a07c-2fb00da5eaed with entity map {pswd: Hash("my-password"), name: "Julia"}
Example 1:
Example request:
CHECK {pswd: \"my-password\",}
FROM my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaed
Example response:
#![allow(unused)] fn main() { {"pswd": true,} }
Exmample 2:
Example request:
CHECK {pswd: \"my-password\", ssn: "1234",}
FROM my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaed
Example response:
#![allow(unused)] fn main() { ( error_type: "CheckNonEncryptedKeys", error_message: "CHECK can only verify encrypted keys: [\"ssn\"]", ) }
Example 3:
Example request:
CHECK {pswd: \"your-password\",}
FROM my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaed
Example response:
#![allow(unused)] fn main() { {"pswd": false,} }
SELECT
This is the way to query entities from WooriDB. Similar to SQL and SparQL SELECT. There are several different keywords that can be combined with SELECT, a few examples are:
*and#{keys...}can be used in all select modes.
SELECTing all entity map keys FROM entity tree key:
Same as SQL using the token * will defined that all keys in the entity map will be returned. The query SELECT * FROM entity_name selects all entity ids and entity maps found inside entity tree key entity_name. It is equivalent to SQL's Select * From table.
Example request: SELECT * from my_entity_name.
Example response:
This query will return a
BTreeMap<Uuid, HashMap<String, Types>>
#![allow(unused)] fn main() { { 48c7640e-9287-468a-a07c-2fb00da5eaed: {a: 123, b: 43.3, c: "hello", d: "world", tx_time: DateTime("2014-11-28T12:00:09Z"),}, 57c7640e-9287-448a-d07c-3db01da5earg: {a: 456, b: 73.3, c: "hello", d: "brasil", tx_time: DateTime("2014-11-28T12:00:09Z"),}, 54k6640e-5687-445a-d07c-5hg61da5earg: {a: 789, b: 93.3, c: "hello", d: "korea", tx_time: DateTime("2014-11-28T12:00:09Z"),}, } }
SELECTing a set of entity map keys FROM entity tree key:
Differently from SQL, WQL requires the keys to be inside a set like #{a, b, c,}, which will return only the keys a, b, c. It is equivalent to SELECT a, b, c FROM table.
Example request: SELECT #{a, b, c,} FROM my_entity_name.
Example response:
#![allow(unused)] fn main() { { 48c7640e-9287-468a-a07c-2fb00da5eaed: {a: 123, b: 43.3, c: "hello",}, 57c7640e-9287-448a-d07c-3db01da5earg: {a: 456, b: 73.3, c: "hello",}, 54k6640e-5687-445a-d07c-5hg61da5earg: {a: 789, b: 93.3, c: "hello",}, } }
SELECTing one entity map FROM entity tree key:
Select one entity map (by its ID) from entity tree my_entity. By including the key ID after the FROM entity_name it is possible to select a single entity. The content for ID is the entity id's Uuid. It is equivalent to SQL's Select * From table WHERE id = <uuid>.
Example request SELECT * from my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaed.
Example response:
It will return only the entity map contained inside inside entity id
48c7640e-9287-468a-a07c-2fb00da5eaed.{a: 123, b: 43.3, c: \"hello\", d: \"world\", tx_time: DateTime(\"2014-11-28T12:00:09Z\"),}.
SELECTing a set of entities IDs and maps FROM entity tree key:
Select a few entities maps (by their IDs) from entity tree my_entity. Key IN receives a set of Uuids
Example request:
SELECT #{a, b, c,}
FROM my_entity_name
IDS IN #{48c7640e-9287-468a-a07c-2fb00da5eaed, 57c7640e-9287-448a-d07c-3db01da5earg}
Example response:
#![allow(unused)] fn main() { { 48c7640e-9287-468a-a07c-2fb00da5eaed: {a: 123, b: 43.3, c: "hello",}, 57c7640e-9287-448a-d07c-3db01da5earg: {a: 456, b: 73.3, c: "hello",}, } }
SELECTing the last entity map for entity id at DATETIME FROM entity tree key:
Select an entity on a defined past day using the WHEN AT keys. Key WHEN AT is the date to search. Time will be discarded. The ID field can be used before WHEN to define a specific entity id, IDS IN is not supported. Date format should be "2014-11-28T21:00:09+09:00" or "2014-11-28T21:00:09Z".
Example requests:
Select * FROM my_entity ID 0a1b16ed-886c-4c99-97c9-0b977778ec13 WHEN AT 2014-11-28T21:00:09+09:00- OR
Select #{name,id,} FROM my_entity WHEN AT 2014-11-28T21:00:09Z.
Example response:
{a: 34, b: 4.3, c: \"hello\", d: \"Julia\",}
TODOs:
-
Support
IDS IN
SELECTing all entities maps BY ID FROM ENTITY between two DATETIME:
Select all occurrences of an entity id from entity tree entity_name in a time range. The time range must be on the same day as WHEN START 2014-11-28T09:00:09Z END 2014-11-28T21:00:09Z.
- Key
WHENdefines it as a temporal query. - Key
STARTis theDateTime<Utc>to start the range query. - Key
ENDis theDateTime<Utc>to end the range query. - Same day validation occurs. Returning the error message
"START date and END date should be the same date.". IDS INwill not be supported as the query is too extensive.
Example request:
SELECT #{a, b, c, d}
FROM entity_name
ID 0a1b16ed-886c-4c99-97c9-0b977778ec13
WHEN START 2014-11-28T09:00:09Z END 2014-11-28T21:00:09Z
Example response:
#![allow(unused)] fn main() { { "2014-11-28T09:00:09Z": {a: 34, b: 4.3, c: "hello", d: "Julia",}, "2014-11-28T13:00:09Z": {a: 23, b: -3.3, c: "hello", d: "World",}, "2014-11-28T19:00:09Z": {a: 78, b: 67.3, c: "hello", d: "Julia",}, "2014-11-28T21:00:09Z": {a: 123, b: 43.3, c: "hello", d: "Gasp",}, } }
SELECTing entities ids and maps FROM entity tree key WHERE conditions are satisfied
This is probably the most different part in relation to SQL as it is inspired by SparQL and Crux/Datomic datalog. Selects entities ids and maps with positive WHERE clauses. Key WHERE receives all clauses inside a {...} block.
To use select with the where clause you can use the following expression:
SELECT * FROM my_entity WHERE {<clauses>}
Example 1:
Example Request:
SELECT * FROM test_entity
WHERE {
?* test_entity:age ?age,
?* test_entity:race ?race,
(> ?age 25),
(in ?race "Black" "brown"),
}
Example response:
#![allow(unused)] fn main() { { "08824242-8098-4253-a384-987ff8d78c7d": { "race": String("Black"), "origin": String("Africa"), "age": Integer(34), "name": String("Diego F"), "tx_time": DateTime("2014-11-28T12:00:09Z"), }, "ea3228d0-0164-453b-bae2-f726c4a9b979": { "origin": String("Bukhara"), "race": String("brown"), "age": Integer(33), "name": String("julia naomi"), "tx_time": DateTime("2014-11-28T12:00:09Z"), }, } }
Example 2:
Example request:
SELECT * FROM test_entity
WHERE {
?* test_entity:age ?age,
(between ?age 18 27),
}
Example response:
#![allow(unused)] fn main() { { "475fab3b-b023-4f6c-a18e-7e8b25b84a28": { "age": Integer(25), "name": String("Otavio"), "origin": String("Brasil"), "race": String("Multiracial"), "tx_time": DateTime("2014-11-28T12:00:09Z"), }, } }
TODOs:
- Support temporality for where clause
Entity History
Entity history is one of the main features of WooriDB. You can run it by using the features flag. To execute WooriDB with history feature enable execute:
make history, or;cargo run --manifest-path woori-db/Cargo.toml --release --features history
It receives an entity_key name and an entity_id which will return the whole history of (DateTime<Utc>, entity_map) for the entity_id in the entity tree for key entity_key. This is done by sending a POST request to endpoint <ip>:1438/entity-history. An example request would be curl -X POST -H "Content-Type: application/wql" <ip>:1438/entity-history -d '(entity_key: "entity_tree_key", entity_id: "<some-Uuid>",)'. In release mode it is necessary to use header Authorization: Bearer <your session token> for this endpoint.
Example request:
(entity_key: "entity_tree_key", entity_id: "dc3069e7-2a22-4fbc-ae05-f78a807239c0",)
Example response:
#![allow(unused)] fn main() { { "2021-03-02T05:00:19.813514240Z": { "a": Integer(123), "b": Float(12.3), "tx_time": DateTime("2021-03-02T05:00:19.813514240Z"), }, "2021-03-02T05:00:19.816357939Z": { "b": Float(12.3), "a": Integer(123), "tx_time": DateTime("2021-03-02T05:00:19.816357939Z"), }, "2021-03-02T05:00:19.817189987Z": { "b": Float(12.3), "c": Boolean(true), "a": Integer(34), "tx_time": DateTime("2021-03-02T05:00:19.817189987Z"), }, "2021-03-02T05:00:19.818031113Z": { "b": Float(12.3), "a": Integer(321), "c": Char('h'), "tx_time": DateTime("2021-03-02T05:00:19.818031113Z"), },} }
- This response is considering the following events:
INSERT {a: 123, b: 12.3,} INTO entity_tree_key.UPDATE entity_tree_key SET {{a: 12, c: Nil,}} INTO dc3069e7-2a22-4fbc-ae05-f78a807239c0. Note that this event does not appear due to DELETE.Delete dc3069e7-2a22-4fbc-ae05-f78a807239c0 FROM entity_tree_key. Entity map state is the same as the previous state, therefore "2021-03-02T05:00:19.813514240Z" and "2021-03-02T05:00:19.816357939Z" have equal content.UPDATE entity_tree_key SET {{a: 34, c: true,}} INTO dc3069e7-2a22-4fbc-ae05-f78a807239c0.UPDATE entity_tree_key SET {{a: 321, c: 'h',}} INTO dc3069e7-2a22-4fbc-ae05-f78a807239c0.
Entity history with time ranges
There are two extra parameters that can be used with entity-history, they are start_datetime and end_datetime. Both parameters are optional and if they are present they will define the time limits of the query. start_datetime is the beginning of the time range query while end_datetime is the ending of the time range query. If we used start_datetime and end_datetime for the previous example as curl -X POST -H "Content-Type: application/wql" <ip>:1438/entity-history -d '(entity_key: "entity_tree_key", entity_id: "<some-Uuid>", start_datetime: Some("2021-03-02T05:00:19.816357937Z"), end_datetime: Some("2021-03-02T05:00:19.817189988Z"),)' we would have the following result:
Example request:
(
entity_key: "entity_tree_key",
entity_id: "dc3069e7-2a22-4fbc-ae05-f78a807239c0",
start_datetime: Some("2021-03-02T05:00:19.816357937Z"),
end_datetime: Some("2021-03-02T05:00:19.817189988Z"),
)
Example response:
#![allow(unused)] fn main() { { "2021-03-02T05:00:19.816357939Z": { "b": Float(12.3), "a": Integer(123), "tx_time": DateTime("2021-03-02T05:00:19.816357939Z"), }, "2021-03-02T05:00:19.817189987Z": { "b": Float(12.3), "c": Boolean(true), "a": Integer(34), "tx_time": DateTime("2021-03-02T05:00:19.817189987Z"), },} }
Relation Algebra Functions
WooriDB has some support to relation algebra functions as well as auxiliary functions to relation algebra. They are:
Functions GROUP BY, ORDER BY, DEDUP LIMIT, OFFSET, COUNT are only supported by the following select queries:
SELECT */#{...} FROM tree_key_nameSELECT */#{...} FROM tree_key_name WHERE {...}SELECT */#{...} FROM tree_key_name IDS IN #{...}
Functions UNION,INTERSECT,DIFFERENCE are only supported by the following select queries:
SELECT */#{...} FROM tree_key_name ID some-uuidSELECT */#{...} FROM tree_key_name ID some-uuid WHEN AT some-date
GROUP BY
This groups the responses of the select query in the following type HashMap<String, BTreeMap<Uuid, HashMap<String, Types>>> (for group by associated with order by the type is HashMap<String, Vec<(Uuid, HashMap<String, Types>)>>). So the query SELECT * FROM key GROUP BY c for the following 6 entities:
#![allow(unused)] fn main() { {a: 123, b: 12.3,} {a: 235, b: 12.3, c: 'c',} {a: 235, b: 12.3, c: 'd',} {a: 25, b: 12.3, c: 'c',} {a: 475, b: 12.3, c: 'd',} {a: 295, b: 12.3, c: 'r',} }
Will produce the response:
#![allow(unused)] fn main() { { "Char(\'r\')": {<Uuid6>: {a: 295, b: 12.3, c: 'r',},} , "Char(\'c\')": {<Uuid2>: {a: 235, b: 12.3, c: 'c',}, <Uuid4>: {a: 25, b: 12.3, c: 'c',},}, "Char(\'d\')": {<Uuid3>: {a: 235, b: 12.3, c: 'd',}, <Uuid5>: {a: 475, b: 12.3, c: 'd',},}, "Nil": {<Uuid1>: {a: 123, b: 12.3,},}, } }
- Note that the Hash of the type is a
Stringcontaining awql::Types.
ORDER BY
This functions orders the response of the query by the value of a key. The key-value can be ordered by :asc or :desc. So the query SELECT * FROM key ORDER BY a :asc will return a Vec<(Uuid, HashMap<String, Types>)> for the following 6 entities:
#![allow(unused)] fn main() { {a: 123, b: 12.3,} {a: 235, b: 12.3, c: 'c',} {a: 235, b: 12.3, c: 'd',} {a: 25, b: 12.3, c: 'c',} {a: 475, b: 12.3, c: 'd',} {a: 295, b: 12.3, c: 'r',} }
Will produce the response:
#![allow(unused)] fn main() { [ (<Uuid4>, {a: 25, b: 12.3, c: 'c',}), (<Uuid1>, {a: 123, b: 12.3,}), (<Uuid2>, {a: 235, b: 12.3, c: 'c',}), (<Uuid3>, {a: 235, b: 12.3, c: 'd',}), (<Uuid6>, {a: 295, b: 12.3, c: 'r',}), (<Uuid5>, {a: 475, b: 12.3, c: 'd',}), ] }
-
Order By with multiple arguments. The problem here is how to have multiple
.and_then(...)alter thepartial_cmp.
DEDUP
This functios is capable of removing duplicates key-values in responses. By using SELECT * FROM key DEDUP a for the following 6 entities:
#![allow(unused)] fn main() { {a: 123, b: 12.3,} {a: 235, b: 12.3, c: 'c',} {a: 235, b: 12.3, c: 'd',} {a: 25, b: 12.3, c: 'c',} {a: 475, b: 12.3, c: 'd',} {a: 295, b: 12.3, c: 'r',} }
We would have as a result something like:
#![allow(unused)] fn main() { { <Uuid1>: {a: 123, b: 12.3,}, <Uuid2>: {a: 235, b: 12.3, c: 'c',}, <Uuid3>: {a: 25, b: 12.3, c: 'c',}, <Uuid4>: {a: 475, b: 12.3, c: 'd',}, <Uuid5>: {a: 295, b: 12.3, c: 'r',}, } }
Also it is possible to eliminate Nil and Types::Nil values for a DEDUP key. This is done by calling the function NIL() (It needs to be UPPERCASE) with the key used for the DEDUP. So for the previous data the response for the query SELECT * FROM key DEDUP NIL(c) would be:
#![allow(unused)] fn main() { { <Uuid2>: {a: 235, b: 12.3, c: 'c',}, <Uuid4>: {a: 475, b: 12.3, c: 'd',}, <Uuid5>: {a: 295, b: 12.3, c: 'r',}, } }
LIMIT and OFFSET
The functions LIMIT and OFFSET expect a positive integer as argument, this means that if you define LIMIT 10 and OFFSET 5 you will skip the first 5 elements from the tree and take only the next 10 elements. LIMIT and OFFSET are also appended to the end of the select query such that SELECT * FROM key LIMIT 100 OFFSET 300.
COUNT
This function is appended to the end of a select query and it will return the count for entities found by that select. So a query like SELECT * FROM key WHERE {...} COUNT will return the responses for select where as well as the count of entities found in that select. The aswer will be in the following structure:
#![allow(unused)] fn main() { ( response: { "map containing the response for the query" }, count: usize, ) }
UNION
This unites two entities into one entity. There are two strategies for this relation the first one is UNION KEY which will unify 2 entities adding to the first one the missing values from the second, then there is UNION KEY-VALUE that will unite the keys and values from the second and if the value is the different for each key a duplicated sign will be added. The following examples will help you understand considering the following entities:
#![allow(unused)] fn main() { { "ent1": {<UUID1>: {a: 123, b: 234, c: true,}} "ent2": {<UUID2>: {a: 123, b: 432, d: false,}} } }
KEY
UNION KEY Select * FROM ent1 ID uuid1 | Select * FROM ent2 ID uuid2. Note the | as query separator.
The entity to be returned will be:
#![allow(unused)] fn main() { {"a": 123, "b": 234, "c": true, "d": false} }
KEY-VALUE
UNION KEY-VALUE Select * FROM ent1 ID uuid1 | Select * FROM ent2 ID uuid2. Note the | as query separator.
The entity to be returned will be:
#![allow(unused)] fn main() { {"a": 123, "b": 234, "b:duplicated": 432, "c": true, "d": false} }
INTERSECT
This intersects two entities into one entity. There are two strategies for this relation the first one is INTERSECT KEY which will return only the key value pairs from the first entity that have a corresponding key in the second entity, then there is INTERSECT KEY-VALUE which will return only the key value pairs from the first entity that have a corresponding key value pair in the second entity. The following examples will help you understand considering the following entities:
#![allow(unused)] fn main() { { "ent1": {<UUID1>: {a: 123, b: 234, c: true,}} "ent2": {<UUID2>: {a: 123, b: 432, d: false,}} } }
KEY
INTERSECT KEY Select * FROM ent1 ID uuid1 | Select * FROM ent2 ID uuid2. Note the | as query separator.
The entity to be returned will be:
#![allow(unused)] fn main() { {"a": 123, "b": 234} }
KEY-VALUE
INTERSECT KEY-VALUE Select * FROM ent1 ID uuid1 | Select * FROM ent2 ID uuid2. Note the | as query separator.
The entity to be returned will be:
#![allow(unused)] fn main() { {"a": 123} }
DIFFERENCE
This intersects two entities into one entity. There are two strategies for this relation the first one is DIFFERENCE KEY which will return only the key value pairs from the first entity that do not have a corresponding key in the second entity, then there is DIFFERENCE KEY-VALUE which will return only the key value pairs from the first entity that do not have a corresponding key value pair in the second entity. The following examples will help you understand considering the following entities:
#![allow(unused)] fn main() { { "ent1": {<UUID1>: {a: 123, b: 234, c: true,}} "ent2": {<UUID2>: {a: 123, b: 432, d: false,}} } }
KEY
DIFFERENCE KEY Select * FROM ent1 ID uuid1 | Select * FROM ent2 ID uuid2. Note the | as query separator.
The entity to be returned will be:
#![allow(unused)] fn main() { {"c": true,} }
KEY-VALUE
DIFFERENCE KEY-VALUE Select * FROM ent1 ID uuid1 | Select * FROM ent2 ID uuid2. Note the | as query separator.
The entity to be returned will be:
#![allow(unused)] fn main() { {"c": true, "b": 234} }
JOIN
Join operation is similar to UNION. However, it does this by comparing keys equallity in two different entities, so if we select all elements in entity_a and all elements in entity_b and we join them in key a for entity_a and key b for entity_b whenever entity_a:a == entity_b:b a new entity will be created and appended to the resulting vector. Also all duplciated keys from entity_b will be appended by :entity_b, so a duplicated key dup_key will be dup_key:entity_b.
For the query JOIN (entity_AA:c, entity_BB:o) Select * FROM entity_AA order by c :asc | Select #{{g, f, o, b,}} FROM entity_BB we are checking equallity on entity_AA:c == entity_BB:o and the two queries two to be joined are Select * FROM entity_AA order by c :asc and Select #{{g, f, o, b,}} FROM entity_BB joined by a |.
#![allow(unused)] fn main() { { "entity_AA": { <UUID1>: {a: 123, b: 12.3,}, <UUID3>: {a: 235, b: 17.3, c: 'c',}, <UUID5>: {a: 476, b: 312.3, c: 'd',}, <UUID7>: {a: 857, c: 'd',},} "entity_BB": { <UUID2>: {a: 66, b: 66.3, c: 'r',}, <UUID4>: {g: 25, f: 12.3, a: 'c',}, <UUID6>: {g: 475, b: 12.3, f: 'h', o: 'd',}, <UUID8>: {g: 756, b: 142.3, f: 'h', o: 'c',}, <UUI10>: {g: 76, b: 12.3, f: 't', o: 'd',}, <UUID12>: {t: 295, b: 12.3, o: 'r',}, <UUID14>: {t: 295, f: 12.3, o: Nil,}, } } }
The response for this join will be:
Notes
tx_timeis thetx_timeofentity_AA.- entities that don't have the field to be matched will be matched with other entities that don't have the field or it is
nil.
#![allow(unused)] fn main() { [ { "tx_time": DateTime( 2021-04-01T18:04:30.029549132Z, ), "a": Integer( 123, ), "b": Float( 12.3, ), "g": Integer( 25, ), "f": Float( 12.3, ), }, { "tx_time": DateTime( 2021-04-01T18:04:30.029549132Z, ), "a": Integer( 123, ), "b": Float( 12.3, ), "b:entity_BB": Float( 66.3, ), }, { "tx_time": DateTime( 2021-04-01T18:04:30.029549132Z, ), "a": Integer( 123, ), "b": Float( 12.3, ), "f": Float( 12.3, ), }, { "g": Integer( 756, ), "f": Char( 'h', ), "b": Float( 17.3, ), "tx_time": DateTime( 2021-04-01T18:04:30.030481424Z, ), "a": Integer( 235, ), "b:entity_BB": Float( 142.3, ), "c": Char( 'c', ), }, { "b:entity_BB": Float( 12.3, ), "tx_time": DateTime( 2021-04-01T18:04:30.031485453Z, ), "f": Char( 't', ), "g": Integer( 76, ), "a": Integer( 476, ), "b": Float( 312.3, ), "c": Char( 'd', ), }, { "f": Char( 'h', ), "tx_time": DateTime( 2021-04-01T18:04:30.031485453Z, ), "b:entity_BB": Float( 12.3, ), "g": Integer( 475, ), "a": Integer( 476, ), "b": Float( 312.3, ), "c": Char( 'd', ), }, { "b": Float( 12.3, ), "c": Char( 'd', ), "tx_time": DateTime( 2021-04-01T18:04:30.032730665Z, ), "g": Integer( 76, ), "a": Integer( 857, ), "f": Char( 't', ), }, { "b": Float( 12.3, ), "c": Char( 'd', ), "tx_time": DateTime( 2021-04-01T18:04:30.032730665Z, ), "g": Integer( 475, ), "a": Integer( 857, ), "f": Char( 'h', ), }, ] }
Error Messages
Woori DB has a variety of Error messages, from WQL when parsing the transaction or the query to some internal database errors. Here are listed expected messages:
WooriDB Errors:
Example errors as response:
#![allow(unused)] fn main() { ( error_type: "CheckNonEncryptedKeys", error_message: "CHECK can only verify encrypted keys: [\"ssn\"]", ) }
Error types:
Io(io::Error)- Failed to read or write file.QueryFormat(String)- WQL error.EntityAlreadyCreated(<entity_name>)- Entityentity_namealready created in the database.EntityNotCreated(<entity_name>)- tx or query at entity tree does not contain keyentity_name.EntityNotCreatedWithUniqueness(<entity_name>)- this error only occurs if a failed write to the bank happened. Migration to fix data inconsistency may be needed.Serialization(ron::Error)- Ron serialization error in wql context.UuidNotCreatedForEntity(<entity_name>, Uuid)- If you try toUPDATE/DELETE/EVICT/SELECTan Uuid thatentity_namedoes not contain.FailedToParseState- Failed to read log file state/entity map.FailedToParseRegistry- Failed to read log file registry informationUnknownCondition-MATCH UPDATEhas an unknown condition (==,>=,<, etc).FailedMatchCondition-MATCH UPDATEinternal service error while processing conditions.DuplicatedUnique(<entity_name>, <entity_map_key>, Types)- This means thatTypesis already present atentity_map_keyfor entity treeentity_name.SelectBadRequest- Select operation done at/wql/tx. Select operations are done at/wql/query.NonSelectQuery- Tx operation done at/wql/query. Tx operations are done at/wql/tx.ActixMailbox(MailboxError)- Internal server error meaning that some actor mailbox crashed.LockData- Failed to get a lock on DataRon(ron::Error)- Ron serialization error that occurred in user creation.InvalidUuid(uuid::Error)- Uuid could not be parsed.UpdateContentEncryptKeys(Vec<keys>)-UPDATE CONTENTcannot update encryptedkeys.CheckNonEncryptedKeys(Vec<keys>)- CannotCHECKnon encryptedkeys.DateTimeParse(chrono::ParseError)- failed to parse inputDateTime<UTC>.FailedToParseDate- failed to parse log file saved date.AdminNotConfigured- Admin is not configured at release mode, please check auth section for more info.AuthBadRequest- Authentication & Authorization error.FailedToCreateUser- Failed to create new user.Unknown- Unknown error.KeyTxTimeNotAllowed- the keytx_timeis not allowed in entities map for inserts and updates.
WQL Parsing
-
Query symbol error:- "Symbol
{symbol_name}not implemented". Symbol name are the start of the query, likeSELECT, CHECK, CREATE, ISNERT.
- "Symbol
-
Keyword error:CREATE: Keyword ENTITY is required for CREATE"- "Correct wording is ENCRYPT" for
CREATE ENTITY ENCRYPT - "Correct wording is UNIQUES" for
CREATE ENTITY UNIQUES.
- "Correct wording is ENCRYPT" for
UPDATE: "UPDATE type is required after entity. Keywords are SET or CONTENT"- "Keyword INTO is required for UPDATE"
- "Keyword INTO is required for MATCH UPDATE"
- "UPDATE keyword is required for MATCH UPDATE"
- "MATCH UPDATE type is required after entity. Keyword is SET". Use
SETas update type inMATCH.
SELECT/CHECK: "Keyword FROM is required for CHECK"- "Keyword FROM is required for SELECT"
- "WHEN not allowed after IDS IN"
- "Keyword AT is required after WHEN"
- "Keyword IN is required after IDS to define a set of uuids"
- "Keyword ID/IDS is required to set an uuid in SELECT".
EVICT/DELETE/INSERT: "Keyword FROM is required for DELETE"- "Keyword INTO is required for INSERT"
- "Keyword FROM is required to EVICT an UUID".
-
Argument format error:- "Arguments set should start with
#{and end with}" - "Entity map should start with
{and end with}" occurs mostly withINSERT and UPDATE. - "Field ID must be a UUID v4"
SELECT: "Encrypted arguments cannot be set to UNIQUE"- "SELECT expression should be followed by
*for ALL keys or#{key_names...}for some keys" and "SELECT arguments set should start with#{and end with}" - "Uuids in
IDS INare reuired to be inside a#{and}" - "START date and END date should be the same date."
- "WHERE clauses must be contained inside
{...}"
- "SELECT expression should be followed by
- "Arguments set should start with
-
Required content:- "MATCH requires ALL or ANY symbols". It is necessary to include
ANYorALLconditions afterMATCHkeyword. - "Entity UUID is required for DELETE"
Entity name:- "Entity name is required after FROM"
- "Entity name is required for SELECT"
- "Entity name is required after INTO"
- "Entity name is required for UPDATE"
- "Entity name is required for EVICT"
- "Entity name is required for MATCH UPDATE"
- "MATCH requires ALL or ANY symbols". It is necessary to include
-
Parse error:- "Couldn't create uuid from {some-text}. Error: {Uuid::Error}"
- "Couldn't parse UPDATE query".
SETorCONTENTwere not found afterUPDATE - "Couldn't parse MATCH UPDATE query"
- "Entity name cannot contain
-". Entity name should contain only alphanumeric and_chars. - "Key must be an alphanumeric value" or contain
_. - "Hash cannot be hashed" and "Nil cannot be hashed".
Types::HashandTypes::Nilcannot be hashed. - "Not able to parse match argument". Match condition has wrong argument type.
- "Unidentified Match Condition". Could not identify match condition.
- "Entity HashMap could not be created"
- "Value Type could not be created from {some value}". Could not create
Typesfromsome value. - "WHERE clause cannot be empty"
using Json
WoorDB is able to support Json requests and responses by using the flag features. To execute WooriDB with json feature enable execute:
Make json, or;cargo run --manifest-path woori-db/Cargo.toml --release --features json
- Remember that Json doesn't have trailing commas while ron has them.
Example request:
For /auth/createUser.
{
"admin_id": "your_admin",
"admin_password": "your_password",
"user_info": {
"user_password": "my_password",
"role": ["User"]
}
}
Example response:
For a SELECT * FROM entity.
{
"38d52c95-b6f6-403a-a0b2-447b8fa15784": {
"a": Integer(123),
"tx_time": DateTime("2021-03-24T23:56:45.179008791Z"),
},
}