Skip to content
NEW BLOG

Using Surrealism to build your own extensions

Read blog

1/2

Releases hero illustration

Releases

See release notes for past versions of SurrealDB

Our newsletter

Get tutorials, AI agent recipes, webinars, and early product updates in your inbox every two weeks

Release v3.1.0-beta.1

Released on May 1, 2026

This is the first beta in the 3.1 series. The focus is bug fixing and stabilisation of the 3.0 line, with a number of substantial additions on top of the fixes that already shipped in v3.0.1–v3.0.5.

Highlights

This release fixes over 40 community-reported bugs.

Exposed as a surreal mcp stdio subcommand for local IDE integrations, and as an HTTP /mcp endpoint guarded by the existing authentication middleware.

  • The tool surface includes typed query, select, create, insert, upsert, update, delete, relate, info, list, use, and run tools

  • read_only_hint / destructive_hint annotations so MCP clients can prompt the user before mutating operations

  • Request bodies are bound as typed Variables rather than string-interpolated

  • Identifiers are validated against a strict allow-list

  • Per-request subject verification rejects cross-session credential reuse

  • New environment variables: SURREAL_HTTP_MAX_MCP_BODY_SIZE (default 4 MiB), SURREAL_MCP_QUERY_TIMEOUT_SECS (default 60 s), SURREAL_MCP_MAX_RESULT_BYTES (default 256 KiB), SURREAL_MCP_RUN_MAX_ARGS (default 64), and SURREAL_MCP_PARAMS_MAX_KEYS (default 256)

Overhauled the observability and monitoring layer onto a single OpenTelemetry pipeline.

  • The previous community Prometheus path and the parallel OtelObserver SemConv pipeline have been collapsed onto one SdkMeterProvider and one SdkLoggerProvider

  • Every signal is recorded once and routed to many exporters: Prometheus text on /metrics, OTLP metrics push, OTLP logs push

  • Metric families are reorganised under the surrealdb.* scope (statement, query, transaction, RPC, auth, session, network, HTTP, live query, process, storage)

  • The /metrics endpoint enforces a render-time PUBLIC_METRICS allow-list: anonymous scrapers see only aggregate process gauges, while root-authenticated operators see the full surface

  • Tenant context (namespace / database / user) is carried on every labelled family but filtered out of the public view by family name

  • New environment variable: SURREAL_PROCESS_METRICS_REFRESH_INTERVAL (default 5 s)

  • Retired: SURREAL_TELEMETRY_NAMESPACE, SURREAL_TELEMETRY_RPC_LIVE_ID

  • Repurposed: SURREAL_METRICS_ENABLED

  • This is a breaking change for existing dashboards; see doc/OBSERVABILITY.md for the migration table

A round of memory work on the value layer including a 24-byte small-string-optimised Strand type, a new surrealdb-collections crate with VecMap/VecSet, and a per-transaction overhead reduction on the hot path.

  • Introduced Strand, a 24-byte small-string-optimised immutable string type, routed through every core hot-path string: Value::String, Object keys, TableName, and RecordIdKey::String

  • Strand packs into the same 24 bytes as String via a repr(C) SmolStr-style tagged union, with an inline variant storing strings up to 23 bytes with zero heap allocation

  • A heap variant uses Arc<str> so clones are a refcount bump rather than a copy

  • Object entries and Value::String shrink by 8 bytes each, which compounds at document scale

  • The wire format is unchanged: Strand serialises identically to String through serde, Revisioned, and storekey

  • A companion Strand::new_static constructor lets compile-time-known strings in core skip allocation entirely

Substantial performance improvements through engine tuning including RocksDB 0.24 upgrade, prefix extractor, blob-file separation by default, and scaled defaults for constrained environments.

  • RocksDB upgraded to 0.24 with enabled prefix extractor for keyspace-aware bloom filters

  • Aligned the compaction readahead size to max_sector_kb on disk (currently 256 KiB on Cloud)

  • Enabled blob-file separation by default for large-value workloads

  • Scaled defaults for constrained environments (the SurrealDB Cloud free tier sees disproportionate memory and disk pressure on the previous mid-sized defaults)

  • Large deployments (≥16 GiB memory) keep the previous defaults via tiered scaling; small deployments get appropriately sized buffers, caches, and compaction settings out of the box

Every DEFINE statement now has a matching ALTER counterpart.

  • Added ALTER support for EVENT, PARAM, BUCKET, ANALYZER, FUNCTION, USER, ACCESS, CONFIG, and API

  • Each follows the same property-by-property pattern as the existing ALTER TABLE / FIELD / INDEX statements

  • Individual properties on a definition can be modified without re-specifying the whole definition via DEFINE … OVERWRITE

  • ALTER PARAM does not support DROP VALUE (a param without a value is meaningless)

  • ALTER EVENT uses DROP ASYNC to revert to synchronous mode

  • ALTER ACCESS does not allow changing the access type itself (record / JWT / bearer), only durations, the AUTHENTICATE clause, and comment

Improvements

  • Added REMOVE CONFIG [IF EXISTS] GRAPHQL | API | DEFAULT to mirror DEFINE CONFIG, providing a way to delete GRAPHQL, API, and DEFAULT configurations.

  • Extended GraphQL schema generation to support fields whose type is a literal (record / object / array literal types), so they now appear in the generated GraphQL schema rather than being silently omitted.

  • Added the surrealdb-collections workspace crate with VecMap and VecSet

    • Vec-backed, ordered, insertion-order-preserving map and set types

    • Tuned for the small-collection workloads that dominate SurrealDB's value layer

    • Object and Set in surrealdb-core now use these types

    • Linear scan for small slices, skipped re-sort when inputs are already ordered, and a tuned linear-vs-binary search threshold

    • User-facing semantics are unchanged

  • Upgraded RocksDB to 0.24 and tuned the engine

    • Enabled prefix extractor for keyspace-aware bloom filters

    • Aligned the compaction readahead size to max_sector_kb on disk (currently 256 KiB on Cloud)

    • Enabled blob-file separation by default for large-value workloads

    • Scaled defaults for constrained environments (the SurrealDB Cloud free tier sees disproportionate memory and disk pressure on the previous mid-sized defaults)

    • Large deployments (≥16 GiB memory) keep the previous defaults via tiered scaling; small deployments get appropriately sized buffers, caches, and compaction settings out of the box

  • Reduced per-transaction overhead on the hot path

    • FunctionRegistry::with_builtins is now built once per datastore and shared across transactions instead of being rebuilt on every query (was ~22% of CPU on a CREATE-heavy profile)

    • The change-feed Writer is allocated lazily so transactions that touch no changefeed-enabled tables no longer pay for a DashMap<ChangeKey, TableMutations> and its drop

    • The per-transaction quick_cache now uses a single shard sized for transaction-scope rather than the available_parallelism() * 4 default, eliminating the 256 CacheShard allocations that used to happen on every Transaction::new on a 64-core host

  • Updated SurrealKV to 0.21.1 and raised the maximum memtable size for large transactions, allowing larger batches to be inserted without flushing mid-transaction.

  • Tuned RocksDB scans and grouped-commit

    • Read-only transactions now bypass the BaseDeltaIterator merge layer in count, keys, keysr, scan, and scanr, which removes overhead on every step

    • The offload heuristic for moving large scans to the storage threadpool now uses byte-based sizing (including skip work) and handles BytesOrCount correctly

    • The grouped-commit coordinators (RocksDB and SurrealKV) had shutdown and lost-wakeup edge cases that could stall threads or callers; both are fixed

  • Capped the scanner's initial batch size using the query LIMIT so that small-LIMIT queries don't fetch more rows than they will ever return.

  • Added a sync fast-path for PhysicalExpr evaluation in scan filters, avoiding the async overhead for the common case where no IO is required.

  • Avoided cloning the document on SELECT * projection where the source row can be passed through unchanged, reducing allocator pressure on read-heavy workloads.

  • Replaced the global HTTP client cache (which keyed on a hash of capabilities) with a single HttpClient instance per Datastore, exposed via Context::http_client

    • Removes process-wide global state

    • Simplifies test setup

    • Removes per-request hashing

    • Context::background is renamed to Context::new_test and is now test-only

    • Context::new is renamed to Context::new_child to make its sub-context role explicit

  • Made datastore initialisation retry resilient to hung attempts

    • Each retry attempt is now wrapped in its own per-attempt tokio::time::timeout that increases linearly (10 s, 20 s, 30 s, …)

    • A single hung attempt against a slow or unresponsive storage backend can no longer silently consume the entire retry budget

    • Per-attempt timeouts and back-off sleeps are capped to the remaining global budget

    • The global retry budget is raised from 60 s to 120 s

    • Failures are now logged with a per-task label so the failed operation is identifiable

    • Exhaustion returns a descriptive Internal error instead of re-raising the last TransactionConflict

  • Added RocksDB user-defined timestamps and threaded the VERSION clause through every internal schema lookup

    • Versioned reads (SELECT … VERSION d'…', INFO FOR DB / TABLE VERSION …) are now isolated from the LRU transaction cache so historical reads cannot pollute current-time reads

    • The VERSION clause is now propagated through graph and reference traversals, the FETCH clause, automatic record dereferences during field access, and inherited by inner subqueries when the outer SELECT carries a version

    • INFO FOR ROOT and INFO FOR NS now also accept VERSION

    • RocksDB gains a custom comparator that stores 8-byte LE timestamps as key suffixes (newer versions ordered before older), with HLC commit timestamps assigned at commit time and ReadOptions::set_timestamp for point-in-time reads

  • Overhauled the Surrealism (WASM plugin) stack to v0.2.0 and migrated to WASI Preview 2 (Component Model)

    • Modules now target wasm32-wasip2 and a WIT interface (surrealism/wit/surrealism.wit) defines the host/guest contract

    • New #[surrealism] attribute options: writeable (lets the planner choose appropriate transaction modes), comment (description visible in surreal module info and INFO FOR DB STRUCTURE), init (one-time init hook with access to host imports), and namespaced module exports that produce prefixed names like math::add

    • Data crossing the host/guest boundary now serialises via FlatBuffers

    • Modules can declare an attached read-only filesystem ([attach] fs = "fs" in surrealism.toml) bundled into the .surli archive and mounted at / inside the WASM sandbox

    • The host runtime gets a two-engine epoch model — a guarded engine with epoch interruption for timeout enforcement, and a fast engine for trusted compute-heavy modules — selected via strict_timeout in surrealism.toml

  • Added a builder pattern to Datastore for properties that cannot be changed after construction, replacing the previous post-construction setters

    • Makes invalid configurations harder to express

    • Lets the language-test suite exercise more configuration surfaces

    • The language-test harness has been overhauled in the same change

  • Expanded the surrealdb-types SDK and derive surface

    • Added #[surreal(rename_all = "...")] on enums and structs and honoured per-variant #[surreal(rename = "...")] (previously parsed only for fields)

    • Added #[surreal(wrap)] for transparent wrapper types

    • Added more SurrealValue implementations for standard-library types and a NaiveDate implementation

    • Generalised the Cow and &str SurrealValue implementations

    • Added serde interop on the Value type

    • Restored query chaining on the Query builder

    • Added several missing crate exports

  • Auto-detect TTY for the console log output and respect the NO_COLOR environment variable

    • ANSI escape codes are now only emitted when both stdout and stderr are TTYs

    • Fixes garbled output in Docker containers, Kubernetes pods, and log shippers like Kibana and GKE Log Explorer

  • Allow surreal validate to read input from stdin, so SurrealQL can be piped in for syntax checking without writing to a file first.

  • Removed several process-wide configuration globals in surrealdb-core so configuration is now carried through Datastore / Context rather than read from static state, simplifying embedding and testing.

Bug fixes

  • [Query Engine] Fixed $parent resolving incorrectly inside graph WHERE clauses in nested subqueries; $parent now refers to the current SELECT's row rather than an outer subquery's parent.

  • [Query Engine] Fixed FROM ONLY not being propagated through fused graph lookup chains, which previously produced incorrect results from constrained graph traversals.

  • [Query Engine] Fixed ORDER BY (and WHERE) being silently ignored in subqueries with graph traversal sources like FROM $parent->edge->target. The streaming source operator now resolves RecordId values to full documents so downstream Filter, Sort, Group, and Split operators have the fields they need.

  • [Query Engine] Fixed bare field paths in a subquery's FROM clause (e.g. FROM data.files where data is a record link on the outer document) evaluating to NONE because the source operator had no current value in correlated-subquery context.

  • [Query Engine] Fixed $parent not being bound during iterator.prepare, which caused correlated subqueries using FROM $parent->… (including nested CREATE, UPDATE, and DELETE) to see $parent unset while select targets were being planned.

  • [Query Engine] Fixed $parent resolution in materialised view definitions on the streaming path so view materialisation now sees the same parent context as the legacy executor.

  • [Query Engine] Fixed LET bindings in planned bodies not being visible to IF / FOR blocks that fall back to the legacy compute path; the legacy FrozenContext is now refreshed after a planned LET body runs.

  • [Query Engine] Fixed $auth and $session returning NONE in queries executed inside a client-side transaction; the session is now correctly attached to the per-statement context.

  • [Query Engine] Fixed duplicate constant values being omitted in the streaming executor (e.g. false AS isLiked, false AS isLocked collapsing to a single column). The expression registry's de-duplication key now includes the alias.

  • [Query Engine] Fixed nested .* destructure returning NONE for record links; DestructureField::All now fetches the linked record before expanding.

  • [Query Engine] Fixed SELECT alias shadowing the source field of the same name on the projection fast path.

  • [Query Engine] Preserved alias idiom structure in streaming-executor projections so that AS foo.bar continues to nest into { foo: { bar: _ } } while AS foo.bar stays as a flat key — both the executor projection and GROUP BY alias paths previously stringified the alias and lost the backtick distinction.

  • [Query Engine] Fixed ?? (null coalescing) and ?: (ternary condition) operator precedence binding tighter than arithmetic. $ctx.limit ?? 2 ** 31 - 1 now parses as $ctx.limit ?? (2 ** 31 - 1), matching JavaScript / C# / Kotlin / Swift / PHP semantics; previously the operators bound above **, *, /, +, and -, which made ?? nearly useless without explicit parentheses.

  • [Query Engine] Fixed value::diff and value::patch returning Function 'value::…' requires async execution when invoked via method syntax ({a: 1}.diff({a: 2})). The async-vs-sync check now matches the behaviour of the full-path call.

  • [Query Engine] Fixed view materialisation crashing when the source SELECT aliased the id field; initialisation now falls back to scanning for any RecordId when the "id" key is absent.

  • [Query Engine] Fixed references being created against a target table even when the originating CREATE/INSERT/RELATE/UPSERT/UPDATE failed its CREATE permission check; references are now cleaned up on the permission-denied error path.

  • [Query Planner] Fixed WHERE filters on indexed fields silently omitting COMPUTED fields from evaluation. IndexScan, FullTextScan, and KnnScan now resolve field state and process computed fields through their pipelines, matching TableScan and UnionIndexScan.

  • [Query Planner] Fixed records with NONE / NULL fields being silently excluded from ORDER BY results when the sort key was on a unique compound index; unique indexes now store NONE / NULL tuples in the non-unique key format so they remain visible to scans (REBUILD INDEX is required to fix existing indexes).

  • [Query Planner] Fixed SELECT VALUE with ORDER BY ignoring the sort order when querying from an array source; the VALUE projection is now applied after sorting and pagination instead of before. ORDER BY can also reference a SELECT VALUE alias.

  • [Query Planner] Fixed ORDER BY DESC with a variable LIMIT inside nested IF / LET blocks inside a function producing wrong results.

  • [Query Planner] Fixed ORDER BY on edge-table queries with record-link source fields like in.creationDate returning unsorted output. The sort path now routes resolved alias expressions through the Compute operator (which can fetch linked records) and restricts the synchronous FieldPath shortcut to single-part paths where record-link traversal is impossible.

  • [Query Planner] Fixed KNN <|K, DISTANCE|> queries bypassing the HNSW index; eval_hnsw_knn now matches the K(k, d) variant when the distance matches the index configuration.

  • [Indexes] Made full-text, COUNT and HNSW compaction SSI-safe so concurrent index activity no longer drops work that was committed after the compaction snapshot. Compaction is now split-phase and generation-guarded, with conditional writes that advance an internal generation key and delete only the exact delta keys captured in the read phase. Full-text and COUNT compaction plans are bounded to COUNT_BATCH_SIZE (50,000) delta keys per batch, so a long-stalled delta backlog is processed in fixed-size batches instead of being held entirely in memory. HNSW pending updates now use record-keyed entries so sequential writes from multiple SurrealDB nodes against the same remote backend no longer depend on local append-key ordering.

  • [Indexes] Fixed concurrent index conflicts and reduced overhead during full-text concurrent builds. The initial-build write transaction no longer reads !ip / !ig queue handoff records (those are now read through a separate read-only transaction, so the initial-build write transaction stays out of active queue contention). maybe_consume no longer writes !ip keys after the initial build phase. The append-phase commit retries on transient transaction conflicts instead of treating them as fatal. Full-text concurrent builds now reuse FullTextIndex across documents in a batch instead of recreating it per document.

  • [Indexes] Fixed cascade-delete index bugs that caused COUNT-index drift, ghost UNIQUE-index entries on the relation table after deleting a vertex with edges, and phantom UNIQUE-index entries when IN / OUT records were deleted before the relation. Also fixed process_lookup hardcoding None for the version parameter, which caused SELECT … WITH VERSION queries that traverse graph edges to silently return current-state records instead of the requested historical snapshot.

  • [Live Queries] Terminate live queries on session invalidation and TTL expiry. invalidate() now calls cleanup_lqs(), mirroring reset(), so all live-query registrations for the session are removed from the notification engine. The dispatcher also compares the session expiry timestamp against the current time before sending each notification and silently skips expired ones, closing a class of bug where revoked sessions continued receiving CREATE / UPDATE / DELETE events indefinitely.

  • [Live Queries] Prevent ignorable evaluation errors in a LIVE SELECT's WHERE clause or SELECT projection (type mismatches, invalid function arguments) from propagating back through try_join_all and aborting the enclosing write transaction. Such errors now silently skip the notification.

  • [Storage] Fixed long server non-responsiveness when a client cancels surreal export mid-stream. The HTTP export handler now exits the proxy task when the body stream channel fails (indicating client disconnect), which causes the next channel send to fail and aborts the export task — releasing the read transaction promptly. On large databases this previously blocked endpoints like /health while the cancelled export ran to completion.

  • [Indexes/SurrealKV] Allow large transactions to insert without flushing mid-transaction by raising the maximum memtable size for SurrealKV.

  • [RPC/WebSocket] Respond with a failure when an in-flight WebSocket request is cancelled rather than silently dropping it, so clients always observe an explicit response.

  • [Transactions] Surface unified Cannot COMMIT: … errors when an explicit COMMIT after a previous statement abort fails (e.g. unique constraint), instead of returning only the first statement error. txn.commit() failures and the post-abort COMMIT fast-forward now share the same error wording so clients no longer treat a missing row as success.

  • [Auth] Fixed id resolving to NONE inside a table's FOR select WHERE … permission clause when a record was created and selected in the same statement (SELECT * FROM ONLY (CREATE ONLY a)), which previously caused the record to be filtered out by its own SELECT permission. RecordId::select_document now re-injects the id into the returned object when it's missing on a transaction-cache hit.

  • [Auth] Fixed user-defined function PERMISSIONS clauses being evaluated for system-level users (root, namespace, database). PERMISSIONS are intended only for record-level users; system-level users with Action::View now correctly bypass the check and can invoke functions defined with PERMISSIONS NONE. Also removed a hardcoded fn:: prefix from the FunctionPermissions error message that produced double-prefixed names like fn::fn::my_function.

  • [Functions] Added encoding::json::encode and encoding::json::decode for serialising SurrealDB values to / from JSON strings, and added support for UTF-16 surrogate-pair escape sequences (\uD800–\uDFFF) in the JSON parser. The surrogate-pair support is gated on a json_string_escapes parser setting so SurrealQL string parsing is unaffected.

  • [Functions] Use serde_json to parse external JSON in http::* functions so valid JSON responses containing escaped forward slashes (\/) — produced by default json_encode() in PHP and other systems — are no longer rejected by SurrealQL's own JSON parser.

  • [Functions] Fixed infinity output and added a non-abbreviated constant path so that the constant is preserved on round-trip.

  • [Functions] Fixed user-duration structure so that durations defined and stored on user definitions round-trip correctly.

  • [Types/SDK] Fixed stack overflow on recursive SurrealValue types so deeply nested or self-referential type definitions no longer crash on encode / decode.

  • [Types/SDK] Fixed SurrealValue derive macro emitting r#type instead of type for raw-identifier struct fields. Fields like r#type: String now serialise as "type" without needing an explicit #[surreal(rename = "type")] attribute.

  • [Types/SDK] Fixed a copy-paste bug in prepare_array (in iterator.rs) that checked x[0] for Part::Lookup after already matching it as Part::Start, making the lookup-optimisation branch dead code. All edge traversals inside array FROM clauses (SELECT * FROM [a:1->edge->table]) previously fell through to the slower prepare_computed path; the optimised prepare_lookup path is now reached.

  • [Types/SDK] Fixed JSON-patch remove operation on lists.

  • [Types/SDK] Fixed CREATE failing for fields typed as set<…>.

  • [Imports/Exports] Fixed re-importing an exported database failing with "field already exists" errors. Export now emits DEFINE FIELD OVERWRITE so a clean re-import succeeds without manual intervention.

  • [Logging] Fixed JSON log format being unused even when configured.

  • [Logging] Fixed ANSI escape codes leaking into JSON log output (which previously produced corrupt records like "level": "\x1b[32mINFO\x1b[0m") and added per-stream TTY detection so mixed-redirect scenarios like surreal start > app.log no longer strip ANSI from the stream that is still an interactive terminal.

  • [CLI] Prevent panic on systems with restricted /proc access (e.g. NixOS with ProcSubset=pid hardening) where sysinfo cannot read /proc/meminfo and System::total_memory() returns 0. The previous cgroup_limits() assertion put the systemd service into an infinite restart loop on both RocksDB and SurrealKV backends.

  • [Events] Fixed events defined on a vertex table incorrectly firing for related edge-table records during cascade deletion. When deleting a vertex with graph edges (via RELATE), the edge deletes now run with the edge table's document context so the vertex table's events, views, live queries, changefeeds, and field validation are no longer applied to edge-record mutations.

Release v3.0.4

Released on Mar 13, 2026

Release v3.0.0

Released on Feb 17, 2026

After numerous alpha and beta releases over more than a year of development, we are releasing SurrealDB v3.0.0! 🎉. The changes for this release pertain to those over all the alpha and beta versions before 3.0.0, starting with 3.0.0-alpha.1 and the last 3.0.0-beta.4 and finally the changes between the final beta version and stable 3.0.0.

Items in the release notes marked as 🆕 are the newest changes from the final beta and 3.0.0.

To migrate from a 2.x version to 3.0, please see the following page which goes into further detail on the breaking changes and how to use migration diagnostics to obtain a list of actions for your particular schema.

Performance and optimisations:

Features, additions and syntax improvements:

Bug fixes:

Workflow, observability and developer experience improvements:

Storage/index format and protocol:

Security/networking:

Release v2.6.5

Released on Mar 24, 2026

Release v2.6.4

Released on Mar 17, 2026

Release v2.6.3

Released on Mar 4, 2026

Release v2.6.2

Released on Feb 16, 2026

Release v2.6.1

Released on Feb 12, 2026

Release v2.6.0

Released on Jan 27, 2026

Release v2.5.0

Released on Jan 22, 2026

Features:

Security Advisories:

Note: Storable closures in future fields and functions are now disabled by default for security reasons. To enable the previous behaviour, set the SURREAL_CAPS_ALLOW_INSECURE_STORABLE_CLOSURES environment variable. This change addresses the security vulnerability described in Advisory.

Release v2.3.10

Released on Sep 19, 2025

Release v2.3.9

Released on Sep 16, 2025

Release v2.3.7

Released on Jul 9, 2025

Release v2.3.5

Released on Jun 19, 2025

Release v2.3.4

Released on Jun 16, 2025

Release v2.3.3

Released on May 21, 2025

Release v2.3.2

Released on May 12, 2025

Release v2.3.1

Released on May 7, 2025

This release resolves an issue identified in v2.3.0 that can corrupt the database when an UPDATE statement is stored within a function. If you experience this issue when migrating to v2.3.0 please kindly remove those function definitions, upgrade to v2.3.1 and then add them back.

Bug fixes:

Release v2.3.0

Released on May 1, 2025

NB: We identified an issue in this release that can corrupt the database when an UPDATE statement is stored within a function. We released v2.3.1 to address this. If you defined functions with UPDATE statements in v2.3.0 please kindly remove those function definitions, upgrade to v2.3.1 and then add them back.

Features:

Bug fixes:

Deprecations:

The following Rust SDK APIs are now deprecated. In addition, some Capabilities methods were renamed and the previous names marked as deprecated.

  • IntoQuery::into_query

  • IntoEndpoint::into_endpoint

  • IntoFn::into_fn

  • IntoExportDestination::into_export_destination

  • QueryResult::query_result

  • QueryResult::stats

  • QueryStream::query_stream

  • IntoResource::into_resource

  • CreateResource::into_resource

  • proto::ResponseAction

  • remote::WsNotification

Workflow improvements:

Release v2.2.8

Released on Sep 11, 2025

Release v2.2.7

Released on Jul 9, 2025

Release v2.2.5

Released on Jun 19, 2025

Release v2.2.4

Released on Jun 17, 2025

Release v2.2.3

Released on May 13, 2025

Bug fixes:

Release v2.2.0

Released on Feb 11, 2025

Features:

Bug fixes:

Optimisations:

SurrealKV:

Workflow improvements:

Experimental:

Breaking changes:

Release v2.1.9

Released on Sep 11, 2025

Release v2.1.8

Released on Jun 26, 2025

Release v2.1.7

Released on Jun 19, 2025

Release v2.1.6

Released on Jun 17, 2025

Release v2.1.4

Released on Dec 18, 2024

Release v2.1.3

Released on Dec 10, 2024

Release v2.1.2

Released on Nov 26, 2024

Release v2.1.0

Released on Nov 21, 2024

Features:

Performance improvements:

Bug fixes:

Workflow improvements:

Features with breaking changes:

Release v2.0.2

Released on Sep 25, 2024

Release v2.0.1

Released on Sep 18, 2024

Bug fixes:

Release v2.0.0

Released on Sep 17, 2024

After numerous alpha and beta releases over months of development, we are releasing SurrealDB v2.0.0! 🎉. The changes for this release pertain to those in between the final 2.0.0-beta-3 and other alpha versions.

NOTE: Because of changes in the underlying way SurrealDB stores data, steps are required to migrate to 2.0.0 from previous versions. Please use the following steps for any existing databases:

Features:

Performance improvements:

Bug fixes:

Workflow improvements:

API improvements:

Surrealkv API improvements:

Parser improvements:

Breaking changes:

Experimental features:

Release v1.5.6

Released on Nov 12, 2024

Features:

Bug fixes:

Release v1.5.5

Released on Sep 11, 2024

Bug fixes:

Release v1.5.4

Released on Jul 10, 2024

Bug fixes:

Release v1.5.3

Released on Jun 14, 2024

Release v1.5.2

Released on Jun 6, 2024

Bug fixes:

  • Fixed incorrect computations on aggregations within foreign tables

  • Fixed surreal upgrade --nightly

Release v1.5.1

Released on May 23, 2024

Bug fixes:

  • Make temporary table active only if the temporary directory is set

  • Fixes compilation issue related to temporary-directory when the feature sql2 is not enabled

Release v1.5.0

Released on May 14, 2024

Features:

  • Added an implementation of HNSW in memory.

  • Added a REBUILD INDEX statement.

  • Added variable support in CONTENT clause for RELATE statements.

  • Added more information to INFO FOR SCOPE.

  • Added a relate method to the RPC protocol.

  • Added an INFO STRUCTURE statement.

  • Stabilised sql2 and jwks features.

  • Introduced an on-disk temporary table.

  • Added a run method to the RPC protocol.

  • Implemented limits for parsing depth in the new parser.

  • Implemented reblessive into the parser to prevent any overflows.

  • Increased the Minimum Supported Rust Version (MSRV) to 1.77.

Bug fixes:

  • Made the query planner recognise the exact operator (==).

  • Fixed math:min in foreign tables.

  • Fixed typo in function math:sum: was math::sun.

  • Made record IDs more flexible in the new parser.

  • Reverted changefeed polling frequency back to 10 seconds.

  • Fixed a problem with date-time parsing.

  • Fixed response content-type on /rpc endpoint.

  • Fixed decimal decoding.

  • Made the query planner support the IN operator.

  • Made the JWKS implementation more RFC 7517 compliant.

  • Fixed wrong count when using COUNT with a subquery.

  • Fixed the content type header on a CBOR HTTP response.

  • Fixed $value being NONE for DELETE events.

  • Fixed array::group in a group by query.

Performance improvements:

  • Added a query planner strategy for record links.

  • Made TreeCache more efficient on writes.

  • Reduced byte size of datetime and UUID types using CBOR format.

Release v1.4.2

Released on Apr 20, 2024

Bug fixes:

  • Fix problems with if in identifiers after remove and define statements.

  • Fix $value being NONE for DELETE events.

  • Fix CBOR headers.

  • Fix wrong count when using COUNT with a subquery.

  • Fix IN operator should be recognised by the query planner.

  • Fix response content-type on /rpc endpoint.

  • Fix problems with if in identifiers after remove and define statements.

  • Fix $value being NONE for DELETE events.

  • Fix CBOR headers

  • Fix wrong count when using COUNT with a subquery

  • Fix IN operator should be recognised by the query planner

  • Fix response content-type on /rpc endpoint

When using the Rust SDK, make sure surrealdb-core is up-to-date. If not, you might get back Revision errors as a result.

Release v1.4.0

Released on Apr 9, 2024

Features:

  • Added a warning message about debug builds in CLI start.

  • Moved JWKS cache storage to memory.

  • Overhauled force implementation.

  • Added DEFINE TABLE ... RELATION.

  • Allowed RPC calls to be made over an HTTP connection, not just WebSocket.

  • Allowed the highlighter to only highlight the matching searched token rather than the whole term.

  • Added an INCLUDE ORIGINAL option to change-feeds.

  • Added an insert method to the Rust SDK to allow bulk inserts.

Bug fixes:

  • Fixed export generating unparsable code with the new parser.

  • Eliminated a potential panic in MsgPack format implementation.

  • Fixed string::is::longitude RegEx.

  • Improved CBOR decoding.

  • Fixed relation type parsing.

  • Fixed handling of empty array on index.

  • Allowed legacy headers in CORS.

  • Allowed surreal upgrade to detect when upgrading to the same version and return early.

  • Fixed certain environment variables to allow configuration at runtime.

  • Reduced the byte size of datetime and uuid types using CBOR format.

  • Fixed array::group in a group by query.

Performance improvements:

  • Improved query aggregation handling.

This was one of the frequently requested features. While this was already possible using the query method, this release adds an insert method that makes it more convenient.

db.insert(Resource::from("person"))
.content(vec![
User {
id: thing("person:tobie")?,
name: "Tobie",
settings: Settings {
active: true,
marketing: false,
},
},
User {
id: thing("person:jaime")?,
name: "Jaime",
settings: Settings {
active: true,
marketing: true,
},
},
])
.await?;

Release v1.3.1

Released on Mar 15, 2024

Bug fixes:

  • Add JWT ID claim to tokens issued by SurrealDB.

  • Consistently enforce session expiration.

Release v1.3.0

Released on Mar 12, 2024

Features:

  • Changed the KNN operator from <2> to <|2|> in the new parser and added support in the old parser for the new syntax.

  • Implemented WebSocket session expiration.

  • Added support for tables and geometries in CBOR.

  • Added support for parsing decimal numbers with scientific notation.

  • Added support for printing a warning in the CLI when using an outdated version of SurrealDB.

  • Added Surreal::wait_for to make it possible for the client to wait for certain events.

  • Added SurrealKV as an unstable feature.

  • Added more error message improvements for the new parser.

Bug fixes:

  • More consistent handling of negative numbers in record IDs.

  • Removed the unimplemented backup command from the CLI.

  • Fixed document not being available to delete permissions clause.

  • Ensured objects properties are recognised by the query planner.

  • Implemented the union strategy on unique indexes.

Performance improvements:

  • Added compile-time feature for flamegraph and pprof profiling.

DEFINE statements now allow you to include an IF NOT EXISTS clause which ensures that the define statement is only run if the resource doesn't already exist. If it exists, you will get an error. Learn more about this in the documentation.

DEFINE DATABASE IF NOT EXISTS example

The KNN operator now supports a new syntax using <| and |> instead of < and >. Learn more about this in the documentation

SELECT id FROM point WHERE point_b <|2|> [2, 3, 4, 5]

It's now possible to initialise the connection and run setup actions like authentication and selecting the database concurrently with other queries by making it possible to wait for the SDK to connect or select the database to use before allowing other queries to execute.

Something like this would be susceptible to a race condition before:

// A static, global instance of the client
static DB: Lazy<Surreal<Any>> = Lazy::new(|| {
// Connect and setup the database connection in the background
tokio::spawn(async {
// This example is for demonstration purposes only.
// If the `unwrap` panics the client will be left hanging,
// waiting for the setup to happen when using `Surreal::wait_for`.
setup_db().await.unwrap();
});
// Initialise a new unconnected instance of the client
Surreal::init()
});

// Connect to the server and run setup actions
async fn setup_db() -> surrealdb::Result<()> {
// Connect to the server
DB.connect("ws://localhost:8000").await?;

// Signin as a namespace, database, or root user
DB.signin(Root {
username: "root",
password: "root",
}).await?;

// Select a specific namespace / database
DB.use_ns("namespace").use_db("database").await?;

Ok(())
}

// Whether this would run successfully or return an error would depend on whether
// `DB::setup` was able to execute before getting to this point.
DB.create(Resource::from("person")).await?;

Now it's possible to make the client wait for either the connection to be established

use surrealdb::opt::WaitFor::Connection;

// This will wait until the connection is established.
// As soon as it is, it will return and allow the client to continue.
DB.wait_for(Connection).await;

// This will still likely return an error.
// While we know that the connection is already established at this point,
// there is no guarantee that the database has already been selected.
// This query requires the database to be selected first.
DB.create(Resource::from("person")).await?;

or the database to be selected.

use surrealdb::opt::WaitFor::Database;

// This will wait until the database is selected.
// As soon as it is, it will return and allow the client to continue.
DB.wait_for(Database).await;

// At this point, the database is guaranteed to be selected already
// so this query should run successfully.
DB.create(Resource::from("person")).await?;

Release v1.2.2

Released on Mar 7, 2024

Bug fixes:

  • Ensure relevant indexes are triggered when using IN in a SELECT query.

  • Ensure the query planner accepts Datetime and Uuid values.

  • Restore cosine distance on MTree indexes.

  • Fix regression in index data serialisation.

  • Ensure rquickjs builds don't stall on macOS.

Release v1.2.1

Released on Feb 16, 2024

Bug fixes:

  • Fix an issue with WHERE clause on queries resolving record links or graph edges.

  • Fix MATH::SQRT_2 not parsing.

  • Fix a panic in span rendering.

  • Fix CLI output not displaying properly sometimes.

Release v1.2.0

Released on Feb 13, 2024

Features:

  • Bump MSRV to 1.75.

  • In-memory index store.

  • Show execution time in CLI.

  • knn brute force.

  • Implement support for remote JSON Web Key Sets.

  • Add support for LIVE SELECT in the SDK and CLI.

  • Add IF EXISTS to REMOVE TABLE statement.

  • Add READONLY keyword to DEFINE FIELD statement.

  • Add alias -V and --version flags for surreal version command.

  • Define types for subfields when defining array fields.

  • Add string::semver::compare, string::semver::major, string::semver::minor, string::semver::patch, string::semver::inc::major, string::semver::inc::minor, string::semver::inc::patch, string::semver::set::major, string::semver::set::minor, string::semver::set::patch methods.

Bug fixes:

  • Make record id string parsing never fail early.

  • Prevent overflow in math::power().

  • Fix error message pointing to wrong character.

  • Respect alias for dynamic field queries with type::field.

  • Remove min/1000 in tx:delp.

  • Replace custom JWT parser causing decoding issues.

  • Issue with scoring on complex queries.

  • Limit recursion depth when parsing nested RELATE statements.

  • Fix a bug where a non-empty list parser would parse empty lists.

  • Ensure an attempt to set a protected variable returns an error.

  • Fix duration addition in timeout causing overflow.

  • Panic invoking parameters and functions without a database.

  • Fix a bug where the kind parser would eat whitespace.

  • Fix WebSocket notification sending format.

  • Implement missing errors for missing clauses on DEFINE-statements.

  • Ensure advanced DEFINE PARAM parameters are computed correctly.

  • Ensure path idioms are correct when looping over.

  • Fix json failing to parse with trailing whitespace.

  • Fix the five second delay in Wasm after initial connection.

  • Add context to live query notifications.

  • Fix the modulo operator on sql2 and parser1.

  • Improve the js-surql value conversion for numbers.

  • Implement revision types for client/server communication.

  • Fix builtin error pointing to the wrong part.

  • Fix a panic when invalid builtin function names are passed to the executor.

Performance improvements:

  • Ensure compression is only enabled when response is a certain size.

  • In MTree large vector improvement.

-- Specify the namespace and database for the token
USE NS abcum DB app_vitalsense;

-- Set the name of the token
DEFINE TOKEN token_name
-- Use this token provider for database authorization
ON DATABASE
-- Specify the JWKS specification used to verify the token
TYPE JWKS
-- Specify the URL where the JWKS object can be found
VALUE "https://example.com/.well-known/jwks.json"
;
DEFINE FIELD created ON resource VALUE time::now() READONLY;
DEFINE FIELD foo ON bar TYPE array<number>
-- Where <number> is a subfield of the array type.
-- It now defines the number subfield automatically instead of having to run this manually:
DEFINE FIELD foo.* ON bar TYPE number

Release v1.1.1

Released on Jan 16, 2024

Bug fixes:

  • Fix WebSocket notification sending format.

  • Fix missing custom claims from token parameter.

  • Fix URL encoding in JS functions.

  • Fix panic when invoking parameters and functions without a database.

Release v1.1.0

Released on Jan 9, 2024

Features:

  • The type::is::record() function now accepts a second optional table argument, validating the record being stored on the passed table.

  • Add time::micros(), time::millis() and time::from::nanos functions.

  • Add type::is::none() function.

  • Add object::entries(), object::from_entries(), object::len(), object::keys() and object::values() functions.

  • Clean paths in the start command and honour ~.

  • CLI: Split results by comment.

  • Add surreal sql welcome message.

  • Add Debugging env flag: SURREAL_INSECURE_FORWARD_SCOPE_ERRORS.

  • Add SURREAL_ROCKSDB_KEEP_LOG_FILE_NUM environment variable (default 20).

  • Support auth levels for basic auth (behind feature flag)

  • Add remainder/modulo operator.

  • Implement string prefixes: s, r, d and u.

  • Add ability to cast string to a Thing/Record ID.

  • Analyzers to support functions.

  • Support of subfields for embedding indexing.

  • Add live query API to Rust SDK.

  • Add Query::with_stats() to return query statistics along with the results.

  • Permissions are now always displayed for visibility

  • Add a --beta flag to surreal upgrade to make installing the latest beta release easier.

Bug fixes:

  • Fix stack overflow in graph traversal.

  • Bugfix - parse error for invalid leading whitespace.

  • Fix memory leak caused by OTEL callbacks.

  • Fix wrong function name export and function name parsing.

  • The position of the LIMIT and ORDER clauses are now interchangeable.

  • Fix index plan for idiom param value.

  • Fix bug where error offset could underflow.

  • Query results should be consistent and representative.

  • Indexes used with the operators CONTAINS [ ANY | ALL ].

  • Forward custom thrown errors in SIGNIN and SIGNUP queries.

  • Fix ORDER BY RAND() failing to parse when selecting specific fields.

  • Fix identifiers which look like numbers failing to parse.

  • Change math::median indexing for even length arrays.

  • Pass IP & Origin onto session used by scope queries.

  • Fix possible corruption of MTree and incomplete knn.

  • Allow array::flatten() to be used as an aggregate function.

  • Make SELECT ONLY deterministic.

  • Optional function arguments should be optional.

  • Default table permissions should be NONE

  • Bugfix: Fix inconsistent record parsing

  • Fix time regression in surrealdb.wasm binaries.

  • Fix computing futures in query conditions.

  • Fix issue with scoring on complex queries.

  • Fix ML support on Windows and enable the feature in Windows binaries.

  • Replace the custom JWT parser causing decoding issues.

  • Ensure compression is only enabled when response is a certain size.

  • Respect alias for dynamic field queries with type::field().

  • Prevent overflow in math::power().

  • Fix error message pointing to wrong character.

  • Expand logic for static value validation to improve DEFAULT clause handling.

  • Fallback to a string when record ID parsing fails.

  • Ensure an attempt to set a protected variable returns an error.

  • Fix duration addition in timeout causing overflow.

  • Fix a bug where a non-empty list parser would parse empty lists.

  • Limit recursion depth when parsing nested RELATE statements.

  • Ensure REMOVE statement does not only remove the first 1000 keys.

  • Fix BTree deletion bug.

  • Replace close method on live::Stream with a Drop trait implementation.

Performance improvements:

  • Enable compression on the HTTP connector.

  • Make REMOVE [ TABLE | DATABASE | NAMESPACE ] faster for TiKV and FoundationDB.

  • Repetitive expressions and idioms are no longer re-evaluated.

  • Improve performance of CREATE statements, and record insertion.

  • Improve RocksDB performance and configuration, introducing SURREAL_ROCKSDB_THREAD_COUNT, SURREAL_ROCKSDB_WRITE_BUFFER_SIZE, SURREAL_ROCKSDB_TARGET_FILE_SIZE_BASE, SURREAL_ROCKSDB_MAX_WRITE_BUFFER_NUMBER, SURREAL_ROCKSDB_MIN_WRITE_BUFFER_NUMBER_TO_MERGE, SURREAL_ROCKSDB_ENABLE_PIPELINED_WRITES, SURREAL_ROCKSDB_ENABLE_BLOB_FILES, SURREAL_ROCKSDB_MIN_BLOB_SIZE environment variables.

  • Improve SpeeDB performance and configuration, introducing SURREAL_SPEEDB_THREAD_COUNT, SURREAL_SPEEDB_WRITE_BUFFER_SIZE, SURREAL_SPEEDB_TARGET_FILE_SIZE_BASE, SURREAL_SPEEDB_MAX_WRITE_BUFFER_NUMBER, SURREAL_SPEEDB_MIN_WRITE_BUFFER_NUMBER_TO_MERGE, SURREAL_SPEEDB_ENABLE_PIPELINED_WRITES, SURREAL_SPEEDB_ENABLE_BLOB_FILES, SURREAL_SPEEDB_MIN_BLOB_SIZE environment variables.

  • Improve WebSocket performance, introduce SURREAL_WEBSOCKET_MAX_FRAME_SIZE, SURREAL_WEBSOCKET_MAX_MESSAGE_SIZE, SURREAL_WEBSOCKET_MAX_CONCURRENT_REQUESTS environment variables.

  • Use specific memory allocators depending on OS.

  • Fix memory leak in WebSocket implementation.

  • The type::is::record() function now accepts a second optional table argument, validating the record being - stored on the passed table.

  • Add time::micros(), time::millis() and time::from::nanos functions.

  • Add type::is::none() function.

  • Add object::entries(), object::from_entries(), object::len(), object::keys- () and object::values() functions.

  • Clean paths in the start command and honour ~.

  • CLI: Split results by comment.

  • Add surreal sql welcome message.

  • Add Debugging env flag: SURREAL_INSECURE_FORWARD_SCOPE_ERRORS.

  • Add SURREAL_ROCKSDB_KEEP_LOG_FILE_NUM environment variable (default 20).

  • Support auth levels for basic auth (behind feature flag)

  • Add remainder/modulo operator.

  • Implement string prefixes: s, r, d and u.

  • Add ability to cast string to a Thing/Record ID.

  • Analyzers to support functions.

  • Support of subfields for embedding indexing.

  • Add live query API to Rust SDK.

  • Add Query::with_stats() to return query statistics along with the results.

  • Permissions are now always displayed for visiblity

  • Add a --beta flag to surreal upgrade to make installing the latest beta release easier.

Bug fixes:

  • Fix stack overflow in graph traversal.

  • Bugfix - parse error for invalid leading whitespace.

  • Fix memory leak caused by OTEL callbacks.

  • Fix wrong function name export and function name parsing.

  • The position of the LIMIT and ORDER clauses are now interchangable.

  • Fix index plan for idiom param value.

  • Fix bug where error offset could underflow.

  • Query results should be consistent and representative.

  • Indexes used with the operators CONTAINS [ ANY | ALL ].

  • Forward custom thrown errors in SIGNIN and SIGNUP queries.

  • Fix ORDER BY RAND() failing to parse when selecting specific fields.

  • Fix identifiers which look like numbers failing to parse.

  • Change math::median indexing for even length arrays.

  • Pass IP & Origin onto session used by scope queries.

  • Fix possible corruption of MTree and incomplete knn.

  • Allow array::flatten() to be used as an aggregate function.

  • Make SELECT ONLY deterministic.

  • Optional function arguments should be optional.

  • Default table permissions should be NONE

  • Bugfix: Fix inconsistant record parsing

  • Fix time regression in surrealdb.wasm binaries.

  • Fix computing futures in query conditions.

  • Fix issue with scoring on complex queries.

  • Fix ML support on Windows and enable the feature in Windows binaries.

  • Replace the custom JWT parser causing decoding issues.

  • Ensure compression is only enabled when response is a certain size.

  • Respect alias for dynamic field queries with type::field().

  • Prevent overflow in math::power().

  • Fix error message pointing to wrong character.

  • Expand logic for static value validation to improve DEFAULT clause handling.

  • Fallback to a string when record ID parsing fails.

  • Ensure an attempt to set a protected variable returns an error.

  • Fix duration addition in timeout causing overflow.

  • Fix a bug where a non-empty list parser would parse empty lists.

  • Limit recursion depth when parsing nested RELATE statements.

  • Ensure REMOVE statement does not only remove the first 1000 keys.

  • Fix BTree deletion bug.

  • Replace close method on live::Stream with a Drop trait implementation.

Performance improvements:

  • Enable compression on the HTTP connector.

  • Make REMOVE [ TABLE | DATABASE | NAMESPACE ] faster for TiKV and FoundationDB.

  • Repetitive expressions and idioms are not anymore re-evaluated.

  • Improve performance of CREATE statements, and record insertion.

  • Improve RocksDB performance and configuration, introducing SURREAL_ROCKSDB_THREAD_COUNT, SURREAL_ROCKSDB_WRITE_BUFFER_SIZE, SURREAL_ROCKSDB_TARGET_FILE_SIZE_BASE, SURREAL_ROCKSDB_MAX_WRITE_BUFFER_NUMBER, SURREAL_ROCKSDB_MIN_WRITE_BUFFER_NUMBER_TO_MERGE, SURREAL_ROCKSDB_ENABLE_PIPELINED_WRITES, SURREAL_ROCKSDB_ENABLE_BLOB_FILES, SURREAL_ROCKSDB_MIN_BLOB_SIZE environment variables.

  • Improve SpeeDB performance and configuration, introducing SURREAL_SPEEDB_THREAD_COUNT, SURREAL_SPEEDB_WRITE_BUFFER_SIZE, SURREAL_SPEEDB_TARGET_FILE_SIZE_BASE, SURREAL_SPEEDB_MAX_WRITE_BUFFER_NUMBER, SURREAL_SPEEDB_MIN_WRITE_BUFFER_NUMBER_TO_MERGE, SURREAL_SPEEDB_ENABLE_PIPELINED_WRITES, SURREAL_SPEEDB_ENABLE_BLOB_FILES, SURREAL_SPEEDB_MIN_BLOB_SIZE environment variables.

  • Improve WebSocket performance, introduce SURREAL_WEBSOCKET_MAX_FRAME_SIZE, SURREAL_WEBSOCKET_MAX_MESSAGE_SIZE, SURREAL_WEBSOCKET_MAX_CONCURRENT_REQUESTS environment variables.

  • Use specific memory allocators depending on OS.

  • Fix memory leak in Websocket implementation.

v1.1.0 introduces a new Live Query API to the Rust SDK, for powerful realtime updates in your Rust applications.

// Select the namespace/database to use
db.use_ns("namespace").use_db("database").await?;

// Listen to all updates on a table
let mut stream = db.select("person").live().await?;

// Listen to updates on a range of records
let mut stream = db.select("person").range("jane".."john").live().await?;

// Listen to updates on a specific record
let mut stream = db.select(("person", "h5wxrf2ewk8xjxosxtyc")).live().await?;

// The returned stream implements `futures::Stream` so we can
// use it with `futures::StreamExt`, for example.
while let Some(result) = stream.next().await {
handle(result);
}

// Handle the result of the live query notification
fn handle(result: Result<Notification<Person>>) {
match result {
Ok(notification) => println!("{notification:?}"),
Err(error) => eprintln!("{error}"),
}
}

It was previously impossible to iterate over objects, so we introduced some new functions to make working with object data structures easier.

LET $fruits = {
apple: {
name: "Apple",
stock: 20,
},
banana: {
name: "Banana",
stock: 40,
},
};

LET $num_fruit_types = object::len($fruits);
LET $num_fruit_total = math::sum((
SELECT VALUE stock FROM object::values($fruits)
));

RETURN "We have " + <string> $num_fruit_types + " type of fruits.";
RETURN "We have " + <string> $num_fruit_total + " pieces of fruit in total.";

Strings can optimistically be parsed as Record IDs, Datetimes or as a UUID, if the content matches such a value. With string prefixes you get to decide what value a string holds.

-- Interpeted as a record ID, because of the structure with the semicolon:
RETURN "5:20";
-- Forcefully parsed as just a string
RETURN s"5:20";

-- This will be a record ID.
RETURN r"person:john";
-- This fails, as it's not a valid record ID.
RETURN r"I am not a record ID";

-- Example for a date and a UUID.
RETURN d"2023-11-28T11:41:20.262Z";
RETURN u"8c54161f-d4fe-4a74-9409-ed1e137040c1";

The ONLY clause is sometimes not deterministic. Selecting from an array, table or range with the ONLY clause now requires you to limit the result to 1.

-- Fails, not limited to 1 result and result can contain multiple outputs.
SELECT * FROM ONLY table_name;

-- Works! Resource gets returned, or NONE if no resource was found.
SELECT * FROM ONLY table_name LIMIT 1;

Optional function arguments on custom functions are now actually optional.

DEFINE FUNCTION fn::create::resource($required: string, $optional: option<string>) {
// The $required argument is a string
// The $optional argument is a string or NONE
};

// Previously you needed to pass NONE for optional arguments
fn::create::resource("Required argument", NONE);

// Now you simply omit the argument
fn::create::resource("Required argument");

Release v1.0.2

Released on Dec 21, 2023

Bug fixes:

  • Support connecting to beta servers from the Rust SDK.

Release v1.0.1

Released on Dec 14, 2023

Bug fixes:

  • Add a patch for GHSA-x5fr-7hhj-34j3.

  • Tables defined without explicit permissions have NONE instead of FULL permissions.

  • Table permissions are always explicitly displayed with the INFO FOR DB statement.

Release v1.0.0

Released on Sep 13, 2023

After numerous beta releases crammed into just months of development, we are releasing SurrealDB v1.0.0! 🎉

Here follow some of our 1.0.0 highlights:

v1.0.0 introduces new type validation methods. These new methods allow you to check which type any sort of value is on the go.

LET $value = "I am a string";

RETURN [
type::is::string($value),
type::is::record($value),
type::is::number($value),
];

It was previously difficult to select, create, update or delete just a single record, so we simplified it a bit.

// Will return an object, as we can guarantee just a single record is requested
SELECT * FROM ONLY person:tobie;

// This will throw an error, as multiple records are being created and returned
CREATE ONLY person:tobie, person:jaime;

v1.0.0 introduces Live Queries. This powerful technology allows you to write applications where you can serve realtime updates to your frontend.

// You can initiate the live query in your application
LET $lq = LIVE SELECT * FROM person WHERE age > 18;
// And you can dispose the live query once it is no longer needed
KILL $lq;
// SurrealDB SDKs allow you to easily initiate live queries, and to process the received messages.
const lq = await db.live('person', function ({ action, result }) {
if (action == 'CREATE') {
renderPersonInFrontend(result);
}
});

// Once you no longer need the live feed of updates, you can dispose the live query
await db.kill(lq);

With Full Text Search, you can efficiently store and index data, and search through it.

// We can define an analyzer on our database,
// Then, define an index to dictate which content we want to index (yep, that easy)
DEFINE ANALYZER simple TOKENIZERS blank,class FILTERS snowball(english);
DEFINE INDEX content ON article FIELDS content SEARCH ANALYZER simple BM25 HIGHLIGHTS;

// We can then create some content
CREATE article SET content = "Join us at SurrealDB World, as we unveil our version 1.0.0 to the world!";
CREATE article SET content = "We will absolutely be at Surreal World!";

// And lastly, select some data from the article table.
// We use the special "@num@" operator, where we define a reference.
// We can then reference back in the search::* functions.
SELECT
*,
search::score(1) AS score,
search::offsets(1) AS offsets,
search::highlight('<b>', '</b>', 1) AS highlighted
FROM article WHERE
content @1@ 'world'
ORDER BY score DESC;

All capabilities are disabled by default. This means that by default, you are not able to use any methods, embedded scripting functions, make outbound network calls, or access the database anonymously. Down below follows a set of examples to showcase how one can configure capabilities.

Capabilities are further documented in the Capabilities documentation.

# Allow all capabilities
user@localhost % surreal start --allow-all

# Allow all functions, except for custom functions
user@localhost % surreal start --allow-funcs --deny-funcs fn

# Allow all capabilities, but deny guest/anonymous access to the database
user@localhost % surreal start --allow-all --deny-guests

It is now possible to define multiple root users in SurrealDB. This change did require some changes in the way that you start your database, however.

With this change, you will now only initially have to provide the --user and --pass flags to create the initial root user, but once the first root user exists, they will no longer be utilised.

For more information, check out the Authentication guide, and the surreal start and DEFINE USER documentation.

# When you initially start the database, you can create the first root user.
user@localhost % surreal start --auth --user root --pass root file:database.db

# In the future, you don't need to specify the --user and --pass flags anymore
user@localhost % surreal start --auth file:database.db
// Afterwards, you are able to create multiple root users.
DEFINE USER tobie ON ROOT PASSWORD "SecurePassword!" ROLES OWNER;
DEFINE USER jaime ON ROOT PASSWORD "SecurePassword!" ROLES EDITOR;
DEFINE USER john ON ROOT PASSWORD "SecurePassword!" ROLES VIEWER;

v1.0.0 introduces a more strict and powerful typing system. It makes things more simple to understand, and it goes a long way in preventing all kinds of weird bugs in your schemas!

// Where you previously had to manually assert that a field does not contain NONE or NULL
DEFINE FIELD age ON person TYPE number ASSERT $value != NONE AND $value != NULL;

// You can now simply set the type and be ensured that only a number will be stored in that field.
DEFINE FIELD age ON person TYPE number;

// To make types optional, you can use the newly introduced "option<type>" type.
// This guarantees the field to be either empty (NONE), or a number.
DEFINE FIELD age ON person TYPE option<number>;

// We also introduces some new types!
DEFINE FIELD ratings ON movie TYPE array<number>;
DEFINE FIELD best_ratings ON movie TYPE array<number, 5>;
DEFINE FIELD unique_ratings ON movie TYPE set<number, 5>;

// And we made sure that the record type is also in-line with this new format
DEFINE FIELD author ON book TYPE record<person>;

// Lastly, it is now possible to set a union type.
DEFINE FIELD title ON article TYPE string | number;
// They can also be used in "nested" types
DEFINE FIELD description ON article TYPE option<string | number>;
DEFINE FIELD author ON article TYPE record<person | admin>;
// You previously needed to involve logic with the "VALUE" clause to set a default value.
DEFINE FIELD enabled ON user TYPE bool VALUE $value OR $before OR true;
// You can now simply use the DEFAULT clause for this.
DEFINE FIELD enabled ON user TYPE bool DEFAULT true;

// As an added bonus, when you only define a VALUE clause with a simple value,
// then that will also act as the default value.
DEFINE FIELD constant ON demo VALUE 123;
// This will act the same as the following
DEFINE FIELD constant ON demo VALUE 123 DEFAULT 123;

Scope and anonymous users previously had access to every defined global parameter and function. You can now define these resources with a PERMISSIONS clause to protect them.

DEFINE PARAM $perms_none VALUE 'Nobody can see me' PERMISSIONS NONE;
DEFINE PARAM $perms_full VALUE 'Everybody can see me' PERMISSIONS FULL;
DEFINE PARAM $perms_scope VALUE 'Only admins can see me' PERMISSIONS WHERE $scope = 'admin';

DEFINE FUNCTION fn::perms::none() {
RETURN 'Nobody can invoke me';
} PERMISSIONS NONE;

DEFINE FUNCTION fn::perms::full() {
RETURN 'Everybody can invoke me';
} PERMISSIONS FULL;

DEFINE FUNCTION fn::perms::scope() {
RETURN 'Only admins can invoke me';
} PERMISSIONS WHERE $scope = 'admin';
// Here, we loop through an array with three numbers.
// For any number under two, we skip the iteration
// For any number above two, we break the loop
// The result is that only when the number is two, we will create the "person:two" record.

FOR $num IN [1, 2, 3] {
IF $num < 2 {
CONTINUE;
};

IF $num > 2 {
BREAK;
};

CREATE person:two;
};

// Here, we select the id for every person in the database, and we gift them a ticket to SurrealDB World.
FOR $person IN (SELECT VALUE id FROM person) {
CREATE gift CONTENT {
recipient: $person,
type: "ticket",
event: "SurrealDB World"
};
};

Did something unexpected happen, and do you want to throw an error to the client? Now you can!

LET $message = "Some error message";
THROW "Failed to perform action: " + $message;
// Give extra context about a certain resource
DEFINE TABLE user COMMENT "This table will store users!";

We found the IF ELSE statement to be a bit bulky at times. Now, when you use a block ({}) as the body of the statement, you can skip out on the THEN and END keywords!

// Previously
IF something = true THEN {
RETURN 123;
} END;

// Now
IF something = true {
RETURN 123;
};

With fetch(), query(), value() and basically every SurrealQL function now being available within the embedded scripting functions, they are a very powerful extension to SurrealQL, and can be used to solve complex problems otherwise impossible!

Read more about them in the Embedded scripting functions documentation.

function() {
const page = await fetch('https://google.com');
const people = await surrealdb.query('SELECT * FROM person');
const auth = await surrealdb.value('auth');
const uuid = surrealdb.functions.rand.uuid.v4();
}

SCHEMAFULL and SCHEMALESS functionality can now be used together, suitable for capturing schema-free log data.

// We want to define a SCHEMAFULL table
DEFINE TABLE person SCHEMAFULL;
// But we want one field to allow any content
DEFINE FIELD settings on person TYPE object FLEXIBLE;
// We can then set the field value without a schema
CREATE person:test CONTENT {
settings: {
nested: {
object: {
thing: 'test'
}
}
}
};

It is now possible to run blocks of code, with support for an arbitrary number of statements, including LET and RETURN statements. This allows for writing advanced custom logic, and allowing for more complicated handling of data operations.

DEFINE FIELD average_sales ON metrics VALUE {
LET $sales = (SELECT VALUE quantity FROM sales);
LET $total = math::sum($sales);
LET $count = count($sales);
RETURN ($total / $count);
};

SurrealDB now supports the ability to define global database-wide custom functions, which allow for complicated or repeated user-defined code, to be run seamlessly within any query across the database. Custom functions support typed arguments, and multiple nested queries with custom logic.

-- 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');

Release v0.3.0

Released on Dec 14, 2021

Features:

  • Enable query and session parameters to be defined on a JSON-RPC connection

  • Ensure subqueries can access encoding parent query and grand-parent queries

  • Add diff-match-patch functionality when updating document records

  • Separate authentication levels for Namespace and Database specific access

  • Authentication scope definition and setup, with user-defined authentication logic for each scope

Aggregate views let you pre-compute analytics queries as data is written to SurrealDB. Similarly to an index, a table view lets you select, aggregate, group, and order data, with support for moving averages, time-based windowing, and attribute-based counting. Pre-defined aggregate views are efficient and performant, with only a single record modification being made for every write.

-- 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)
;

Release v0.2.0

Released on Jan 21, 2021

Features:

  • Parameters can be used to store values or result sets

  • Nested subquery functionality, with scoped parameters

  • Nested field query notation allowing nested arrays and objects to be queried

  • Mathematical operators for complex mathematical calculations in queries

  • Advanced functions for working with arrays, strings, time, validation, parsing, and counting

Release v0.1.0

Released on Dec 8, 2019

Features:

  • Multi-tenancy data separation, with namespaces and databases

  • Schemafull or schemaless tables with limitless document fields

  • Multi-table, multi-row, serialisable ACID transactions

  • Table fields, table change events, table indexes, and data constraints

  • Advanced data model including empty values, strings, numbers, objects, arrays, durations, and datetimes