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 release
in project root for performance optimization.Release mode size
:make run
in project root for size optimization.Debug mode
:make debug
in 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 debug
for 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 run
for 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 release
for performance optimization.- All
-e/--env
can be replaced by a--env-file path/to/your/.env
. Your.env
file 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
RON
format. BLOB
will 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
debug
mode 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_CONNECTIONS
is the maximum number of connections supported simultaneously.CLIENT_SHUTDOWN
is the time to drop a client's connection, use 0 to disable it.- To run the project in
release
mode 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
UNIQUE
for an entity tree. - Hashed values are filtered out of
SELECT
and can only be checked withCHECK
keyword. Ron
schemas 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.
-
S3
as a backend is to be developed. -
DynamoDB
as a backend is to be developed.
-
- Able to handle very large numbers when using the
P
suffix.- 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
CREATE
entity 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.
INSERT
entity map into entity tree.UPDATE
s withSET
orCONTENT
entity 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 UPDATE
updates entity map content with new content if match condition is satisfied.DELETE
s 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
SELECT
the only way to retrieve an entity's content.WHERE
clause is available.WHEN
clause is available
CHECK
the 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_entity
this 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_entity
will only allow unique values for the entities keysname
andssn
in its maps.CREATE ENTITY my_entity ENCRYPTS #{pswd,}
the entity tree key namedmy_entity
will 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_key
this will insert the entity map{a: 123, b: "hello julia",}
(keya
containing asType::Integer(123)
and keyb
containing 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-2fb00da5eaed
this 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-2fb00da5eaed
this will add-4
to entity map keya
and add32
to entity map keyb
in 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-fc4a061ce536
if all conditions defined insideALL
are satisfied the set update will happen.ALL
is an logicaland
between all conditions, meaning that all of them must be true.ANY
is an logicalor
between all conditions, meaning that at least one of them must be true.- NULL KEYS,
ALL
returns an error if a null key is present andANY
just ignores null keys. - Possible conditions are:
==
means equals, so ifa == 100
, this means that the entity map keya
must equal to100
.!=
means not equal, so ifa != 100
, this means that the entity map keya
must not equal to100
.>=
means greater or equal, so ifa >= 100
, this means that the entity map keya
must br greater or equal to100
.<=
means lesser or equal, so ifa <= 100
, this means that the entity map keya
must be lesser or equal to100
.>
means greater, so ifa > 100
, this means that the entity map keya
must be greater than100
.<
means lesser, so ifa < 100
, this means that the entity map keya
must 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_name
this will delete the last state of entity id48c7640e-9287-468a-a07c-2fb00da5eaed
in entity tree keymy_entity_name
from 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_name
removes all occurrences of the entity id48c7640e-9287-468a-a07c-2fb00da5eaed
from the entity tree keymy_entity_name
, they cannot be queried anymore.EVICT my_entity
removes the keymy_entity
from 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-2fb00da5eaed
this will check if keyspsdw
andssn
from entity id48c7640e-9287-468a-a07c-2fb00da5eaed
in entity tree keymy_entity_name
have the values"my-password"
for pswd and3948453
for ssn.
SELECT
This is the way to query entities from WooriDB. Similar to SQL and SparQL SELECT
.
Possible SELECT
combinations:
SELECT * FROM my_entity_name
selects all entity ids and entity maps for the entity tree keymy_entity_name
with all the possible entities map keys.SELECT #{name, last_name, age,} FROM my_entity_name
selects all entity ids and entity maps for the entity tree keymy_entity_name
with only the keysname, last_name, age
for the entities map.SELECT * FROM my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaed
selects the entity map containing the entity id48c7640e-9287-468a-a07c-2fb00da5eaed
from the entity tree keymy_entity_name
with all the possible entities map keys.SELECT #{name, last_name, age,} FROM my_entity_name ID 48c7640e-9287-468a-a07c-2fb00da5eaed
selects the entity map containing the entity id48c7640e-9287-468a-a07c-2fb00da5eaed
from the entity tree keymy_entity_name
with only the keysname, last_name, age
for 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:00
this will select the last entity map state for the entity id0a1b16ed-886c-4c99-97c9-0b977778ec13
in entity tree keymy_entity
at 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:09Z
this will select the all entity map states for the entity id0a1b16ed-886c-4c99-97c9-0b977778ec13
in entity tree keymy_entity
in the time range starting at2014-11-28T09:00:09Z
and 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_entity
that satisfy the where clause.?* my_entity:a ?a
and?* my_entity:c ?c
define that the entity keysa
andc
from entity tree keymy_entity
will receive the attributed value?a
and?c
respectively.(== ?a 123)
selects all entities which entity map keya
is equal to123
.(or (>= ?c 4300.0), (< ?c 6.9),)
selects all entities which entity map keyc
is greater than or equal to4300.0
or is smaller than6.9
.
WHERE Clause
Possible functions for the where clause:
in
:(in ?k1 123 34543 7645 435)
,?k1
must be present in the set containing123 34543 7645 435
. NOTE: for now, please don't use,
.between
:(between ?k1 0 435)
,?k1
must be between starting value0
and ending value435
. If you set more than 2 arguments it will return aClauseError
.like
:(like ?k2 "%naomi%")
, like is comparing?k2
with 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?k1
is greater than or equal to0
.or
: All arguments inside theor
function call will be evaluated totrue
if 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
P
at the end.
- Note: This type cannot be updated with
UPDATE CONTENT
.- Ex.:
INSERT {a: 98347883122138743294728345738925783257325789353593473247832493483478935673.9347324783249348347893567393473247832493483478935673P, } INTO my_entity
.
BLOB
will 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 booleantrue
offalse
, -
Vector(Vec<Types>)
contains a vector ofTypes
, -
Map(HashMap<String, Types>)
contains a HashMap of keyString
and valueTypes
, -
Hash(String)
contains a Hash generated byENCRYPTS
, -
Precise(String)
contains a very large integer or a very large float, -
Nil
contains anull/nil
value, -
DateTime
contains 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_COST
is the hashing cost for password in the authentication system.ADMIN
is the admin's user id.ADMIN_PASSWORD
is 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
ADMIN
is 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/tx
and/auth/putUserSession
.WRITE
- only works on/wql/tx
and/auth/putUserSession
.READ
- only works on/wql/query
and/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
ADMIN
is 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, theinsert
andupload
requests 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
WITH
keyword 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
WHEN
defines it as a temporal query. - Key
START
is theDateTime<Utc>
to start the range query. - Key
END
is 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 IN
will 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_name
SELECT */#{...} 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-uuid
SELECT */#{...} 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
String
containing 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_time
is thetx_time
ofentity_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_name
already 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/SELECT
an Uuid thatentity_name
does not contain.FailedToParseState
- Failed to read log file state/entity map.FailedToParseRegistry
- Failed to read log file registry informationUnknownCondition
-MATCH UPDATE
has an unknown condition (==
,>=
,<
, etc).FailedMatchCondition
-MATCH UPDATE
internal service error while processing conditions.DuplicatedUnique(<entity_name>, <entity_map_key>, Types)
- This means thatTypes
is already present atentity_map_key
for 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 CONTENT
cannot update encryptedkeys
.CheckNonEncryptedKeys(Vec<keys>)
- CannotCHECK
non 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_time
is 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
SET
as 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 IN
are 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
ANY
orALL
conditions afterMATCH
keyword. - "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".
SET
orCONTENT
were 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::Hash
andTypes::Nil
cannot 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
Types
fromsome 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"),
},
}