IndexedDB)complete
VALUE
clauses can be used to ensure a default value is always specified if no data is entered.$before
and $after
value of the record, enabling advanced custom logic with each trigger.UNIQUE
values in a table. Table indexes can be specified for a column, multiple columns, and have support for all nested fields including arrays and objects.
-- Specify a field on the user table DEFINE FIELD email ON TABLE user TYPE string ASSERT string::is::email($value); -- Add a unique index on the email field to prevent duplicate values DEFINE INDEX email ON TABLE user COLUMNS email UNIQUE; -- Create a new event whenever a user changes their email address DEFINE EVENT email ON TABLE user WHEN $before.email != $after.email THEN ( CREATE event SET user = $this, time = time::now(), value = $after.email, action = 'email_changed' );
ASSERT
clause which acts as a constraint on the data. This clause enables advanced SurrealQL statements which can ensure that the $value
entered is within certain parameters. Each clause is also able to see the $before
and $after
value of the record, enabling advanced custom logic with each trigger.
-- Specify a field on the user table DEFINE FIELD countrycode ON user TYPE string -- Ensure country code is ISO-3166 ASSERT $value = /[A-Z]{3}/ -- Set a default value if empty VALUE $value OR $before OR 'GBR' ;
-- Define a text analyzer DEFINE ANALYZER en TOKENIZERS camel,class FILTERS snowball(English); -- Define a search index for a field on the book table DEFINE INDEX search_title ON book COLUMNS title SEARCH ANALYZER en BM25 HIGHLIGHTS; -- Select all books who match given keywords SELECT search::score(1) AS score, search::highlight('<b>', '</b>', 1) AS title FROM book WHERE title @1@ 'rust web' ORDER BY score DESC;
euclidean
distance metrics. Vector embeddings can be used for similarity matching, and for advanced data analysis.
-- Add vector embedding data to record content CREATE article:1 SET embedding = [1, 2, 3, 4]; CREATE article:2 SET embedding = [4, 5, 6, 7]; CREATE article:3 SET embedding = [8, 9, 10, 11]; -- Define a vector embedding index for a field on the article table DEFINE INDEX mt_obj ON vec FIELDS embedding MTREE DIMENSION 4 DIST EUCLIDEAN;
-- Drop all writes to the reading table. We don't need every reading. DEFINE TABLE reading DROP; -- Define a table as a view which aggregates data from the reading table DEFINE TABLE temperatures_by_month AS SELECT count() AS total, time::month(recorded_at) AS month, math::mean(temperature) AS average_temp FROM reading GROUP BY city ; -- Add a new temperature reading with some basic attributes CREATE reading SET temperature = 27.4, recorded_at = time::now(), city = 'London', location = (-0.118092, 51.509865) ;
-- Subscribe to all matching document changes LIVE SELECT * FROM document WHERE account = $auth.account OR public = true ; -- Subscribe to all changes to a single record LIVE SELECT * FROM post:c569rth77ad48tc6s3ig; -- Stop receiving change notifications KILL "1986cc4e-340a-467d-9290-de81583267a2";
-- Define a global parameter which will be accessible to all queries. DEFINE PARAM $STRIPE VALUE "https://api.stripe.com/payments/new"; -- Use the defined global parameter in all queries on the database. DEFINE EVENT payment ON TABLE order WHEN $event = 'CREATE' THEN http::post($STRIPE, $value);
decimal
based numbers, but can be stored as int
or float
values for 64 bit integer or 64 bit floating point precision.NONE
, or NULL
. A field which is NONE
does not have any data stored, while NULL
values are values which are entered but empty.Point
, Line
, Polygon
, MultiPoint
, MultiLine
, MultiPolygon
, and Collection
values. SurrealQL automatically detects GeoJSON objects converting them into a single data type.
UPDATE city:london SET centre = (-0.118092, 51.509865), boundary = { type: "Polygon", coordinates: [[ [-0.38314819, 51.37692386], [0.1785278, 51.37692386], [0.1785278, 51.61460570], [-0.38314819, 51.61460570], [-0.38314819, 51.37692386] ]] } ;
SELECT
clause.
UPDATE product SET name = "SurrealDB", launch_at = <datetime> "2021-11-01", countdown = <future> { launch_at - time::now() } ;
bool
, int
, float
, string
, number
, decimal
, datetime
, and duration
casts.
UPDATE person SET waist = <int> "34", height = <float> 201, score = <decimal> 0.3 + 0.3 + 0.3 + 0.1 ;
// Ensure that a record field must be a number. DEFINE FIELD age ON person TYPE number; // Allow the field to be optional or a number. DEFINE FIELD age ON person TYPE option<number>; // Ensure that a record link is specified and of a specific type. DEFINE FIELD author ON book TYPE record<person>; // Allow a field to be optional and of a selection of types. DEFINE FIELD pet ON user TYPE option<record<cat | dog>>; // Allow a field to be one of multiple types. DEFINE FIELD rating ON film TYPE float | decimal; // Ensure that a field is an a array of unique values of a certain length. DEFINE FIELD tags ON person TYPE set<string, 5>;
SELECT
, CREATE
, UPDATE
, and DELETE
methods. These enable selecting or modifying individual records, or whole tables. Each statement supports multiple different tables or record types at once.
-- Create a new article record with a specific id CREATE article:surreal SET name = "SurrealDB: The next generation database"; -- Update the article record, and add a new field UPDATE article:surreal SET time.created = time::now(); -- Select all matching articles SELECT * FROM article, post WHERE name CONTAINS 'SurrealDB'; -- Delete the article DELETE article:surreal;
RELATE
statement adds graph edges between records in SurrealDB. It follows the convention of vertex -> edge -> vertex
or noun -> verb -> noun
, enabling the addition of metadata to the edge record.
-- Add a graph edge between user:tobie and article:surreal RELATE user:tobie->write->article:surreal SET time.written = time::now() ; -- Add a graph edge between specific users and developers LET $from = (SELECT users FROM company:surrealdb); LET $devs = (SELECT * FROM user WHERE tags CONTAINS 'developer'); RELATE $from->like->$devs UNIQUE SET time.connected = time::now() ;
INSERT
statement resembles the traditional SQL statement, enabling users to get started quickly. It supports the creation of records using a VALUES
clause, or by specifying the record data as an object.
INSERT INTO company { name: 'SurrealDB', founded: "2021-09-10", founders: [person:tobie, person:jaime], tags: ['big data', 'database'] }; INSERT IGNORE INTO company (name, founded) VALUES ('SurrealDB', '2021-09-10') ON DUPLICATE KEY UPDATE tags += 'developer tools' ;
FOR
statements enable simplified iteration over data, or for advanced logic when dealing with nested arrays or recursive functions, within code blocks or custom functions.
FOR $person IN (SELECT VALUE id FROM person) { CREATE gift CONTENT { recipient: $person, type: "ticket", date: time::now(), event: "SurrealDB World", }; };
THROW
statement statement can be used to return custom error types, which allow for building advanced programming and business logic right within the database and authentication engine.
FOR $user IN $input { IF $user.age < 18 { THROW "Only adults should be inserted"; } IF $user.country != 'USA' { THROW $user.country + " is not a valid country"; } };
.
, or array notation []
.
-- Use mathematical operators to calculate value SELECT * FROM temperature WHERE (celsius * 1.8) + 32 > 86.0; -- Use geospatial operator to detect polygon containment SELECT * FROM restaurant WHERE location INSIDE { type: "Polygon", coordinates: [[ [-0.38314819, 51.37692386], [0.1785278, 51.37692386], [0.1785278, 51.61460570], [-0.38314819, 51.61460570], [-0.38314819, 51.37692386] ]] }; -- Select all people whose tags contain "tag1" OR "tag2" SELECT * FROM person WHERE tags CONTAINSANY ["tag1", "tag2"]; -- Select all people who have any email address ending in 'gmail.com' SELECT * FROM person WHERE emails.*.value ?= /gmail.com$/;
math::E
, math::FRAC_1_PI
, math::FRAC_1_SQRT_2
, math::FRAC_2_PI
, math::FRAC_2_SQRT_PI
, math::FRAC_PI_2
, math::FRAC_PI_3
, math::FRAC_PI_4
, math::FRAC_PI_6
, math::FRAC_PI_8
, math::LN_10
, math::LN_2
, math::LOG10_2
, math::LOG10_E
, math::LOG2_10
, math::LOG2_E
, math::PI
, math::SQRT_2
, and math::TAU
..
, array notation []
, and graph semantics ->
. SurrealQL enables records to link to other records and traverses all embedded links or graph connections as desired. When traversing and fetching remote records SurrealQL enables advanced filtering using traditional WHERE
clauses.
-- Select a nested array, and filter based on an attribute SELECT emails[WHERE active = true] FROM person; -- Select all 1st, 2nd, and 3rd level people who this specific person record knows, or likes, as separate outputs SELECT ->knows->(? AS f1)->knows->(? AS f2)->(knows, likes WHERE influencer = true AS e3)->(? AS f3) FROM person:tobie; -- Select all person records (and their recipients), who have sent more than 5 emails SELECT *, ->sent->email->to->person FROM person WHERE count(->sent->email) > 5; -- Select other products purchased by people who purchased this laptop SELECT <-purchased<-person->purchased->product FROM product:laptop; -- Select products purchased by people in the last 3 weeks who have purchased the same products that we purchased SELECT ->purchased->product<-purchased<-person->(purchased WHERE created_at > time::now() - 3w)->product FROM person:tobie;
// Set a new parameter LET $now = time::now(); // Create a record with a complex ID using an array CREATE temperature:['London', $now] SET location = 'London', date = $now, temperature = 23.7 ; // Create a record with a complex ID using an object CREATE temperature:{ location: 'London', date: $now } SET location = 'London', date = $now, temperature = 23.7 ;
-- Select all person records with IDs between the given range SELECT * FROM person:1..1000; -- Select all records for a particular location, inclusive SELECT * FROM temperature:['London', NONE]..=['London', time::now()]; -- Select all temperature records with IDs less than a maximum value SELECT * FROM temperature:..['London', '2022-08-29T08:09:31']; -- Select all temperature records with IDs greater than a minimum value SELECT * FROM temperature:['London', '2022-08-29T08:03:39']..; -- Select all temperature records with IDs between the specified range SELECT * FROM temperature:['London', '2022-08-29T08:03:39']..['London', '2022-08-29T08:09:31'];
.surml
data-format, enabling the model to be run consistently and safely in Python, Rust, or SurrealDB. Models can then be imorted into SurrealDB, allowing for Inference directly against any data in the database.
from surrealml import SurMlFile # Specify the trained model, model name, and model inputs file = SurMlFile(model=model, name="house::price::prediction", inputs=test_inputs) # Add a description for this model that can be viewed in teh database file.add_description("This model predicts the price of a house based on its square footage and number of floors.") # Add a version identifier which enables specific model versions to be run separately file.add_version("0.3.0") # Optionally define named input fields and data normalisation functions file.add_column("squarefoot") file.add_column("num_floors") file.add_normaliser("squarefoot", "z_score", squarefoot_mean, squarefoot_std) file.add_normaliser("num_floors", "z_score", num_floors_mean, num_floors_std) # Optionally specify named output fields with normalisation functions file.add_output("house_price", "z_score", house_price_mean, house_price_std) # Save the .surml file, ready to execute or import into the database file.save("./my_model.surml")
.surml
model files in Python allows for consistent and reproducible modeel computation in development, continuous integration, testing, or production environments.
from surrealml import SurMlFile # Load the model file from disk model = SurMlFile.load("./my_model.surml") # Perform raw vector compute against the model output = model.raw_compute([1.0, 2.0], [1, 2]) # Perform named buffered compute against the model output = model.buffered_compute({ "squarefoot": 3200.0, "num_floors": 2.0 })
-- Perform raw computation against the imported model RETURN ml::house::price::prediction<0.3.0>( [1.0, 2.0], [1, 2] ); -- Perform named buffered computation against the imported model SELECT *, ml::house::price::prediction<0.3.0>({ squarefoot: squarefoot_col, num_floors: num_floors_col }) AS price_prediction FROM property_listing WHERE price_prediction > 177206.21875 ;
SELECT * FROM geo::hash::encode( (-0.118092, 51.509865) );
SELECT time::floor(created_at, 1w) FROM user;
SELECT count(age > 18) FROM user GROUP ALL;
SELECT email, string::is::email(email) AS valid FROM user;
this
value.
CREATE film SET ratings = [ { rating: 6, user: user:bt8e39uh1ouhfm8ko8s0 }, { rating: 8, user: user:bsilfhu88j04rgs0ga70 }, ], featured = function() { return this.ratings.filter(r => { return r.rating >= 7; }).map(r => { return { ...r, rating: r.rating * 10 }; }); } ;
-- Define a global function which can be used in any query DEFINE FUNCTION fn::get::person($first: string, $last: string, $birthday: string) { LET $person = SELECT * FROM person WHERE [first, last, birthday] = [$first, $last, $birthday]; RETURN IF $person[0].id THEN $person[0] ELSE CREATE person SET first = $first, last = $last, birthday = $birthday END; }; -- Call the global custom function, receiving the returned result LET $person = fn::get::person('Tobie', 'Morgan Hitchcock', '2022-09-21');
IPv4
or IPv6
IP addresses.ES256
, ES384
, ES512
, HS256
, HS384
, HS512
, PS256
, PS384
, PS512
, RS256
, RS384
, and RS512
algorithms are supported.
-- Enable scope authentication directly in SurrealDB DEFINE SCOPE account SESSION 24h SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) ) SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) ) ;
// Signin and retrieve a JSON Web Token let jwt = fetch('https://api.surrealdb.com/signup', { method: 'POST', headers: { Accept: 'application/json', NS: 'google', // Specify the namespace DB: 'gmail', // Specify the database }, body: JSON.stringify({ NS: 'google', DB: 'gmail', SC: 'account', email: 'tobie@surrealdb.com', pass: 'a85b19*1@jnta0$b&!', }), });
-- Specify access permissions for the 'post' table DEFINE TABLE post SCHEMALESS PERMISSIONS FOR select -- Published posts can be selected WHERE published = true -- A user can select all their own posts OR user = $auth.id FOR create, update -- A user can create or update their own posts WHERE user = $auth.id FOR delete -- A user can delete their own posts WHERE user = $auth.id -- Or an admin can delete any posts OR $auth.admin = true ;
## Execute a SurrealQL query localhost % curl -X POST https://api.surrealdb.com/sql ## Interact with a table localhost % curl -X GET https://api.surrealdb.com/key/user localhost % curl -X POST https://api.surrealdb.com/key/user localhost % curl -X DELETE https://api.surrealdb.com/key/user ## Interact with a record localhost % curl -X GET https://api.surrealdb.com/key/user/tobie localhost % curl -X PUT https://api.surrealdb.com/key/user/tobie localhost % curl -X POST https://api.surrealdb.com/key/user/tobie localhost % curl -X PATCH https://api.surrealdb.com/key/user/tobie localhost % curl -X DELETE https://api.surrealdb.com/key/user/tobie
user@localhost % surreal .d8888b. 888 8888888b. 888888b. d88P Y88b 888 888 'Y88b 888 '88b Y88b. 888 888 888 888 .88P 'Y888b. 888 888 888d888 888d888 .d88b. 8888b. 888 888 888 8888888K. 'Y88b. 888 888 888P' 888P' d8P Y8b '88b 888 888 888 888 'Y88b '888 888 888 888 888 88888888 .d888888 888 888 888 888 888 Y88b d88P Y88b 888 888 888 Y8b. 888 888 888 888 .d88P 888 d88P 'Y8888P' 'Y88888 888 888 'Y8888 'Y888888 888 8888888P' 8888888P' To get started using SurrealDB, and for guides on connecting to and building applications on top of SurrealDB, check out the SurrealDB documentation (https://surrealdb.com/docs/). If you have questions or ideas, join the SurrealDB community (https://surrealdb.com/community). If you find a bug, submit an issue on GitHub (https://github.com/surrealdb/surrealdb/issues). We would love it if you could star the repository (https://github.com/surrealdb/surrealdb). ---------- Usage: surreal <COMMAND> Commands: start Start the database server backup Backup data to or from an existing database import Import a SurrealQL script into an existing database export Export an existing database as a SurrealQL script version Output the command-line tool and remote server version information upgrade Upgrade to the latest stable version sql Start an SQL REPL in your terminal with pipe support is-ready Check if the SurrealDB server is ready to accept connections [aliases: isready] validate Validate SurrealQL query files help Print this message or the help of the given subcommand(s) Options: -h, --help Print help
docker run --rm --pull always -p 8000:8000 surrealdb/surrealdb:latest start