<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: SurrealDB</title>
    <description>The latest articles on Forem by SurrealDB (@surrealdb).</description>
    <link>https://forem.com/surrealdb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F4795%2F6422ffbe-ae06-43aa-8dc1-bfb088408cc0.png</url>
      <title>Forem: SurrealDB</title>
      <link>https://forem.com/surrealdb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/surrealdb"/>
    <language>en</language>
    <item>
      <title>Exponential cost traps in database architectures: how SurrealDB breaks the cycle</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Thu, 02 Apr 2026 12:33:01 +0000</pubDate>
      <link>https://forem.com/surrealdb/exponential-cost-traps-in-database-architectures-how-surrealdb-breaks-the-cycle-4ane</link>
      <guid>https://forem.com/surrealdb/exponential-cost-traps-in-database-architectures-how-surrealdb-breaks-the-cycle-4ane</guid>
      <description>&lt;h2&gt;
  
  
  Exponential cost traps in database architectures: how SurrealDB breaks the cycle
&lt;/h2&gt;

&lt;p&gt;In the fast-paced world of application development, database choices can make or break an organisation's scalability and budget. Traditional polyglot persistence (relying on multiple specialised databases) often leads to exponential cost growth as systems expand. What starts as a simple setup spirals into a complex, inefficient infrastructure riddled with data duplication, ETL pipelines, and operational silos. SurrealDB, with its multi-model architecture unifying document, graph, vector, full-text, time-series, geospatial, and relational capabilities, offers a consolidated alternative that eliminates these pitfalls. By storing data once and querying it across models, SurrealDB can reduce overall costs by multiple factors, particularly in storage-heavy environments where cloud block storage is in use.&lt;/p&gt;

&lt;p&gt;To understand the value of consolidation, consider the common trajectory of database sprawl. Below, we explore a real-world-inspired story of how costs escalate exponentially, helping readers identify if their setup is heading toward inefficiency. This narrative highlights the compounding effects of architectural decisions, setting the stage for how SurrealDB provides a streamlined, cost-effective path forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  A story of exponentially increasing costs: from simplicity to sprawl
&lt;/h2&gt;

&lt;p&gt;Many teams begin with modest needs but face rapid growth that exposes the limitations of single-model databases. Let's trace a typical app's evolution, illustrating how costs outpace traffic due to added complexity, redundancy, and management overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Launching the app: a simple start
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sentiment: "We start simple: a single database, fast and cheap to run."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your app is new, with straightforward transactional requirements. You deploy a single MySQL instance on AWS, adding one replica for high availability. It's low cost and easy to manage. Database expenses are minimal, infrastructure is straightforward, and personnel needs are light.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database Costs:&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic Handled:&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra Cost:&lt;/td&gt;
&lt;td&gt;Minimal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personnel Required:&lt;/td&gt;
&lt;td&gt;1–2 engineers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temp cost:&lt;/td&gt;
&lt;td&gt;None yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity cost:&lt;/td&gt;
&lt;td&gt;None yet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MySQL (Primary)&lt;/li&gt;
&lt;li&gt;MySQL (Failover Replica)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. First wave of growth: scaling reads
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sentiment: "Our first scaling challenge is reads. Replicas help, but cost growth slightly outpaces traffic."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;User traffic surges, dominated by reads. You add 8 read replicas and scale the writer to a larger instance (where per-vCPU costs rise disproportionately). Costs begin increasing slightly faster than traffic due to over-provisioning and load balancing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database costs:&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic handled:&lt;/td&gt;
&lt;td&gt;Increased&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra cost:&lt;/td&gt;
&lt;td&gt;Rising&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personnel required:&lt;/td&gt;
&lt;td&gt;Still low, but monitoring grows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temp cost:&lt;/td&gt;
&lt;td&gt;None yet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity cost:&lt;/td&gt;
&lt;td&gt;None yet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MySQL Writer&lt;/li&gt;
&lt;li&gt;Load Balancer&lt;/li&gt;
&lt;li&gt;8x MySQL Read Replicas&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Write traffic explodes: sharding begins
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sentiment: "As writes explode, we shard the system, but migrations are expensive and disruptive."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Writes overwhelm the single writer. You shard into four MySQL instances, over-provisioning each for spikes. A data access layer handles routing by key, and the migration temporarily doubles costs. Complexity introduces temporary and ongoing expenses.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database costs:&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic handled:&lt;/td&gt;
&lt;td&gt;Substantial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra cost:&lt;/td&gt;
&lt;td&gt;Accelerating&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personnel required:&lt;/td&gt;
&lt;td&gt;Increasing (sharding expertise needed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temp cost:&lt;/td&gt;
&lt;td&gt;Spike from migration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity cost:&lt;/td&gt;
&lt;td&gt;Emerging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4x MySQL Shards (each with Writer + Multiple Read Replicas)&lt;/li&gt;
&lt;li&gt;Load Balancers&lt;/li&gt;
&lt;li&gt;CDC (Change Data Capture) for transitioning from old architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Analytics demands: the first pipeline
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sentiment: "Analytics needs have driven us to add a pipeline and duplicate data with added complexity as a result.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Product teams demand dashboards and real-time insights. You build an Extract, Transform, Load (ETL) pipeline to an analytical database such as ClickHouse, duplicating data via Change Data Capture (CDC). Applications now query multiple systems, and engineers must manage query routing while maintaining consistency across stores.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database costs:&lt;/td&gt;
&lt;td&gt;Very high&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic handled:&lt;/td&gt;
&lt;td&gt;Advanced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra cost:&lt;/td&gt;
&lt;td&gt;Ballooning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personnel required:&lt;/td&gt;
&lt;td&gt;Still increasing (pipeline maintenance)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temp cost:&lt;/td&gt;
&lt;td&gt;Pipeline setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity cost:&lt;/td&gt;
&lt;td&gt;Significant&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existing MySQL Shards&lt;/li&gt;
&lt;li&gt;CDC + ETL&lt;/li&gt;
&lt;li&gt;ClickHouse&lt;/li&gt;
&lt;li&gt;Application querying multiple databases&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. AI ambitions: vector database enters
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sentiment: "AI ambitions require another new system, and more pipelines. The architecture is growing fast."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI agents need RAG (Retrieval-Augmented Generation) support, querying across systems. You spin up a vector DB like Weaviate or Pinecone, with new pipelines for replication. Data is consolidated in S3 for training, adding sharding as usage grows.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database costs:&lt;/td&gt;
&lt;td&gt;Extreme&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic handled:&lt;/td&gt;
&lt;td&gt;AI-enhanced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra cost:&lt;/td&gt;
&lt;td&gt;Exponential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personnel required:&lt;/td&gt;
&lt;td&gt;Specialised teams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temp cost:&lt;/td&gt;
&lt;td&gt;AI integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity cost:&lt;/td&gt;
&lt;td&gt;Overwhelming&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existing Setup&lt;/li&gt;
&lt;li&gt;Additional ETL + Kafka&lt;/li&gt;
&lt;li&gt;Weaviate&lt;/li&gt;
&lt;li&gt;S3 for data lakes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Keeping it all running: growing ops
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Sentiment: "Now we need a large ops team just to stay afloat. Costs are scaling exponentially, not linearly."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ongoing tasks include re-sharding MySQL, scaling MongoDB/Weaviate/ClickHouse, upgrades across systems, node recoveries, and pipeline fixes. Teams manage credentials, security, and optimisations in isolation. Ops cycles dominate, slowing innovation and ballooning personnel costs.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;th&gt;​&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database costs:&lt;/td&gt;
&lt;td&gt;Off the charts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traffic handled:&lt;/td&gt;
&lt;td&gt;Peak&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra cost:&lt;/td&gt;
&lt;td&gt;Exponential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personnel required:&lt;/td&gt;
&lt;td&gt;Large teams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Temp cost:&lt;/td&gt;
&lt;td&gt;Constant disruptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complexity cost:&lt;/td&gt;
&lt;td&gt;Debilitating&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All systems with balancing, re-sharding, upgrades, and recoveries&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why the cost curve went exponential
&lt;/h3&gt;

&lt;p&gt;Exponential costs are rarely caused by user growth alone. Instead, they often stem from an architecture that acts as a tax on every new login. Between the headaches of manual sharding, the hidden drain of data duplication, and the hours lost to rigid schema changes, many teams end up subsidizing a fragmented mess. If your infrastructure spend is climbing faster than your traffic, you are leaking resources rather than scaling. Moving toward a unified database simplifies the stack and returns your cost curve to a manageable, linear state.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hidden costs of database sprawl: ETL, layers, and operational overhead
&lt;/h2&gt;

&lt;p&gt;Building on this story, polyglot architectures create fragile ETL pipelines for data movement between systems like MySQL and ClickHouse. These demand resources for processing, debugging, and schema handling. Operational burdens include multiple query languages, fragmented backups, and custom synchronisation.&lt;/p&gt;

&lt;p&gt;SurrealDB eliminates this by natively supporting all models in one platform with SurrealQL. No ETL needed. Data stays unified, reducing dependencies and accelerating development, as seen in replacements of PostgreSQL + Neo4j + RabbitMQ stacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  EBS storage: the #1 cost driver in self-hosted databases
&lt;/h2&gt;

&lt;p&gt;As the story shows, EBS volumes (and Azure and GCP’s equivalent) multiply with replicas and duplications, becoming the dominant expense. With EBS volumes not being eligible for discounts, costs compound in sprawls: 3x replication per cluster, plus copies across databases, over-provisioning, and snapshots typically result in massive redundant data duplication&lt;/p&gt;

&lt;p&gt;For example, in a 50-node cluster with 4TB EBS volumes on AWS, storage will be $28,098 monthly, over twice the $12,577 for EC2 (factoring in discounts). Polyglot setups exacerbate this exponential growth.&lt;/p&gt;

&lt;h2&gt;
  
  
  SurrealDB's compression edge: reducing data size by 1/3 for dramatic savings
&lt;/h2&gt;

&lt;p&gt;SurrealDB's storage engine (TiKV) delivers 70-80% better compression than PostgreSQL's, typically shrinking datasets to one-third. For the 4TB example, this cuts EBS to ~1.33TB, dropping costs to under $10,000, a 65% savings. In addition to the raw savings, smaller data reduces I/O, snapshots, and data transfer cost, countering the sprawl's redundancy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eliminating data duplication: organization-wide cost reductions
&lt;/h2&gt;

&lt;p&gt;Storing data once avoids multi-system duplication. A single cluster with 3× replication replaces the primary and secondary copies spread across multiple database systems. Eliminating ETL removes staging costs, while unified data views simplify compliance and backups, reducing S3 storage and egress fees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linear scalability: ensuring cost-effective growth
&lt;/h2&gt;

&lt;p&gt;Unlike most other database systems, SurrealDB scales linearly via TiKV, with less than a 5% performance deviation coefficient per node. You get roughly the same increase in capability and performance when adding a node to a 9-node cluster as when adding a node to a 100-node cluster. This keeps costs predictable and prevents exponential cost growth which is typical in sprawling platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: escape the exponential trap with SurrealDB
&lt;/h2&gt;

&lt;p&gt;The story of sprawl reveals how polyglot decisions lead to exponential costs through duplication, complexity, and ops overhead. SurrealDB's "one database to rule them all" reverses this, slashing EBS-driven expenses via consolidation and compression. For teams in inefficient systems, migrating promises simplicity and transformative savings.&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>database</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>SurrealMX: In-memory storage with time travel and persistent storage</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Thu, 26 Mar 2026 15:32:42 +0000</pubDate>
      <link>https://forem.com/surrealdb/surrealmx-in-memory-storage-with-time-travel-and-persistent-storage-5ahp</link>
      <guid>https://forem.com/surrealdb/surrealmx-in-memory-storage-with-time-travel-and-persistent-storage-5ahp</guid>
      <description>&lt;p&gt;Author: &lt;a href="https://x.com/mithridates" rel="noopener noreferrer"&gt;Dave MacLeod&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SurrealMX: SurrealDB's new in-memory storage backend
&lt;/h2&gt;

&lt;p&gt;There have been quite a few blog posts since the release of SurrealDB 3.0 to introduce each of the new features we have to show. Today's post is about one called SurrealMX that you likely have yet to hear about - but use every day.&lt;/p&gt;

&lt;p&gt;That's because the way you use it is...by typing &lt;code&gt;surreal start&lt;/code&gt;. That's it!&lt;/p&gt;

&lt;p&gt;That's because SurrealMX is in fact the name of SurrealDB's new in-memory datastore. And since SurrealDB stores data in memory by default, 100% of SurrealDB's users have already given it a try.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is SurrealMX?
&lt;/h2&gt;

&lt;p&gt;SurrealMX is SurrealDB's in-memory storage engine that was built from the ground up a bit over a year ago. It was merged into the core database &lt;a href="https://github.com/surrealdb/surrealdb/pull/6581" rel="noopener noreferrer"&gt;during the 3.0 alpha period last November&lt;/a&gt; with no fanfare at all in order to try it out for the first time as SurrealDB's in-memory storage engine. Since SurrealDB uses in-memory storage by default, that means that SurrealMX has been every 3.x user's default storage option since then.&lt;/p&gt;

&lt;p&gt;But SurrealMX isn't just a more performant way to store data in memory, it comes with new optional functionality too.&lt;/p&gt;

&lt;p&gt;If you pay close attention to the SurrealDB source code you might have noticed a PR merged &lt;a href="https://github.com/surrealdb/surrealdb/pull/6882" rel="noopener noreferrer"&gt;three weeks ago&lt;/a&gt; in the runup to 3.0 general availability that included a new way to pass in configuration parameters when starting a database.&lt;/p&gt;

&lt;p&gt;If you go to &lt;a href="https://surrealdb.com/docs/surrealdb/cli/start#datastore-configuration" rel="noopener noreferrer"&gt;this part of the documentation&lt;/a&gt; for the &lt;code&gt;surreal start&lt;/code&gt; command, you can see a comparison of how this worked in 2.x versions compared to 3.0.&lt;/p&gt;

&lt;p&gt;In 2.x versions, specifying whether SurrealKV is versioned or not was done before the path to the database which led to either writing &lt;code&gt;surrealkv&lt;/code&gt; or &lt;code&gt;surrealkv+versioned&lt;/code&gt;. Other backends didn't have similar configurations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;surrealkv://my_db
rocksdb://my_db
surrealkv://mydb
surrealkv+versioned://mydb
memory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In 3.0, this has now been changed to pass as many configurations as needed &lt;em&gt;after&lt;/em&gt; the path to the database, in the same way that you do the same with a URL. Here are two examples from the documentation showing how this is done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"surrealkv://path/to/db?versioned=true&amp;amp;sync=every&amp;amp;retention=30d"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"mem://tmp/data?versioned=true&amp;amp;aol=sync&amp;amp;snapshot=60s&amp;amp;sync=5s"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="s2"&gt;"mem://?versioned=true"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Did you notice that &lt;code&gt;versioned=true&lt;/code&gt; is found not just in the &lt;code&gt;surrealkv&lt;/code&gt; but also the &lt;code&gt;mem&lt;/code&gt; backend? That's because SurrealMX includes versioned queries in the same way that SurrealKV does.&lt;/p&gt;

&lt;p&gt;Let's take a closer look!&lt;/p&gt;

&lt;h2&gt;
  
  
  Time travel using the VERSION clause
&lt;/h2&gt;

&lt;p&gt;With the addition of SurrealMX, SurrealDB now has two storage backgrounds that have versioning: SurrealKV and memory. Versioning is the capability to not just query a table as it stands now, but as it stood before. This feature is exceptionally important in areas like legal compliance in which a database needs to keep past information intact even if it has been overwritten later on.&lt;/p&gt;

&lt;p&gt;In SurrealDB, time travel queries are done using the &lt;a href="https://surrealdb.com/docs/surrealql/statements/select#the-version-clause" rel="noopener noreferrer"&gt;&lt;code&gt;VERSION&lt;/code&gt;&lt;/a&gt; keyword. Let's give this a quick try with the in-memory backend. First we will start a server with &lt;code&gt;?versioned=true&lt;/code&gt; added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal start &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; secret &lt;span class="s2"&gt;"mem://?versioned=true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then connect via &lt;a href="https://surrealdb.com/docs/surrealist" rel="noopener noreferrer"&gt;Surrealist&lt;/a&gt; or this command in the CLI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal sql &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that is done, we can create a &lt;code&gt;person&lt;/code&gt; record or two and make some queries using &lt;code&gt;VERSION&lt;/code&gt; followed by &lt;code&gt;time::now()&lt;/code&gt; minus how ever long ago we would like to query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;g4z4v7yoju9pe21jck6v&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;g4z4v7yoju9pe21jck6v&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="k"&gt;No&lt;/span&gt; &lt;span class="s1"&gt;'person'&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="n"&gt;existed&lt;/span&gt; &lt;span class="n"&gt;yesterday&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="k"&gt;VERSION&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="k"&gt;Add&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;second&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;[[{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;aczudhy0mvef5bxurcry&lt;/span&gt; &lt;span class="p"&gt;}]]&lt;/span&gt;

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="s1"&gt;'person'&lt;/span&gt; &lt;span class="n"&gt;has&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;but&lt;/span&gt; &lt;span class="n"&gt;about&lt;/span&gt; &lt;span class="n"&gt;ten&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt; &lt;span class="n"&gt;ago&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="n"&gt;had&lt;/span&gt; &lt;span class="n"&gt;just&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="k"&gt;VERSION&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;g4z4v7yoju9pe21jck6v&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the &lt;code&gt;VERSION&lt;/code&gt; keyword has been available via SurrealKV since SurrealDB 2.0, it's very likely that you are already quite familiar with it. Let's now move on to in-memory persistent storage options which were entirely unavailable before SurrealDB 3.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  In-memory with optional persistent storage and versioning
&lt;/h2&gt;

&lt;p&gt;In-memory storage with optional persistent storage is one of the features of Redis that has made it so popular. SurrealMX offers this as well, via a somewhat more composable model.&lt;/p&gt;

&lt;p&gt;Persistence for the memory backend is used for the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Append-Only Log (AOL) - Synchronous/asynchronous modes for durability&lt;/li&gt;
&lt;li&gt;Snapshots - Periodic full database state capture&lt;/li&gt;
&lt;li&gt;Data Recovery - Automatic recovery from snapshots + AOL on startup&lt;/li&gt;
&lt;li&gt;AOL Truncation - Automatic cleanup after snapshots&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may be curious at this point how this differs from using SurrealKV (or RocksDB) to store your data. The difference is that SurrealMX persistent storage does not compress the data. It is an append-only-log of database writes, never compacted, until a snapshot occurs. It is in effect an entire database export. You can think of it as a convenience on top of in-memory speed while SurrealKV is used for traditional durable storage for datasets of any size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying it out
&lt;/h2&gt;

&lt;p&gt;Let's grab part of the documentation to see how it works. These are the available parameters to use when starting a database with in-memory storage.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;retention&lt;/code&gt; (a duration string, e.g. 30d, 24h, 30m)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aol&lt;/code&gt; (never, sync, or async), for writing changes to an append-only log file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;snapshot&lt;/code&gt; (a duration string, e.g. 60s), for periodically writing a snapshot of the database to the file system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sync&lt;/code&gt; (never, every, or a duration string like 5s), for specifying when to flush the append-only log file to the file system&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;never&lt;/code&gt; - (default) leave flushing to the OS (least durable)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;every&lt;/code&gt; - sync on every commit (most durable)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;interval&lt;/code&gt; - periodic background flushing at the given interval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can match this up against the example above which shows that &lt;code&gt;aol&lt;/code&gt; is set to &lt;code&gt;sync&lt;/code&gt;, &lt;code&gt;snapshot&lt;/code&gt; is set to &lt;code&gt;60s&lt;/code&gt;, and &lt;code&gt;sync&lt;/code&gt; is set to &lt;code&gt;5s&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="s2"&gt;"mem://tmp/data?versioned=true&amp;amp;aol=sync&amp;amp;snapshot=60s&amp;amp;sync=5s"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's quite a bit to think about, but fortunately the SurrealMX repo has a table that lays out exactly when you would want to use one configuration over another. Each of these is balanced against the performance hit, ranging from no persistence at all (the fastest) to Sync AOL + Every fsync which survives both process and system crash at the greatest cost to performance.&lt;/p&gt;

&lt;p&gt;To make a choice, just ask yourself the following as you move down the chart: what do you want to happen to your data if the process or system crashes? Then choose the most performant option that offers you the guarantees you need.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Survives Process Crash&lt;/th&gt;
&lt;th&gt;Survives System Crash&lt;/th&gt;
&lt;th&gt;Performance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No persistence&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;Fastest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshot-only&lt;/td&gt;
&lt;td&gt;⚠️ (last snapshot)&lt;/td&gt;
&lt;td&gt;⚠️ (last snapshot)&lt;/td&gt;
&lt;td&gt;Fastest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async AOL + No fsync&lt;/td&gt;
&lt;td&gt;⚠️ (mostly)&lt;/td&gt;
&lt;td&gt;⚠️ (mostly + OS buffers)&lt;/td&gt;
&lt;td&gt;Very fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async AOL + Interval fsync&lt;/td&gt;
&lt;td&gt;⚠️ (mostly)&lt;/td&gt;
&lt;td&gt;⚠️ (mostly + since last fsync)&lt;/td&gt;
&lt;td&gt;Very fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async AOL + Every fsync&lt;/td&gt;
&lt;td&gt;⚠️ (mostly)&lt;/td&gt;
&lt;td&gt;⚠️ (mostly)&lt;/td&gt;
&lt;td&gt;Very fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync AOL + No fsync&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ (OS buffers)&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync AOL + Interval fsync&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ (since last fsync)&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync AOL + Every fsync&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Looks like the quickest and easiest way to use persistent storage is through periodic snapshots. Since these must be set to a number at least greater than 30 seconds, we'll give it a try with 31.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal start &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; secret &lt;span class="s2"&gt;"mem://tmp/data?snapshot=31s"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then connect with Surrealist or the &lt;code&gt;surreal sql --user root --pass secret&lt;/code&gt; command and do a &lt;code&gt;CREATE person&lt;/code&gt; or two.&lt;/p&gt;

&lt;p&gt;After that, use &lt;code&gt;cd tmp/data&lt;/code&gt; to go into the folder and wait for the file to show up. After 30 seconds you should see a file called &lt;code&gt;snapshot.bin&lt;/code&gt;. That's the snapshot!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;!v�4��y��
         /!cgdefault�?H/�y��mainmain/!ndI��� H�
��t����4P���I��� H�
��t��@~����D�&amp;amp;���I��� H�
...snip
*/***person*rh6xmo5z8cqffvp4hbf6��BQ7
                                     ��
/***user*john��T|��
nameJohn%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that file is present, you can stop and restart the database, use &lt;code&gt;SELECT * FROM person&lt;/code&gt; and see that the &lt;code&gt;person&lt;/code&gt; records are still there. Not bad at all!&lt;/p&gt;

&lt;p&gt;We suspect that the most popular options will be the following four:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Snapshot only: the fastest option&lt;/li&gt;
&lt;li&gt;Async AOL + no fsync: the fastest option that tracks every change the moment one happens&lt;/li&gt;
&lt;li&gt;Sync AOL + no fsync: the fastest option if you think you might have a process crash&lt;/li&gt;
&lt;li&gt;Sync AOL + every fsync: the most durable option, survives both process and systems crashes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But that remains to be seen.&lt;/p&gt;

&lt;p&gt;If you've given persistent in-memory storage a try already or have used it since the publication of this blog post, please get in touch and let us know!&lt;/p&gt;

</description>
      <category>inmemorystorage</category>
      <category>surrealdb</category>
      <category>surrealmx</category>
      <category>database</category>
    </item>
    <item>
      <title>Where SurrealDB fits in your stack</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Tue, 17 Mar 2026 09:02:54 +0000</pubDate>
      <link>https://forem.com/surrealdb/where-surrealdb-fits-in-your-stack-2dh3</link>
      <guid>https://forem.com/surrealdb/where-surrealdb-fits-in-your-stack-2dh3</guid>
      <description>&lt;h2&gt;
  
  
  Where SurrealDB fits in your stack: a path to multi-model efficiency
&lt;/h2&gt;

&lt;p&gt;In today's data-driven applications, managing multiple specialised databases (often referred to as polyglot persistence) has become a common challenge. Teams juggle relational databases like PostgreSQL for structured data, document stores like MongoDB for flexible schemas, graph databases like Neo4j for relationships, and vector databases for AI workloads.&lt;/p&gt;

&lt;p&gt;While powerful once finally set up, this configuration introduces complexity: multiple query languages, data pipelines, security models, and scaling strategies. Enter SurrealDB, an open-source multi-model database that unifies these paradigms into a single system. By consolidating databases into one platform, SurrealDB enables applications with multi-model capabilities, blending relational queries, graph traversals, vector search, time-series and geospatial analysis without the overhead of disparate tools.&lt;/p&gt;

&lt;p&gt;This article explores how SurrealDB facilitates database consolidation, drawing from real-world benchmarks, case studies (e.g., Tencent &lt;a href="https://surrealdb.com/customer/tencent" rel="noopener noreferrer"&gt;consolidating nine databases into one&lt;/a&gt;), and performance comparisons. We'll map current polyglot setups to a consolidated future state, discuss ROI and TCO implications, and provide architecture overviews. If you're a technical evaluator considering SurrealDB as a replacement for your existing stack, this guide assumes basic database knowledge but no deep familiarity with SurrealDB's SurrealQL query language or internals.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where SurrealDB fits in your deployment
&lt;/h2&gt;

&lt;p&gt;SurrealDB positions itself as a versatile, AI-native database that scales from edge devices to petabyte clusters. &lt;a href="https://surrealdb.com/blog/why-we-are-betting-on-rust" rel="noopener noreferrer"&gt;Built in Rust&lt;/a&gt;, it supports deployment as an embedded library, single-node server, or distributed system. Its layered architecture separates compute (query processing) from storage (e.g., using TiKV for distributed persistence), allowing flexible scaling.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it replaces
&lt;/h3&gt;

&lt;p&gt;SurrealDB's multi-model design natively handles multiple data paradigms, making it a direct substitute for several specialised databases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Document stores (e.g., &lt;a href="https://surrealdb.com/docs/surrealdb/migrating/mongodb" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt;)&lt;/strong&gt;: SurrealDB stores data as flexible, JSON-like documents, supporting a default schemaless model that allows as much strictness to be added to your schema as your solution requires. It replaces MongoDB for content management, real-time apps, and unstructured data, with SurrealQL providing aggregation and querying akin to MongoDB's syntax but unified across models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graph databases (e.g., &lt;a href="https://surrealdb.com/docs/surrealdb/migrating/neo4j" rel="noopener noreferrer"&gt;Neo4j&lt;/a&gt;)&lt;/strong&gt;: with built-in graph functionality, SurrealDB uses record links and graph edges as first-class citizens for traversals and relationships. It has been used to replace Neo4j in recommendation engines, fraud detection, and knowledge graphs, offering SQL-like syntax for graph queries without Cypher's learning curve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relational databases (e.g., &lt;a href="https://surrealdb.com/docs/surrealdb/migrating/postgresql" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt;)&lt;/strong&gt;: SurrealDB emulates relational tables with ACID transactions, indexes, and joins (via record or graph links rather than traditional SQL JOINs for better scalability). It consolidates Postgres workloads for transactional data, geospatial queries, and time-series.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vector and Search databases&lt;/strong&gt;: integrated vector embeddings, full-text search, and hybrid retrieval replace standalone tools like Pinecone or Elasticsearch for AI apps, including RAG (Retrieval-Augmented Generation) pipelines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key-Value and Time-Series stores (e.g., Redis, InfluxDB)&lt;/strong&gt;: SurrealDB handles simple key-value pairs and temporal data with live queries for real-time updates along with an in-memory datastore that also includes &lt;a href="https://surrealdb.com/docs/surrealdb/cli/start#datastore-configuration" rel="noopener noreferrer"&gt;persistent snapshot or AOL storage&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://surrealdb.com/casestudies" rel="noopener noreferrer"&gt;Case studies&lt;/a&gt; highlight replacements: one company swapped Neo4j, RabbitMQ, and Postgres for SurrealDB, while Tencent consolidated nine systems, reducing complexity and costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it does not replace
&lt;/h3&gt;

&lt;p&gt;While versatile, SurrealDB isn't a one-size-fits-all solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pure OLAP or data warehouses (e.g., Snowflake, BigQuery)&lt;/strong&gt;: for massive analytical workloads requiring columnar storage and distributed joins across exabytes, SurrealDB's focus on multi-model OLTP (online transaction processing) may not match specialised OLAP tools' optimisation for ad-hoc queries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highly specialised hardware-accelerated databases&lt;/strong&gt;: it doesn't replace niche systems like GPU-optimised databases for extreme-scale ML training or legacy mainframe databases with proprietary integrations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-database tools&lt;/strong&gt;: SurrealDB consolidates data storage but not orchestration (e.g., Kubernetes).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Outcome
&lt;/h3&gt;

&lt;p&gt;A single SurrealDB instance or cluster handles all, with SurrealQL as the query interface. Compute nodes process queries, storage persists data distributively.&lt;/p&gt;

&lt;p&gt;Benchmarks show efficiency: SurrealDB often outperforms single-model databases in mixed workloads (e.g., faster than MongoDB in reads, comparable to Neo4j in graphs).&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping the customer journey into consolidation: ROI and TCO over time
&lt;/h2&gt;

&lt;p&gt;Adopting SurrealDB follows a phased journey, yielding compounding ROI (return on investment) and reduced TCO (total cost of ownership).&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: evaluation (months 1-2)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Assess: map current databases to SurrealDB models using migration guides (e.g., from Neo4j/PostgreSQL) and Surreal Sync migration tool.&lt;/li&gt;
&lt;li&gt;POC: run SurrealDB in a testing environment as the backend for a single application; test key validation areas such as:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Setup and integration&lt;/strong&gt;: install SurrealDB locally or via SurrealDB Cloud, in your application stack, and verify connectivity with existing microservices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data migration&lt;/strong&gt;: import sample datasets from your current databases and test schema definitions in schemaless or schemafull modes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query performance&lt;/strong&gt;: execute mixed-model queries (e.g., combining document lookups with graph traversals and vector searches) and benchmark against your existing setup for read/write throughput, latency, and resource usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feature validation&lt;/strong&gt;: test real-time capabilities like live queries and change feeds; evaluate AI features such as vector embeddings and hybrid search for RAG use cases; assess geospatial and time-series handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security and compliance&lt;/strong&gt;: configure authentication (e.g., JWTs, scopes), test access controls, and ensure alignment with your security best practices like least privilege.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability and reliability&lt;/strong&gt;: simulate load with tools like the SurrealDB CLI or Surrealist dashboard, test failover in a small cluster, and monitor performance metrics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer experience&lt;/strong&gt;: use the Surrealist dashboard for querying and visualisation; experiment with SurrealQL syntax to rewrite existing queries from MongoDB/Neo4j/PostgreSQL.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Initial ROI: is higher than baseline due to the overhead on the team doing the POC/migration, and temporarily running 2 different databases in parallel until full cut-over.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 2: partial consolidation (months 3-12)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Migrate one workload (e.g., replace MongoDB for documents).&lt;/li&gt;
&lt;li&gt;Integrate with AI frameworks (e.g., LangChain for RAG).&lt;/li&gt;
&lt;li&gt;ROI: faster feature iteration performance improvements and lower overhead. TCO: 10-50% reduction by eliminating 2-3 databases, cutting licensing/maintenance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Phase 3: full multi-model shift (year 1+)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Consolidate all: from polyglot deployment model to a unified stack.&lt;/li&gt;
&lt;li&gt;Scale: horizontal clustering for high availability.&lt;/li&gt;
&lt;li&gt;ROI: 2-3x faster time-to-market; AI apps ship quicker with native vectors/graphs. TCO: Up to 70% savings via fewer systems, no ETL, and simplified ops (e.g., one monitoring setup).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Over time, ROI grows exponentially as complexity drops: enterprises report 3-4x lower costs through consolidation than managing 3-4+ databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SurrealDB offers a compelling consolidation path for technical teams, replacing multiple databases while supporting multi-model apps. By mapping your current polyglot setup to its unified models, you can achieve significant ROI through faster development and lower TCO. Start with a POC to evaluate fit. Learn what companies like Tencent, Walmart and Samsung already know. For more, explore SurrealDB's &lt;a href="https://surrealdb.com/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; or &lt;a href="https://github.com/surrealdb/surrealdb" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, or visit our &lt;a href="https://discord.gg/surrealdb" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>database</category>
      <category>multimodal</category>
      <category>multimodel</category>
    </item>
    <item>
      <title>OpenAI’s Postgres Architecture: A Brilliant Fix for a Billion-Dollar Mistake</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Wed, 11 Mar 2026 23:33:25 +0000</pubDate>
      <link>https://forem.com/surrealdb/openais-postgres-architecture-a-brilliant-fix-for-a-billion-dollar-mistake-1g65</link>
      <guid>https://forem.com/surrealdb/openais-postgres-architecture-a-brilliant-fix-for-a-billion-dollar-mistake-1g65</guid>
      <description>&lt;p&gt;Author: &lt;a href="https://www.linkedin.com/in/mpenaroza/" rel="noopener noreferrer"&gt;Matthew Penaroza&lt;/a&gt;, Head of AI Solution Architecture at SurrealDB&lt;/p&gt;

&lt;p&gt;OpenAI’s &lt;a href="https://openai.com/index/scaling-postgresql/" rel="noopener noreferrer"&gt;write-up on scaling PostgreSQL&lt;/a&gt; for ChatGPT is worth reading twice: once as an operations playbook, and once as a case study in what happens when a system outgrows its original shape. The engineering is excellent. But the database they started with leaves a lot to be desired and has almost certainly cost the company millions of dollars in completely unnecessary infrastructure pain.&lt;/p&gt;

&lt;p&gt;What I’ve noticed on social media is that many people are treating this article as proof of how amazing the engineering is at OpenAI (which it is) and as a blueprint for how their own organizations should scale. I’d argue the opposite. This architecture is a lesson in what not to do. Just because brilliant engineers can make something work doesn’t mean you should. I wouldn’t eat soup with a fork, and I wouldn't use a single-writer PostgreSQL cluster to serve 800 million users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this architecture exists (and why it shouldn't)
&lt;/h3&gt;

&lt;p&gt;The scaling strategies OpenAI used here are very familiar to me. I've helped design the database architecture for some of the largest transactional systems on the planet, and what OpenAI did mirrors patterns I’ve seen in the field. What’s unusual is the timing. Most companies I’ve seen running this exact playbook are 2010s organizations that started on MySQL or Postgres before the distributed-database space matured. They scaled with read replicas and orchestration because migrating later was impractical. That isn’t OpenAI. They made these architectural decisions presumably in the early 2020s, when far better options already existed. My suspicion is that their technical decisions had a lot to do with the fact that Microsoft has a large stake in OpenAI and wanted them running on Azure services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rl9dw23hgefzc79iaen.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rl9dw23hgefzc79iaen.png" alt="Traditional 2010s Postgres Architecture" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Traditional 2010s Postgres Architecture)&lt;/p&gt;

&lt;h3&gt;
  
  
  What OpenAI actually built
&lt;/h3&gt;

&lt;p&gt;But enough history. Let's look at what they actually built.&lt;/p&gt;

&lt;p&gt;OpenAI runs a single primary Azure PostgreSQL Flexible Server instance with nearly 50 full read replicas distributed globally, handling millions of QPS for ChatGPT and the API. All writes hit the single primary. Reads are aggressively offloaded to replicas. They have explicitly avoided sharding the existing PostgreSQL deployment because of the inherent complexity and time-consuming nature of that adaptation, a fully understandable call at their scale.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggkhc1i5i8d8ol1s5ffh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggkhc1i5i8d8ol1s5ffh.png" alt="Diagram of OpenAI's single primary Azure PostgreSQL Flexible Server" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A single-writer Postgres instance, even the biggest one Azure offers, obviously can’t handle this kind of write volume. So OpenAI migrated shardable workloads and write-heavy tables to Azure Cosmos DB and blocked all new tables/features on the main PostgreSQL cluster. New workloads now default to sharded alternatives. This also likely means they have applications stitching data together from both Cosmos DB and Postgres.&lt;/p&gt;

&lt;p&gt;Because Azure PostgreSQL caps connections at 5,000, OpenAI deployed PgBouncer in Kubernetes. Each read replica gets its own K8s deployment with multiple PgBouncer pods behind a Service for load balancing. This dramatically cuts active connections and connection-setup latency.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmckypcxmmqp8et3gzycj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmckypcxmmqp8et3gzycj.png" alt="OpenAI's PgBounter setup in Kubernetes" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On replication: the primary currently streams WAL directly to all ~50 replicas. That’s sustainable today on huge instances with fat pipes, but it has hard limits. To go further, OpenAI is having the Azure Postgres team build out a new feature for them: cascading replication (intermediate replicas relay WAL downstream). But the feature is still in testing.&lt;/p&gt;

&lt;p&gt;Other key optimizations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aggressive query tuning to kill expensive multi-table joins and ORM anti-patterns&lt;/li&gt;
&lt;li&gt;Multi-layer caching with cache locking/leasing to survive cache-miss storms from overloading their PostgreSQL instances&lt;/li&gt;
&lt;li&gt;Workload isolation: high-priority vs low-priority traffic (and different products) routed to separate instances to avoid noisy-neighbor issues&lt;/li&gt;
&lt;li&gt;Multi-layer rate limiting (application, proxy, connection pooler, and per-query) plus careful retry policies to prevent spikes and retry storms&lt;/li&gt;
&lt;li&gt;Restricted schema changes: only lightweight operations allowed (no full table rewrites); backfills are heavily rate-limited; new tables forbidden&lt;/li&gt;
&lt;li&gt;High-availability setup on the primary with a hot standby for fast failover, plus enough replica headroom per region to survive individual replica failures&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The results: impressive… and concerning
&lt;/h3&gt;

&lt;p&gt;The result is a system that's… kinda stable: double-digit ms p99 client-side latency, claimed five-nines availability, and “only one SEV-0 PostgreSQL incident” in the past year. (I’m questioning how they measure five-nines availability if they’re happy about only having one SEV-0 this past year, but I digress.)&lt;/p&gt;

&lt;p&gt;That’s impressive problem-solving, and it’s a credit to OpenAI’s engineering talent that they pulled it off. Many organizations fail at far smaller scale.&lt;/p&gt;

&lt;p&gt;But let’s be clear: I have never in my career heard an infrastructure engineer be happy about having “only one SEV-0” on their primary transactional database infrastructure in a year. (Can you imagine what that says about the previous years?) A SEV-0 is the highest-severity outage. This is not normal for a well-architected system (granted, most systems are not well architected). Even in gigantic, high-growth companies, many go years without a SEV-0 on their primary transactional database system, or have never even had one.&lt;/p&gt;

&lt;p&gt;The problem with this system is that it currently is and is going to continue to be constantly on the verge of a SEV incident, and it will never stop unless they shard or migrate off of it. Growth is endless (or it is if the company is healthy, which hopefully OpenAI stays healthy), which means more data, QPS, connections, etc. every year forever. And they have put themselves in a situation where there are inherent architectural limits that have required all kinds of architectural workarounds and special arrangements with their cloud hosting provider just to keep things running. Not to mention teams and teams of engineers at both Azure and OpenAI just to keep this alive who could all probably be better utilized to increase functionality instead of keeping a system alive. Remember, Azure Database for PostgreSQL Flexible Server only allows for 5 read replicas, so in order for OpenAI to scale this way they had to have custom bespoke solutions made for them by the Azure engineering team.&lt;/p&gt;

&lt;h3&gt;
  
  
  The real cost: engineering heroics forever
&lt;/h3&gt;

&lt;p&gt;Virtually every problem they had to solve has already been solved, out of the box, by modern distributed databases. They would have saved millions in infra spend, thousands of engineering hours, and future-proofed the system by choosing something designed for this scale from the start.&lt;/p&gt;

&lt;p&gt;But I don’t blame the OpenAI team; maybe this level of growth wasn’t anticipated, or maybe internal constraints forced their hand. No one knows but the OpenAI team.&lt;/p&gt;

&lt;p&gt;But the point I want to make with this article is: if you were designing the database layer for this class of workload today, should you do this? And my very strong recommendation would be no. Unless you don't care about time, money, outages, your engineers' sanity, weekends, holidays, sleeping through the night, developer velocity, future-proofing, your remaining hair, or basic human joy.&lt;/p&gt;

&lt;h3&gt;
  
  
  The alternative: start with distributed architecture
&lt;/h3&gt;

&lt;p&gt;So what's the alternative? You should use a database architecturally designed for this type of scale from the start. In the rest of this piece, I’ll use SurrealDB as the example (full disclosure: they pay me, so I’m biased, at least until the iron fist of the CEO comes down on me for writing this article). That said, the underlying principles I’m about to walk through apply to many modern distributed systems, not just SurrealDB. Bias or not, the technical realities are what matter. And those realities should bias you towards a very different path forward, one that avoids nearly all of the problems OpenAI had to solve through sheer operational heroics. Let’s walk through it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problems PostgreSQL forced OpenAI to solve, and how a native distributed system like SurrealDB avoids them entirely
&lt;/h3&gt;

&lt;p&gt;Let’s go through the biggest pain points OpenAI encountered (including ones they didn’t mention) and why almost every one of them disappears with a distributed database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Fundamental scale limitations of a 1990s design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL came out of Berkeley in the mid-1990s, back when a few terabytes felt huge and “big data” was whatever fit on a lab server. And while it's seen a lot of development since then, the core design was never meant to handle planet-scale traffic across regions with millions of QPS and hundreds of millions of users. All the tricks OpenAI threw at it (read replicas, PgBouncer, begging Azure for cascading replication) are basically duct tape on a system that just wasn’t built for this.&lt;/p&gt;

&lt;p&gt;Databases like SurrealDB were architected at their core for this level of modern distributed scale. A SurrealDB distributed deployment uses the TiKV storage engine, the same storage engine that some of the biggest banks, insurance companies, Flipkart, Databricks, Pinterest, Plaid, Atlassian, and others use in clusters with hundreds of nodes, pushing millions of QPS and petabytes of data. And these companies are not constantly dealing with SEV-0 events. The fundamental architecture is designed to handle this traffic without the same kind of custom patches and workarounds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybbj7janc4i0f427jwv5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fybbj7janc4i0f427jwv5.png" alt="Old versus distributed scaling strategy diagrams" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition, databases like SurrealDB do not have the same connection limit issues as older relational databases, where you’re capped at 5,000 connections. So the connection pooling problem OpenAI had to overcome becomes mitigated (though it's still a good idea to optimize the system).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The insane replica cost&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenAI has ~50 full read replicas, which means 50 complete copies of the whole dataset. Block storage is the priciest, least-discountable thing on any cloud bill, and copying dozens/hundreds of TBs of data fifty times is throwing away millions a year for no extra value beyond read capacity. On top of that, each replica is its own isolated box that can’t share compute. You have to overprovision every single one to survive its own spikes, so half (or very likely more) of your CPU is probably sitting idle 95% of the time. And when traffic surges, you can’t just add compute instantly. Provisioning a new replica and replicating the full dataset takes a huge amount of time (potentially hours). That’s why OpenAI overprovisions well in advance, to give themselves a buffer while new replicas catch up. But this architecture means that they need accurate forecasting and plenty of spare capacity ahead of time.&lt;/p&gt;

&lt;p&gt;And if you don't believe me on the storage pricing analysis, here's the AWS bill calculator for 50 64TB EBS volumes and 50 128vCPU instances. How much more expensive do you think storage is vs. compute?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4fk93iltf8pxcx4g7nj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl4fk93iltf8pxcx4g7nj.png" alt="AWS bill calculator showing the estimate" width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s right. Storage costs ~7× more than compute in this example. This can vary depending on the disk type, how much data and compute you need, and whether you are using managed services. In your situation it might only be 4× or 2×, but you get the gist. And while this example is from AWS, this is true across all cloud hosting providers. &lt;/p&gt;

&lt;p&gt;The core reason is that with reserved instances you can get huge discounts on compute with all the cloud providers, up to 72% or even 80%+ with enterprise discount plans. The standard discount for block storage is 0%. I have seen up to 5% with special arrangements on AWS, but that was for one of their largest customers. This is why scaling via read replicas is such a financial disaster. Now, maybe OpenAI gets special treatment here because they are partially owned by Microsoft and this isn’t relevant to them, but it should be relevant to you if your company cares about money.&lt;/p&gt;

&lt;p&gt;And feel free to verify these cost figures yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/products/calculator" rel="noopener noreferrer"&gt;https://cloud.google.com/products/calculator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://azure.microsoft.com/en-us/pricing/calculator/" rel="noopener noreferrer"&gt;https://azure.microsoft.com/en-us/pricing/calculator/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://calculator.aws/" rel="noopener noreferrer"&gt;https://calculator.aws/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But how does a distributed database like SurrealDB address this? When you store data on SurrealDB, it uses a separate node entirely for storage that replicates it 3 times, auto-shards it into small chunks across the cluster, and distributes it across the cluster for high availability.&lt;/p&gt;

&lt;p&gt;And if you need to scale throughput and concurrency, you can do so by adding cheaper stateless compute nodes that all read from the same shared storage pool. No full copies. And on top of that, the stateless compute nodes can be removed and spun up dynamically at any time, which means you don’t have to massively overprovision for any given event. The system is flexible enough to be scaled up and down all the time, saving huge costs on overprovisioning. And in the event you do have a high-growth event and you need more compute, it only takes a few minutes to get the cluster powered up enough to handle the increased workload.&lt;/p&gt;

&lt;p&gt;All this means that by going with a distributed architecture you could, depending on your scenario, decrease your current/future total cloud bill by several factors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. The single-writer bottleneck and the forced migration to a separate NoSQL system&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In PostgreSQL’s single-primary architecture, every single write (updates, inserts, deletes) has to funnel through that one primary instance. No matter how large the machine (and OpenAI is clearly running one of the biggest Azure offers), a sudden write spike can still saturate CPU, I/O, or WAL bandwidth. Latency climbs, queries start timing out, retries pour in, and you’re suddenly in a classic overload death spiral that can degrade ChatGPT and the API for everyone.&lt;/p&gt;

&lt;p&gt;OpenAI’s fix was to identify the workloads that were shardable and write-heavy (many of which are probably fundamentally relational in nature), migrate them to Cosmos DB, and accept the permanent cost of running two completely different database systems  side-by-side. That means separate query languages, different consistency models, distinct operational tooling, and application code that has to stitch results together across the two stores forever.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfweb1rxq87l20hkf1vc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkfweb1rxq87l20hkf1vc.png" alt="SurrealDB architecture versus CosmosDB-PostgreSQL architecture diagram" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A distributed database like SurrealDB is multi-writer by design. Writes are spread across multiple nodes in the cluster, so write capacity grows horizontally as you add machines. There is no single primary choke point, and no need to spin up and maintain an entirely separate database just to handle write load.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Expensive multi-table joins killing the cluster&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenAI repeatedly calls out multi-way joins as a major source of SEV incidents. They had to audit ORM output, break joins into application-side logic, and basically treat joins as dangerous. That’s not a great place to be when your data model is relational.&lt;/p&gt;

&lt;p&gt;SurrealDB avoids this class of failure by not relying on traditional relational joins at all. Relationships are modeled explicitly as record links and graph edges, and resolved through traversal inside a single query engine rather than join operators. This eliminates join explosion and centralized join execution while still allowing expressive relationship queries across documents, graphs, and vectors without forcing application-side orchestration or creating single-node saturation risks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Schema changes become impractical&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenAI now bans anything beyond the lightest schema changes and rate-limits backfills for weeks. That’s a huge velocity tax. Every new column or type change becomes a high-risk, multi-week operation.&lt;/p&gt;

&lt;p&gt;SurrealDB reduces schema-change issues by decoupling schema definition from rewriting existing storage. Where relational systems often force heavy DDL and backfills just to keep data and schema synchronized, SurrealDB’s current approach enforces schema primarily as data is written/updated, and the team has explicitly identified richer zero-downtime mechanisms (aliases/versioning/async migrators) as the path to handling large-scale online migrations and reindexing without downtime. This results in a scenario where you can make a change to a table at scale without complexity or waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Stability constantly threatened by spikes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the single-primary PostgreSQL setup, any unexpected surge (cache-miss storms, new feature launches, retry loops, or sudden traffic growth) can quickly saturate a single instance’s CPU, I/O, or connection limits. Latency spikes, requests time out, retries amplify the load, and the whole service risks cascading degradation. The primary is still a true single point of failure (even with a hot standby), and losing a read replica requires pre-planned headroom across every region. OpenAI had to build multiple layers of rate limiting, workload isolation, and cache leasing just to keep the system from regularly tipping over.&lt;/p&gt;

&lt;p&gt;A distributed architecture with separated compute and storage handles this differently. Stateless query nodes can be scaled automatically in seconds during spikes, absorbing bursts without saturating any one machine. High availability is native: individual node failures are isolated, and automatic failover and rebalancing occur without any intervention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Astronomical ongoing engineering overhead&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This setup requires constant tuning, custom Azure features, cascading replication experiments, query audits, rate-limit adjustments, connection-pool management, and on-call heroics to prevent the next SEV-0. That’s a permanent tax on engineering bandwidth. People who could be building new features are instead keeping Frankenstein alive.&lt;/p&gt;

&lt;p&gt;SurrealDB is designed for distributed scale from the start and is intended to be boring at this traffic level. The problems OpenAI is constantly solving just aren’t issues with databases like SurrealDB. Now, every database will encounter problems at hyperscale (and tons of them (and when I say tons, I mean tons; scale is hard)), but they won't be nearly as severe. In fact, with many of the (healthy) companies I’ve worked with, the majority of the time infrastructure engineers spend is more on the optimization and functionality side of things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Inevitable database sprawl&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By freezing the core PostgreSQL schema and banning new tables entirely, OpenAI has ensured that any future workload with different performance characteristics or heavier writes will need its own separate database system (Cosmos DB today, likely others tomorrow). That guarantees growing proliferation, more cross-system data movement, and increasing consistency and operational challenges over time, which, by the way, likely means more data transfer fees and more replicas across databases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8iclensuu3uuxob83mqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8iclensuu3uuxob83mqj.png" alt="A possible future of OpenAI’s architecture" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(A possible future of OpenAI’s architecture)&lt;/p&gt;

&lt;p&gt;SurrealDB is multi-model, which can evolve with the product. It can incorporate relational, document, geospatial, temporal, graph, vector, and time-series data in the same cluster as requirements evolve, avoiding the need to continually spin up new databases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Locking yourself out of future AI workloads on your own data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the subtler long-term cost. OpenAI has arguably the most valuable dataset on the planet, but their core transactional store is perpetually at capacity and can’t absorb new workloads. To do anything interesting with that data at scale, they’ll need to replicate it elsewhere, adding cost, latency, time, and consistency risk.&lt;/p&gt;

&lt;p&gt;SurrealDB is built for exactly this future: relational + temporal + document + graph + vector + time-series in the same system, on the same dataset, without replication or ETL. You can run transactional workloads, analytics, RAG, and recommendation engines against the same live data without fear of blowing up the cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Database infrastructure shouldn’t require miracles
&lt;/h3&gt;

&lt;p&gt;OpenAI’s infrastructure team pulled off a minor miracle keeping a single-writer PostgreSQL cluster alive at this scale. The optimizations are clever, the operational discipline is elite, and the fact that ChatGPT mostly works for 800 million users is a testament to their skill.&lt;/p&gt;

&lt;p&gt;But miracles aren’t scalable. This architecture is a patchwork of workarounds, custom Azure features, and permanent firefighting, held together by constant engineering effort that could be spent building new capabilities instead. All to compensate for fundamental limitations of a 1990s-era database design pushed far beyond its natural habitat.&lt;/p&gt;

&lt;p&gt;If you’re building something ambitious in 2026, don’t inherit these problems. Choose a database built for distributed scale, horizontal everything, multi-model flexibility, and reliably boring operations from day one.&lt;/p&gt;

&lt;p&gt;Your bill will be lower, your on-call rotation will sleep better, and you’ll have vastly more freedom to experiment with the AI-native workloads that will define the next decade.&lt;/p&gt;

&lt;p&gt;Pick the right foundation the first time. Everyone on your team will thank you.&lt;/p&gt;

</description>
      <category>database</category>
      <category>openai</category>
      <category>postgres</category>
      <category>postgressql</category>
    </item>
    <item>
      <title>SurrealDB is now available on AWS Marketplace</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Wed, 11 Mar 2026 18:08:27 +0000</pubDate>
      <link>https://forem.com/surrealdb/surrealdb-is-now-available-on-aws-marketplace-5db6</link>
      <guid>https://forem.com/surrealdb/surrealdb-is-now-available-on-aws-marketplace-5db6</guid>
      <description>&lt;p&gt;We’re excited to announce that SurrealDB is now officially available on AWS Marketplace.&lt;/p&gt;

&lt;p&gt;You can now deploy SurrealDB Cloud with streamlined procurement, simplified billing, and faster time to production - all through your existing AWS accounts.&lt;/p&gt;

&lt;p&gt;This launch also reinforces our commitment to meeting customers where they build, and deepens our partnership with AWS to make SurrealDB easier to adopt, deploy, and scale in the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;AWS Marketplace makes it easier for companies to adopt SurrealDB with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seamless AWS-native deployment&lt;/li&gt;
&lt;li&gt;Consolidated billing&lt;/li&gt;
&lt;li&gt;Faster security and procurement approval&lt;/li&gt;
&lt;li&gt;Production-ready infrastructure from day one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re already building on AWS, getting started with SurrealDB just became frictionless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SurrealDB?
&lt;/h2&gt;

&lt;p&gt;SurrealDB combines document, graph, and relational capabilities in one unified database - designed for modern applications, real-time systems, and AI-powered workloads.&lt;/p&gt;

&lt;p&gt;With flexible schema, SurrealQL, and built-in real-time features, SurrealDB simplifies your technology stack, reduces Total Cost of Ownership and operational complexity, and allows you to ship products and features in days rather than weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started
&lt;/h2&gt;

&lt;p&gt;SurrealDB is &lt;a href="https://aws.amazon.com/marketplace/seller-profile?id=seller-3gvbyh53eomro" rel="noopener noreferrer"&gt;available now on AWS Marketplace&lt;/a&gt;. We can’t wait to see what you build.&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>database</category>
      <category>aws</category>
      <category>cloud</category>
    </item>
    <item>
      <title>How to use Surrealism to build your own custom SurrealDB extensions</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Wed, 11 Mar 2026 16:56:48 +0000</pubDate>
      <link>https://forem.com/surrealdb/how-to-use-surrealism-to-build-your-own-custom-surrealdb-extensions-1dj8</link>
      <guid>https://forem.com/surrealdb/how-to-use-surrealism-to-build-your-own-custom-surrealdb-extensions-1dj8</guid>
      <description>&lt;p&gt;Author: &lt;a href="https://x.com/mithridates" rel="noopener noreferrer"&gt;Dave MacLeod&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Surrealism to build your own custom SurrealDB extensions
&lt;/h2&gt;

&lt;p&gt;Two weeks ago we announced SurrealDB 3.0, along with a &lt;a href="https://surrealdb.com/blog/introducing-surrealism" rel="noopener noreferrer"&gt;new feature called Surrealism&lt;/a&gt; that lets you write your own functions in Rust that can then be annotated to call directly from the database.&lt;/p&gt;

&lt;p&gt;In this blog post, we are going to take a look at what use cases Surrealism solves and easy it is to get started with it. Let's begin by taking a look at how functions are used in SurrealQL and how Surrealism expands their range of use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functions in SurrealDB
&lt;/h2&gt;

&lt;p&gt;SurrealDB has always had user-defined functions that look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;-- 20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These functions can get to any complexity and length that you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;relate_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;-- Don't do anything, ending the recursion&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;..];&lt;/span&gt;
      &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;counterpart&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;RELATE&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;first&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;counterpart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;relate_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;remainder&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next type of function was added in SurrealDB 2.0: closures. Closures are anonymous functions that can work in read operations that can access both arguments and parameters in their scope.&lt;/p&gt;

&lt;h4&gt;
  
  
  Defining and using a closure
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;log_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"DEBUG"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Define a closure&lt;/span&gt;
&lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tb&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;log_level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;log_level&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Closure output
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Calling $split&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;log_level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'DEBUG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'user'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;-- Passing $split into .map() which takes a closure&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'one'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;log_level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'DEBUG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'person'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'two'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;log_level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'DEBUG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'cat'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, both defined functions and closures live inside the SurrealQL query language inside the database.&lt;/p&gt;

&lt;p&gt;Surrealism surpasses this limitation by letting you define functions in Rust instead, with all of the available bells and whistles that you get from the Cargo package manager and &lt;a href="https://crates.io/" rel="noopener noreferrer"&gt;crates.io&lt;/a&gt; ecosystem.&lt;/p&gt;

&lt;p&gt;These functions are then compiled to a WASM binary that then can be linked to from the database itself.&lt;/p&gt;

&lt;p&gt;Being able to add functions just like that means that new functionality can be added today, to your existing SurrealDB version, with the only setup being two &lt;code&gt;DEFINE&lt;/code&gt; statements - one to set up a file bucket, and one more to show the database where inside this bucket the WASM file is located.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we made Surrealism
&lt;/h2&gt;

&lt;p&gt;Making Surrealism was in fact an absolute necessity for a project like SurrealDB in which the community plays such a large part. That's because a lot of requests are for features that are nice to have, but don't quite make sense to merge into the code for the database itself. But with an extension system, you can make the feature yourself!&lt;/p&gt;

&lt;p&gt;Using Surrealism is basically like installing apps on your phone. You can't modify the OS of the phone itself, but you can install a new app any time you find one that appeals to you. Similarly, you can build and upload an app for other people to try out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some examples
&lt;/h2&gt;

&lt;p&gt;Let's look at two case studies where Surrealism allows a functionality to be added that was not possible before.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 1: A well-intended PR
&lt;/h3&gt;

&lt;p&gt;Sometimes you will see a well-intended PR that isn't able to be accepted, such as &lt;a href="https://github.com/surrealdb/surrealdb/pull/6063" rel="noopener noreferrer"&gt;this one&lt;/a&gt;. That PR used Rust's &lt;a href="https://crates.io/crates/fake" rel="noopener noreferrer"&gt;fake&lt;/a&gt; crate to create a number of database functions that could be used to seed a database with mock data: fake names, addresses, companies, and so on. The function signatures would have looked like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;building_number&lt;/span&gt;
&lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;
&lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;country&lt;/span&gt;
&lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;country_code&lt;/span&gt;
&lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;state&lt;/span&gt;
&lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;street&lt;/span&gt;
&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;so&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While useful (and tempting), it would lead to an increase in the binary size for a functionality that is not core to a database, so it was not able to be merged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Case 2: A feature request
&lt;/h3&gt;

&lt;p&gt;Another example comes in the form of feature requests, such as &lt;a href="https://github.com/surrealdb/surrealdb/issues/2794" rel="noopener noreferrer"&gt;this one&lt;/a&gt; from a user who would like to see Serbian added to the list of languages that could use the Snowball filter.&lt;/p&gt;

&lt;p&gt;First let's look at what the Snowball filter is and what it does. This filter is used inside a &lt;code&gt;DEFINE ANALYZER&lt;/code&gt; statement to enable full-text search. The filter uses something called stemming which reduces words to something like a root form. This is best seen through an example. In the two queries below we have defined an analyzer that first splits a string by class (whitespace, dots, numbers vs. letters, etc.) and then passes it through the Snowball filter for English.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;ANALYZER&lt;/span&gt; &lt;span class="n"&gt;en_snowball&lt;/span&gt; &lt;span class="n"&gt;TOKENIZERS&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="n"&gt;FILTERS&lt;/span&gt; &lt;span class="n"&gt;snowball&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;english&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;search&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;"en_snowball"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;"Making Surrealism was in fact an absolute necessity for a project like SurrealDB..."&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;The output shows some words like 'absolut' and 'necess', which are the shortest possible forms that can be used to match on a word. Note that this isn't an exact science as you could argue that 'make' should be 'mak' in order to match on forms like 'making'. But that is a subject for another time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'make'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'surreal'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'was'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'in'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fact'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'an'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'absolut'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'necess'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="s1"&gt;'for'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'project'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'like'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'surrealdb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;p&gt;Now, the interesting part about this feature request is that Serbian &lt;strong&gt;is&lt;/strong&gt; one of the languages with a Snowball stemmer. So adding Serbian is technically doable, except that it would require quite a bit of work.&lt;/p&gt;

&lt;p&gt;The reason why is that the crate that SurrealDB uses to implement it hasn't been updated for a while. There was even a &lt;a href="https://github.com/CurrySoftware/rust-stemmers/pull/15" rel="noopener noreferrer"&gt;PR to add Serbian&lt;/a&gt; to it but it was never merged and has since been closed. So adding Serbian to the list of languages would require something like the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Try contacting the crate author to bring up the issue again&lt;/li&gt;
&lt;li&gt;Wait a while, fork the repo if there is no response&lt;/li&gt;
&lt;li&gt;Contacting the author of the PR to add Serbian to see if it's okay to use that PR, or implement it ourself&lt;/li&gt;
&lt;li&gt;Follow up with a PR to the SurrealDB source code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, if you'd like to put a PR together that accomplishes all that then feel free!&lt;/p&gt;

&lt;p&gt;However, thanks to Surrealism, this sort of problem can be resolved today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a Surrealism module
&lt;/h2&gt;

&lt;p&gt;So let's put a Surrealism module together that allows you to create mock data as well as work with Serbian text. This section is a somewhat quicker walkthrough of the same steps as &lt;a href="https://surrealdb.com/docs/surrealdb/extensions/tutorial" rel="noopener noreferrer"&gt;this page&lt;/a&gt; in the documentation, so check that page out too to see more details.&lt;/p&gt;

&lt;p&gt;There is a bit of boilerplate to start.&lt;/p&gt;

&lt;h3&gt;
  
  
  The boilerplate
&lt;/h3&gt;

&lt;p&gt;First we need to add a line in Cargo.toml to create a dynamic library that can be loaded from another language.&lt;/p&gt;

&lt;h4&gt;
  
  
  Add to Cargo.toml
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[lib]&lt;/span&gt;
&lt;span class="py"&gt;crate-type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"cdylib"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then a surrealism.toml file is needed. We can just copy and paste this into the file.&lt;/p&gt;

&lt;h4&gt;
  
  
  Create surrealism.toml
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[package]&lt;/span&gt;
&lt;span class="py"&gt;organisation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"surrealdb"&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"demo"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;

&lt;span class="nn"&gt;[attach]&lt;/span&gt;
&lt;span class="py"&gt;fs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"fs"&lt;/span&gt;

&lt;span class="nn"&gt;[capabilities]&lt;/span&gt;
&lt;span class="py"&gt;allow_scripting&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;allow_arbitrary_queries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;allow_functions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"fn::test"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;allow_net&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:8080"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Annotating functions
&lt;/h3&gt;

&lt;p&gt;Now we can make some functions and annotate them.&lt;/p&gt;

&lt;p&gt;The code for the fake data couldn't be simpler, as the &lt;a href="https://crates.io/crates/fake" rel="noopener noreferrer"&gt;&lt;code&gt;fake&lt;/code&gt;&lt;/a&gt; crate uses a dedicated function for each type of fake data. You do have to choose a language though. Let's go with German for the fun of it.&lt;/p&gt;

&lt;p&gt;To make a function a Surrealism function, just add the &lt;code&gt;#[surrealism]&lt;/code&gt; annotation to the top. This will allow them to be exposed in the binary once the code is compiled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Inside lib.rs&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;Fake&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nn"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
        &lt;span class="nn"&gt;address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;de_de&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;CityName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CountryName&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; 
        &lt;span class="nn"&gt;company&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;de_de&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CompanyName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;creditcard&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;de_de&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CreditCardNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="nn"&gt;name&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;de_de&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;surrealism&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;surrealism&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;city&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;CityName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;company_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;CompanyName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling these functions will generate an output something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Oberschreiberheim
Schmidt and Götz Stiftung
Gerhard Hoffmann
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's move on to Serbian. Implementing Snowball ourselves would be a lot of code, so let's go with something simpler for demonstration: stop words. Stop words is a method by which you remove all the most frequent words from a language that are not very useful when working with text. For English these are words like the, a, an, but, at, on, is, and so on.&lt;/p&gt;

&lt;p&gt;Rust has a &lt;a href="https://docs.rs/stop-words/0.10.0/stop_words/" rel="noopener noreferrer"&gt;stop words&lt;/a&gt; crate which doesn't have Serbian, but does have Croatian (language code &lt;code&gt;hr&lt;/code&gt;). Both Serbian and Croatian are one of the standards of &lt;a href="https://en.wikipedia.org/wiki/Serbo-Croatian" rel="noopener noreferrer"&gt;Serbo-Croatian&lt;/a&gt; which means that a tool for one works pretty well for another.&lt;/p&gt;

&lt;p&gt;This crate has an example of the tool in action &lt;a href="https://github.com/cmccomb/rust-stop-words/blob/48ddeb615215cce40e51497224bc7e5384fa62cd/examples/remove_stop_words_with_regex.rs#L1" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Read in a file&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"examples/foreword.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cannot read file"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Print the contents&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Original text:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the stopwords&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;stop_words&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;stop_words&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;LANGUAGE&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;English&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove punctuation and lowercase the text to make parsing easier&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lowercase_doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="nf"&gt;.to_ascii_lowercase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;regex_for_punctuation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;one_or_more&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;punctuation&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;text_without_punctuation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex_for_punctuation&lt;/span&gt;
        &lt;span class="nf"&gt;.to_regex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.replace_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lowercase_doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Make a regex to match stopwords with trailing spaces and punctuation&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;regex_for_stop_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;word_boundary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;exactly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;word_boundary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;one_or_more&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;whitespace&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove stop words&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;clean_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex_for_stop_words&lt;/span&gt;
        &lt;span class="nf"&gt;.to_regex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.replace_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;text_without_punctuation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Clean text:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clean_text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have a small hiccup here: unlike Croatian, Serbian is also written using the Cyrillic alphabet. However, a quick search through crates.io shows that there is &lt;a href="https://docs.rs/serbian-cyrillic-latin-conversion/1.0.2/serbian_cyrillic_latin_conversion/index.html" rel="noopener noreferrer"&gt;a crate&lt;/a&gt; to convert Serbian Cyrillic to Latin and the other way around.&lt;/p&gt;

&lt;p&gt;Combining these two, we can create a function called &lt;code&gt;serbian_stop()&lt;/code&gt; that converts the input to Latin or Cyrillic. Instead of exposing that, we'll make two more functions, &lt;code&gt;serbian_stop_latin()&lt;/code&gt; and &lt;code&gt;serbian_stop_cyrillic()&lt;/code&gt;, which will get the &lt;code&gt;#[surrealism]&lt;/code&gt; annotation so that they can be called directly from the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Alphabet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Latin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Cyrillic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;serbian_stop_latin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;serbian_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Latin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;serbian_stop_cyrillic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;serbian_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cyrillic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;serbian_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alphabet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Make sure it's the Latin alphabet first&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Convertion&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_cyrillic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the stopwords&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;stop_words&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hr"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove punctuation and lowercase the text to make parsing easier&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lowercase_doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="nf"&gt;.to_ascii_lowercase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;regex_for_punctuation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;one_or_more&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;punctuation&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;text_without_punctuation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex_for_punctuation&lt;/span&gt;
        &lt;span class="nf"&gt;.to_regex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.replace_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lowercase_doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Make a regex to match stopwords with trailing spaces and punctuation&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;regex_for_stop_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;word_boundary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;exactly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;word_boundary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;one_or_more&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;whitespace&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove stop words&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;clean_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex_for_stop_words&lt;/span&gt;
        &lt;span class="nf"&gt;.to_regex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.replace_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;text_without_punctuation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Return as input user requested&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;alphabet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cyrillic&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Convertion&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_latin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;clean_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Latin&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Convertion&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_cyrillic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;clean_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Putting all the functions together gives us this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="n"&gt;Fake&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;faker&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="nn"&gt;address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;de_de&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CityName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;company&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;de_de&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CompanyName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;name&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;de_de&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;serbian_cyrillic_latin_conversion&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Convertion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;surrealism&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;surrealism&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;city&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;CityName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;company_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;CompanyName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.fake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Alphabet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Latin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Cyrillic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;serbian_stop_latin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;serbian_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Latin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;serbian_stop_cyrillic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;serbian_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cyrillic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;serbian_stop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alphabet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Make sure it's the Latin alphabet first&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Convertion&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_cyrillic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the stopwords&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;stop_words&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hr"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove punctuation and lowercase the text to make parsing easier&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;lowercase_doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="nf"&gt;.to_ascii_lowercase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;regex_for_punctuation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;one_or_more&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;punctuation&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;text_without_punctuation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex_for_punctuation&lt;/span&gt;
        &lt;span class="nf"&gt;.to_regex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.replace_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lowercase_doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Make a regex to match stopwords with trailing spaces and punctuation&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;regex_for_stop_words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;word_boundary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;exactly&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;words&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;word_boundary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;one_or_more&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;human_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;whitespace&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// Remove stop words&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;clean_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regex_for_stop_words&lt;/span&gt;
        &lt;span class="nf"&gt;.to_regex&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;.replace_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;text_without_punctuation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Return as input user requested&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;alphabet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cyrillic&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Convertion&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_latin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;clean_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;Alphabet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Latin&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Convertion&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_cyrillic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;clean_text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Compiling and accessing the Surrealism module
&lt;/h3&gt;

&lt;p&gt;With the functions done, it's now time to compile the Surrealism module. That can be done with the &lt;a href="https://surrealdb.com/docs/surrealdb/cli/module" rel="noopener noreferrer"&gt;&lt;code&gt;surreal module build&lt;/code&gt;&lt;/a&gt; command. Follow this with &lt;code&gt;--out&lt;/code&gt; and the file name, which must have the extension &lt;code&gt;.surli&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal module build &lt;span class="nt"&gt;--out&lt;/span&gt; serbian_and_fake.surli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the following output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Building WASM module...
   Compiling random v0.1.0 (/Users/mithr/surrealism_test)
    Finished `release` profile [optimized] target(s) in 2.38s
Optimizing bundle...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's time to start the database with &lt;code&gt;surreal start&lt;/code&gt;. It needs two environment variables to make this happen: one to allow the &lt;code&gt;files&lt;/code&gt; and &lt;code&gt;surrealism&lt;/code&gt; experimental features to be used, and another that shows the location of the &lt;code&gt;.surli&lt;/code&gt; file that we just compiled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SURREAL_CAPS_ALLOW_EXPERIMENTAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;files,surrealism &lt;span class="nv"&gt;SURREAL_BUCKET_FOLDER_ALLOWLIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/Users/my_name/my_rust_code/"&lt;/span&gt; surreal start &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll connect to the database either via Surrealist or the following CLI command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SURREAL_CAPS_ALLOW_EXPERIMENTAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;files,surrealism surreal sql &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we are connected we just need two more statements: one to define a bucket for the files, and another to define a module, a module being a collection of Surrealism functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;BUCKET&lt;/span&gt; &lt;span class="n"&gt;my_bucket&lt;/span&gt; &lt;span class="n"&gt;BACKEND&lt;/span&gt; &lt;span class="nv"&gt;"file:/Users/my_name/my_rust_code"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;MODULE&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;my_funcs&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/serbian_and_fake.surli"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's all you need to do! Let's call the first three functions to see what output we get. It'll be different every time, but will look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;my_funcs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;full_name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;-- 'Christa Simon'&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;my_funcs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;company_name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;-- 'Götz and Seidel und Partner'&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;my_funcs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;         &lt;span class="c1"&gt;-- 'Kleinfuchsfurt'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's try the Serbian functions. We'll choose this passage from the Serbian Wikipedia:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Неолитско насеље у Винчи удаљено је око 14 km од ушћа Саве у Дунав, што је изузетно повољно место које је омогућило да постане фокална тачка простора југоисточне Европе.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That passage is about a pre-historic neolithic culture that existed in and well beyond present day Serbia. It means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The Neolithic settlement in Vinča is about 14 km from the confluence of the Sava and the Danube, which is an extremely favorable place that allowed it to become a focal point of the area of Southeastern Europe."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We'll take the Cyrillic form and see if we can turn it into Latin and vice versa.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;my_funcs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;serbian_stop_latin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;"Неолитско насеље у Винчи удаљено је око 14 km од ушћа Саве у Дунав, што је изузетно повољно место које је омогућило да постане фокална тачка простора југоисточне Европе."&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;my_funcs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;serbian_stop_cyrillic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;"Neolitsko naselje u Vinči udaljeno je oko 14 km od ušća Save u Dunav, što je izuzetno povoljno mesto koje je omogućilo da postane fokalna tačka prostora jugoistočne Evrope."&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success! Even if you don't know the language, you can compare the input and output to see that the output does indeed lack a number of words. This is clear when you line them up against each other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Original text in Latin&lt;/span&gt;
&lt;span class="s1"&gt;'Neolitsko naselje u Vinči udaljeno je oko 14 km od ušća Save u Dunav, što je izuzetno povoljno mesto koje je omogućilo da postane fokalna tačka prostora jugoistočne Evrope.'&lt;/span&gt;

&lt;span class="c1"&gt;-- Cyrillic turned into Latin with stop words applied&lt;/span&gt;
&lt;span class="s1"&gt;'neolitsko naselje   vinči udaljeno    oko 14 km    ušća save   dunav         izuzetno povoljno mesto         omogućilo    postane fokalna tačka prostora jugoistočne evrope'&lt;/span&gt;

&lt;span class="c1"&gt;-- Latin turned into Cyrillic with stop words applied&lt;/span&gt;
&lt;span class="s1"&gt;'неолитско насеље    винчи удаљено     око 14 км    ушћа саве   дунав         изузетно повољно  место         омогућило    постане фокална тачка простора југоисточне европе'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So there we have it. In the space of a 350-line blog post we've managed to turn SurrealDB from regular SurrealDB 3.0 into one with a few extra "apps" that let you create mock data on the fly, or work with a new language that wasn't available before.&lt;/p&gt;

&lt;p&gt;And since a Surrealism plugin is a WASM binary written in Rust, you can compile it yourself or share it for others to use. The ability to share what you've built is what makes a plugin system like this so extensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community example
&lt;/h2&gt;

&lt;p&gt;To finish up this post, let's quickly check out an example of Surrealism in action. SurrealDB user (and &lt;a href="https://surrealdb.com/ambassador-programme" rel="noopener noreferrer"&gt;ambassador&lt;/a&gt;!) Horváth Bálint is the author of an app called recet that he described as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Recet is an open source, work in progress recipe manager application built using Nuxt and SurrealDB. It is motivated from personal needs and the curiosity to see how much functionality can be squeezed out from using SurrealDB as the backend. My goal is to create a web app that feels great on both desktop and mobile using modern technologies like PWA, View Transition API, all while showcasing many features of SurrealDB.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the features of this app is a Surrealism function! Let's &lt;a href="https://github.com/horvbalint/recet/blob/4d5fa7009049b3b30b61ff90ddcf8980b1b97fdc/surreal-plugin/src/recipe_scraper.rs#L9" rel="noopener noreferrer"&gt;take a look at it&lt;/a&gt;. As you can see, it uses a crate called &lt;a href="https://crates.io/crates/html2text" rel="noopener noreferrer"&gt;&lt;code&gt;html2text&lt;/code&gt;&lt;/a&gt;. Here's part of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[surrealism]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;scrape_for_recipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;RecipeInfo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;source_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;source_text&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;source_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"url"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sql_with_vars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"http::get($url)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nd"&gt;vars!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;source_text&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nn"&gt;html2text&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="nf"&gt;.as_bytes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;anyhow!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid source type"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="c1"&gt;// ... snip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we have the same &lt;code&gt;DEFINE BUCKET&lt;/code&gt; and &lt;code&gt;DEFINE MODULE&lt;/code&gt; &lt;a href="https://github.com/horvbalint/recet/blob/4d5fa7009049b3b30b61ff90ddcf8980b1b97fdc/db.surql#L34" rel="noopener noreferrer"&gt;statements&lt;/a&gt; as above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;PLUGINS&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;BUCKET&lt;/span&gt; &lt;span class="n"&gt;OVERWRITE&lt;/span&gt; &lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="n"&gt;BACKEND&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;pluginsBucketPath&lt;/span&gt; &lt;span class="n"&gt;PERMISSIONS&lt;/span&gt; &lt;span class="k"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;MODULE&lt;/span&gt; &lt;span class="n"&gt;OVERWRITE&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;recet&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"plugins:/recet.surli"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then &lt;a href="https://github.com/horvbalint/recet/blob/4d5fa7009049b3b30b61ff90ddcf8980b1b97fdc/app/pages/recipe/create/%5B%5Bid%5D%5D.vue#L277" rel="noopener noreferrer"&gt;a query via the JavaScript SDK&lt;/a&gt; that calls the function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;importRecipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;importing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;surql&lt;/span&gt;&lt;span class="s2"&gt;`mod::recet::scrape_for_recipe(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;importSourceType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;importSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ImportedRecipe&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;recipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No recipe found at the provided URL.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// ... snip&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it's the same process described in the first examples above. Annotate the function, compile the module, use &lt;code&gt;DEFINE&lt;/code&gt; twice to connect to it, and the function(s) are yours to call.&lt;/p&gt;

&lt;p&gt;The final app, by the way, looks like this. Personally I think the &lt;em&gt;kókusztejes gombás fehérbab&lt;/em&gt; looks particularly good, or the &lt;em&gt;póréhagymás mascarponés gnocchi&lt;/em&gt; with the leeks replaced with grated cheese.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9shmjlh7ps75xn22em9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo9shmjlh7ps75xn22em9.png" alt="Recipes" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to you
&lt;/h2&gt;

&lt;p&gt;There's no telling what the SurrealDB community will do with Surrealism now that 3.0 is here and the word is getting out.&lt;/p&gt;

&lt;p&gt;We can't wait to see what you build!&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started with Surrealism
&lt;/h2&gt;

&lt;p&gt;Go to the &lt;a href="https://surrealdb.com/docs/surrealdb/extensions" rel="noopener noreferrer"&gt;Surrealism Docs&lt;/a&gt; to get started. We are excited to see what extensions you build, be sure to share them in our &lt;a href="https://discord.com/invite/surrealdb" rel="noopener noreferrer"&gt;Discord channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for being on this journey with us!&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>database</category>
      <category>surrealism</category>
      <category>plugins</category>
    </item>
    <item>
      <title>Introducing JavaScript SDK 2.0</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Thu, 26 Feb 2026 12:26:43 +0000</pubDate>
      <link>https://forem.com/surrealdb/introducing-javascript-sdk-20-lon</link>
      <guid>https://forem.com/surrealdb/introducing-javascript-sdk-20-lon</guid>
      <description>&lt;p&gt;Author: &lt;a href="https://bsky.app/profile/jmills.nl" rel="noopener noreferrer"&gt;Julian Mills&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We're excited to announce the SurrealDB JavaScript SDK v2, the most significant update to the SDK to date. We've rebuilt the core internals with a focus on ergonomics, flexibility, and developer experience. Key highlights include full support for SurrealDB 3.0, multi-session support, automatic token refreshing, client-side transactions, a redesigned live query API, and a new query builder pattern that makes working with your data more intuitive than ever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcome WASM and Node!
&lt;/h2&gt;

&lt;p&gt;The existing WebAssembly and Node.js SDKs have been rewritten, updated to support the 2.0 JavaScript SDK, and moved into the JavaScript SDK repository.&lt;/p&gt;

&lt;p&gt;Going forward, the JS, WASM, and Node.js SDKs will be published together, so embedded SurrealDB stays up to date. The WASM and Node.js SDK versions align their major and minor versions with SurrealDB, so you can tell at a glance which SurrealDB version you're running.&lt;/p&gt;

&lt;p&gt;As a bonus, the WASM SDK now supports running inside a Web Worker, so you can offload database work from the main thread and keep your UI responsive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wasm
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Surreal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createRemoteEngines&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;surrealdb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createWasmEngines&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@surrealdb/wasm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;WorkerAgent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@surrealdb/wasm/worker?worker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Surreal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;engines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;createRemoteEngines&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;createWasmEngines&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="c1"&gt;// or for Web Worker based engines&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;createWasmWorkerEngines&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;createWorker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WorkerAgent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Node.js (and Bun &amp;amp; Deno)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Surreal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createRemoteEngines&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;surrealdb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createNodeEngines&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@surrealdb/node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Surreal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;engines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;createRemoteEngines&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;createNodeEngines&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Official event listeners
&lt;/h2&gt;

&lt;p&gt;The original SDK exposed events through the internal &lt;code&gt;surreal.emitter&lt;/code&gt; field. The new SDK provides a type-safe &lt;code&gt;surreal.subscribe()&lt;/code&gt; function instead. Calling &lt;code&gt;.subscribe()&lt;/code&gt; returns a cleanup function that unsubscribes the listener when invoked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Subscribe to events&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unsub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;connected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Unsubscribe&lt;/span&gt;
&lt;span class="nf"&gt;unsub&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Access internal state
&lt;/h2&gt;

&lt;p&gt;New getters let you read internal state from the &lt;code&gt;Surreal&lt;/code&gt; instance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;surreal.namespace&lt;/code&gt; and &lt;code&gt;surreal.database&lt;/code&gt; for the selected namespace and database&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;surreal.params&lt;/code&gt; for connection parameters&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;surreal.accessToken&lt;/code&gt; and &lt;code&gt;surreal.refreshToken&lt;/code&gt; for authentication tokens
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;other-db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automatic token refreshing
&lt;/h2&gt;

&lt;p&gt;The SDK now restores or renews authentication when your access token expires or the connection reconnects. When refresh tokens are available, they are exchanged for a new token pair. Otherwise the SDK reuses the credentials you provided or fires an &lt;code&gt;auth&lt;/code&gt; event for custom handling. &lt;a href="https://github.com/surrealdb/surrealdb.js/pull/484" rel="noopener noreferrer"&gt;Read more on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For asynchronous authentication, you can pass a callable to the &lt;code&gt;authentication&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Surreal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;renewAccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default true&lt;/span&gt;
    &lt;span class="na"&gt;authentication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multi-session support
&lt;/h2&gt;

&lt;p&gt;You can create multiple isolated sessions on a single connection, each with its own namespace, database, variables, and authentication state. New sessions can be created at any time, or you can fork an existing session and reuse its state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a new session&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Use the session&lt;/span&gt;
&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signin&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;

&lt;span class="c1"&gt;// Dispose the session&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;closeSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Forking sessions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;freshSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Clone a session including namespace, database, variables, and auth state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;forkedSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;freshSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forkSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Await using
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;using&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// JavaScript will automatically close the session at the end of the current scope&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Redesigned live query API
&lt;/h2&gt;

&lt;p&gt;Live query methods on the Surreal class have been redesigned to be more intuitive. Live select queries can also restart automatically when the driver reconnects.&lt;/p&gt;

&lt;p&gt;The record ID is now passed as the third argument to your handlers, so you can identify which record changed when handling patch updates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Construct a new live subscription&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;live&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;live&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Listen to changes&lt;/span&gt;
&lt;span class="nx"&gt;live&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Alternatively, iterate messages&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;live&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Kill the query and stop listening&lt;/span&gt;
&lt;span class="nx"&gt;live&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;kill&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Create an unmanaged query from an existing id&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LIVE SELECT * FROM users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;live&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;liveOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Improved parameter explicitness
&lt;/h2&gt;

&lt;p&gt;Query functions no longer accept plain strings as table names. You must use the &lt;code&gt;Table&lt;/code&gt; class, which avoids record IDs being mistaken for table names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tables.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usersTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productsTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// main.ts&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;usersTable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Query builder pattern
&lt;/h2&gt;

&lt;p&gt;A new builder pattern lets you configure RPC calls with optional chaining. All query methods support chainable helpers for filtering, limiting, and fetching.&lt;/p&gt;

&lt;p&gt;As a result, &lt;code&gt;update&lt;/code&gt; and &lt;code&gt;upsert&lt;/code&gt; no longer take content as a second argument. You choose how to change records by calling &lt;code&gt;.content()&lt;/code&gt;, &lt;code&gt;.merge()&lt;/code&gt;, &lt;code&gt;.replace()&lt;/code&gt;, or &lt;code&gt;.patch()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Select&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firstname&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lastname&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Update&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;record&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Query method overhaul
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.query()&lt;/code&gt; function has been overhauled to support more use cases, including picking response indexes, automatic JSON output, and streaming responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Execute a query and return results&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM user:foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Collect specific results&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LET $foo = ...; LET $bar = ...; SELECT * FROM $foo; SELECT * FROM $bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Jsonify responses&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Response objects&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;[]]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM product&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Stream responses&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM foo`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isValue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Foo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//  Process a single value with frame.value typed Foo&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isDone&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle completion and access stats with frame.stats&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle error frame.error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SurrealDB does not yet support streaming individual records, but this API is ready for when it does. It works with current SurrealDB versions and is now the only way to obtain query stats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expressions API
&lt;/h2&gt;

&lt;p&gt;A new Expressions API works with the &lt;code&gt;.where()&lt;/code&gt; function and makes it easier to build dynamic expressions. It integrates with the &lt;code&gt;surql&lt;/code&gt; template tag so you can insert parameter-safe expressions anywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkActive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Query method&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userTable&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checkActive&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Custom query&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;surql&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM user WHERE &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checkActive&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Expressions even allow raw insertion&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;surql&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM user &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;WHERE active = true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also serialize expressions to a string with the &lt;code&gt;expr()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BoundQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;expr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alpha&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;beta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;and&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;inside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
            &lt;span class="nf"&gt;between&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Value encode/decode visitor API
&lt;/h2&gt;

&lt;p&gt;For advanced use cases where you need to process SurrealDB value types, you can pass encode or decode visitor callbacks in the Surreal constructor. These run for each value sent to or received from the engine, so you can modify or wrap values before they appear in responses.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Surreal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;codecOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;valueDecodeVisitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;RecordId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RecordId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;surreal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`RETURN hello:world`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RecordId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// foo:bar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Diagnostics API
&lt;/h2&gt;

&lt;p&gt;The Diagnostics API lets you wrap engines and inspect protocol-level communication. It is useful for debugging queries, analysing SDK behaviour, measuring timings, and similar tasks.&lt;/p&gt;

&lt;p&gt;Because it is implemented as a wrapper engine, there is no extra cost unless you use it. We do not recommend using it in production, as it can affect performance and the events may change between versions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Surreal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;driverOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;engines&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;applyDiagnostics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;createRemoteEngines&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each event has a &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;key&lt;/code&gt;, and &lt;code&gt;phase&lt;/code&gt;. The &lt;code&gt;type&lt;/code&gt; identifies the operation, &lt;code&gt;key&lt;/code&gt; is a stable id across phases, and &lt;code&gt;phase&lt;/code&gt; indicates start, progress, or completion. The &lt;code&gt;query&lt;/code&gt; diagnostic exposes the internal chunk stream; you are responsible for stitching queries and batches together. The &lt;code&gt;after&lt;/code&gt; phase includes &lt;code&gt;duration&lt;/code&gt;, &lt;code&gt;success&lt;/code&gt;, and &lt;code&gt;result&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started with the new SDK
&lt;/h2&gt;

&lt;p&gt;You can visit the &lt;a href="https://surrealdb.com/docs/sdk/javascript" rel="noopener noreferrer"&gt;JavaScript SDK documentation&lt;/a&gt; for more information on the new SDK, which has been fully updated to reflect all new changes. It contains many new concept guides explaining how to use the new SDK, and now includes a comprehensive API reference for detailed information.&lt;/p&gt;

&lt;p&gt;Follow the &lt;a href="https://surrealdb.com/docs/sdk/javascript/start" rel="noopener noreferrer"&gt;Quickstart guide&lt;/a&gt; to get started.&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>javascriptsdk</category>
      <category>database</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>File support in SurrealDB 3.0</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Wed, 25 Feb 2026 18:19:04 +0000</pubDate>
      <link>https://forem.com/surrealdb/file-support-in-surrealdb-30-2928</link>
      <guid>https://forem.com/surrealdb/file-support-in-surrealdb-30-2928</guid>
      <description>&lt;h2&gt;
  
  
  File Support
&lt;/h2&gt;

&lt;p&gt;Until now, SurrealDB has always encouraged users to store files externally, referencing them with paths stored in the database. We’re now introducing the first step toward native file support - bringing file storage directly into your database workflow.&lt;/p&gt;

&lt;p&gt;File storage is still an experimental feature at this moment, and we would love to hear from you as you give it a try for your own solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up file storage
&lt;/h2&gt;

&lt;p&gt;To begin, we can start a new SurrealDB server with the experimental &lt;code&gt;files&lt;/code&gt; capability enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal start &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; secret &lt;span class="nt"&gt;--allow-experimental&lt;/span&gt; files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting up file storage can be done with a single &lt;code&gt;DEFINE BUCKET&lt;/code&gt; statement! A bucket can be a physical location, or even in-memory.&lt;/p&gt;

&lt;p&gt;For global usage across all namespaces and databases, you can set a &lt;code&gt;SURREAL_GLOBAL_BUCKET&lt;/code&gt;  environment variable, with an optional &lt;code&gt;SURREAL_GLOBAL_BUCKET_ENFORCED&lt;/code&gt; if you want to ensure that every database uses the same global bucket.&lt;/p&gt;

&lt;p&gt;Let’s define a simple in-memory bucket. Just provide a name, and set the backend to "memory":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;BUCKET&lt;/span&gt; &lt;span class="n"&gt;my_bucket&lt;/span&gt; &lt;span class="n"&gt;BACKEND&lt;/span&gt; &lt;span class="nv"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name of the bucket is also your path to the bucket and everything inside. &lt;/p&gt;

&lt;h2&gt;
  
  
  Storing and accessing files
&lt;/h2&gt;

&lt;p&gt;Once your bucket is defined, you can reference it using a file pointer - a string prefixed with the letter &lt;code&gt;f&lt;/code&gt; to indicate a file path. &lt;/p&gt;

&lt;p&gt;File pointers have their own functions, like &lt;code&gt;.put()&lt;/code&gt; to add bytes, &lt;code&gt;.get()&lt;/code&gt; to see them, &lt;code&gt;.rename()&lt;/code&gt; to rename a file, and &lt;code&gt;.head()&lt;/code&gt; to see its metadata. Let’s use the &lt;code&gt;.get()&lt;/code&gt; function to see if our bucket has a file called &lt;code&gt;tour_of_surrealdb.txt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function returned &lt;code&gt;NONE&lt;/code&gt;, so looks like there’s nothing there.&lt;/p&gt;

&lt;p&gt;Let’s use the &lt;code&gt;.put()&lt;/code&gt; method to add some bytes, creating the file. &lt;code&gt;.put()&lt;/code&gt; can take bytes or a string, which it will automatically convert into bytes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"Welcome to the Tour of SurrealDB!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’re adding the string “Welcome to the Tour of SurrealDB!” &lt;/p&gt;

&lt;p&gt;Now that the file exists, the .get() function will return its as bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;-- Output:&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nv"&gt;"57656C636F6D6520746F2074686520546F7572206F66205375727265616C444221"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Working with file data
&lt;/h2&gt;

&lt;p&gt;If you prefer to view the data differently, SurrealDB also now supports improved type conversions for byte-related types.&lt;/p&gt;

&lt;p&gt;For example, you can cast the result to an array to see each byte as a number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;87&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;116&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;116&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;84&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or cast to a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In cases where the bytes include invalid UTF-8, you can use &lt;code&gt;type::string_lossy()&lt;/code&gt; - a new function that replaces invalid bytes with the &lt;code&gt;�&lt;/code&gt; Unicode replacement character, rather than throwing an error.&lt;/p&gt;

&lt;p&gt;This function is especially useful when working with partially corrupted or inconsistently encoded data. Instead of failing outright, it preserves and returns all valid parts of the string.&lt;/p&gt;

&lt;p&gt;Here’s an example using &lt;code&gt;type::string&lt;/code&gt;, which returns an error because not all the bytes are valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;254&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;-- 'Could not cast into `string` using input `b"537572FF726561FE6C4442"`'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here’s the same input passed through &lt;code&gt;type::string_lossy()&lt;/code&gt;. We can see that there are some valid bytes in there - enough to make out the word SurrealDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;254&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;-- 'Sur�rea�lDB'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SurrealDB also has &lt;a href="https://surrealdb.com/docs/surrealql/functions/database/encoding" rel="noopener noreferrer"&gt;encoding functions&lt;/a&gt; that allow you to encode and decode bytes into SurrealDB types. That means that you can take a type like this object and turn it into bytes...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cbor&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="k"&gt;some&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"data"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;-- Output:&lt;/span&gt;
&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nv"&gt;"A164736F6D656464617461"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then turn it back from bytes into an object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cbor&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nv"&gt;"A164736F6D656464617461"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Output:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;some&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'data'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A takeaway example to get started
&lt;/h2&gt;

&lt;p&gt;Let's take a quick running example to give you some ideas of how you could use files in your own app, in this case an app that stores temporary shopping cart files to disk so that other parts of the app can access them.&lt;/p&gt;

&lt;p&gt;This time we'll define a bucket to a file path instead of memory, which requires &lt;code&gt;SURREAL_BUCKET_FOLDER_ALLOWLIST&lt;/code&gt; to be set so that the database knows that this folder has been approved as the location for file storage. Note that the SurrealDB instance itself is using memory as the backend, but it's able to save files to this directory, getting the best of both worlds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SURREAL_BUCKET_FOLDER_ALLOWLIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/users/your_user_name"&lt;/span&gt; surreal start &lt;span class="nt"&gt;--allow-experimental&lt;/span&gt; files &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After connecting, defining the bucket can be done with a path that begins with &lt;code&gt;file:&lt;/code&gt; and holds the path we have allowed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;BUCKET&lt;/span&gt; &lt;span class="n"&gt;shopping_carts&lt;/span&gt; &lt;span class="n"&gt;BACKEND&lt;/span&gt; &lt;span class="nv"&gt;"file:/users/your_user_name"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since working with files takes a good amount of code, we'll put some functions together to simplify the process. These three will allow us to save, get, and delete files in the &lt;code&gt;shopping_carts&lt;/code&gt; backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Convenience functions to save, decode back into&lt;/span&gt;
&lt;span class="c1"&gt;-- SurrealQL type, and delete&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;save_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"shopping_carts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cbor&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;input&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cbor&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"shopping_carts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;delete_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"shopping_carts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those functions set up, we can play around with a file for a user with the user number 24567. Once you execute the &lt;code&gt;fn::save_file()&lt;/code&gt; function below you will be able to go into the directory yourself to view it. And you can also view and interact with it using SurrealQL via the other functions in the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Save current shopping cart&lt;/span&gt;
&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;save_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"temp_cart_user_24567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;"shirt1"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;last_updated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"temp_cart_user_24567"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Returns { items: ['shirt1', 'deck_of_cards'], last_updated: d'2026-02-25T01:03:24.141080Z' }&lt;/span&gt;

&lt;span class="c1"&gt;-- User adds item, save over file with newer information&lt;/span&gt;
&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;save_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"temp_cart_user_24567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;"shirt1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"deck_of_cards"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;last_updated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"temp_cart_user_24567"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- Returns { items: ['shirt1', 'deck_of_cards'], last_updated: d'2026-02-25T01:06:02.752429Z' }&lt;/span&gt;

&lt;span class="c1"&gt;-- Session is over, delete temp file&lt;/span&gt;
&lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;delete_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"temp_cart_user_24567"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re excited to introduce this first stage of native file support in SurrealDB. As the next step in our journey toward multimodality, we’re working on enabling integrations with object storage solutions such as Amazon S3, allowing you to work with files at scale. If you’re building apps that rely on lightweight file storage or embedded assets, give it a try - and let us know what features you’d like to see next. This is just the beginning.&lt;/p&gt;

&lt;p&gt;Learn more at: &lt;a href="https://surrealdb.com/docs/surrealql/datamodel/files" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealql/datamodel/files&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>database</category>
      <category>multimodal</category>
      <category>filestorage</category>
    </item>
    <item>
      <title>Custom API Endpoints: streamlining your architecture</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Wed, 25 Feb 2026 17:53:23 +0000</pubDate>
      <link>https://forem.com/surrealdb/custom-api-endpoints-streamlining-your-architecture-36oe</link>
      <guid>https://forem.com/surrealdb/custom-api-endpoints-streamlining-your-architecture-36oe</guid>
      <description>&lt;h2&gt;
  
  
  Custom API Endpoints: streamlining your architecture
&lt;/h2&gt;

&lt;p&gt;With the release of SurrealDB 3.0, we’re excited to announce the stabilisation of a powerful feature: &lt;a href="https://surrealdb.com/docs/surrealql/statements/define/api" rel="noopener noreferrer"&gt;&lt;code&gt;DEFINE API&lt;/code&gt;&lt;/a&gt;. This innovative addition allows developers to define database behaviours, set up middleware, and create custom API endpoints directly within the familiar SurrealQL query language.&lt;/p&gt;

&lt;p&gt;Traditionally, applications connect to databases through middleware, resulting in a three-layer architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client → Middleware (API) → Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With SurrealDB's new &lt;code&gt;DEFINE API&lt;/code&gt;, you can simplify your infrastructure significantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client → Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This streamlined approach not only reduces complexity but also enhances performance and ease of management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world use case: implementing rate limits for a social app
&lt;/h2&gt;

&lt;p&gt;Consider a scenario: you've built a popular social application that allows users to see recent comments. You want to provide a free API for anonymous users, but you also need to ensure these guest users don't overload your system resources during peak traffic.&lt;/p&gt;

&lt;p&gt;Here are some ways how you can achieve this efficiently using SurrealDB's &lt;code&gt;DEFINE API&lt;/code&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup: restrict arbitrary queries
&lt;/h3&gt;

&lt;p&gt;To begin, pass in the new &lt;code&gt;--deny-arbitrary-query&lt;/code&gt; flag when starting the SurrealDB server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal start &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; root &lt;span class="nt"&gt;--deny-arbitrary-query&lt;/span&gt; guest &lt;span class="nt"&gt;--allow-experimental&lt;/span&gt; define_api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents certain groups, like guests or record users, from putting their own queries together. We'll add &lt;code&gt;guest&lt;/code&gt; here so that any guest users will have to use the API endpoint instead.&lt;/p&gt;

&lt;p&gt;You can then connect using Surrealist, or with the following command in a new terminal window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal sql &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining your API Endpoint
&lt;/h3&gt;

&lt;p&gt;Next, we'll add a &lt;code&gt;DEFINE API&lt;/code&gt; statement to define the endpoint. The statement will start with the path &lt;code&gt;/get_latest&lt;/code&gt; , and to specify that it can only be used for &lt;code&gt;GET&lt;/code&gt; requests, we will include it after the &lt;code&gt;FOR&lt;/code&gt; keyword.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;OVERWRITE&lt;/span&gt; &lt;span class="nv"&gt;"/get_latest"&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add middleware functions
&lt;/h3&gt;

&lt;p&gt;We can follow this up with some middleware by passing in some of SurrealDB's &lt;a href="https://surrealdb.com/docs/surrealql/functions/database/api" rel="noopener noreferrer"&gt;API functions&lt;/a&gt;. We'll use  &lt;code&gt;api::timeout&lt;/code&gt; to give guest users up to 50 milliseconds of server time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt;
    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Define the RETURN data
&lt;/h3&gt;

&lt;p&gt;And then we'll finish up the statement by adding what it actually returns: a &lt;code&gt;SELECT&lt;/code&gt; statement for all &lt;code&gt;comment&lt;/code&gt; records that have been created over the past ten minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]..&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;             
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In summary, that gives us a &lt;code&gt;DEFINE API&lt;/code&gt; statement that looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;OVERWRITE&lt;/span&gt; &lt;span class="nv"&gt;"/get_latest"&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;
    &lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt;
        &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]..&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Testing your endpoint
&lt;/h3&gt;

&lt;p&gt;You can test it from SurrealQL itself by first adding a comment or two and then calling the  &lt;code&gt;api::invoke&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;user_says&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Nice blog!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;user_says&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Can't wait for the new version"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"/get_latest"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or externally at:  &lt;code&gt;/api/:namespace/:database/:endpoint_name&lt;/code&gt;. Here's what our defined endpoint will look like with a namespace called &lt;code&gt;test_ns&lt;/code&gt; and a database called &lt;code&gt;test_db&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8000/api/test_ns/test_db/get_latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means that a curl command like the following will be enough to get the latest comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:8000/api/test_ns/test_db/get_latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output should look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"comment:[d'2026-02-12T01:10:02.445607Z']"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"user_says"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Can't wait for the new version"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"comment:[d'2026-02-12T01:08:33.709073Z']"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"user_says"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Nice blog!"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that’s not all. The &lt;code&gt;MIDDLEWARE&lt;/code&gt; clause in a &lt;code&gt;DEFINE API&lt;/code&gt; statement can take custom middleware as well that you define yourself using a regular &lt;code&gt;DEFINE FUNCTION&lt;/code&gt; statement. These functions are automatically populated with the user request and a way to get the current state of the response as you put it together.&lt;/p&gt;

&lt;p&gt;There are a lot of examples in the documentation to show how it works, but here’s a simple example to get you started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;add_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;": "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="nv"&gt;"/custom_with_args"&lt;/span&gt;
    &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;
        &lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt;
            &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;add_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"PREFIX"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"original message"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;DEFINE API&lt;/code&gt;, SurrealDB brings the power of API design directly into the database - no external layers, no additional frameworks. Whether you’re enforcing limits, defining access, or building powerful custom endpoints, this feature helps you simplify your architecture and move faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore further
&lt;/h2&gt;

&lt;p&gt;Dive deeper into SurrealDB's powerful API management capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://surrealdb.com/docs/surrealql/functions/database/api" rel="noopener noreferrer"&gt;API Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://surrealdb.com/docs/surrealql/statements/define/api" rel="noopener noreferrer"&gt;DEFINE API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://surrealdb.com/docs/surrealdb/integration/http#custom" rel="noopener noreferrer"&gt;HTTP Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://surrealdb.com/docs/surrealdb/security/capabilities#arbitrary-queries" rel="noopener noreferrer"&gt;Managing Arbitrary Queries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Harness the power of &lt;code&gt;DEFINE API&lt;/code&gt; and redefine what's possible with SurrealDB 3.0.&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>software</category>
      <category>api</category>
      <category>database</category>
    </item>
    <item>
      <title>SurrealDB 3.0 benchmarks: a new foundation for performance</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Thu, 19 Feb 2026 21:11:12 +0000</pubDate>
      <link>https://forem.com/surrealdb/surrealdb-30-benchmarks-a-new-foundation-for-performance-4ofm</link>
      <guid>https://forem.com/surrealdb/surrealdb-30-benchmarks-a-new-foundation-for-performance-4ofm</guid>
      <description>&lt;h2&gt;
  
  
  SurrealDB 3.0 benchmarks: a new foundation for performance
&lt;/h2&gt;

&lt;p&gt;When we began our benchmarking journey with SurrealDB 2.0, we set out to answer a simple but important question: how does SurrealDB perform in the real world?&lt;/p&gt;

&lt;p&gt;Since then, SurrealDB has seen rapidly growing traction - including enterprises like Tencent and &lt;a href="http://Later.com" rel="noopener noreferrer"&gt;Later.com&lt;/a&gt; running in production at significant scale, across high-throughput, business-critical workloads. As adoption has increased, so has the importance of demonstrating not just raw performance, but predictable, production-ready performance.&lt;/p&gt;

&lt;p&gt;Last year we shared those early results openly, along with our methodology and learnings, in our &lt;a href="https://surrealdb.com/blog/beginning-our-benchmarking-journey" rel="noopener noreferrer"&gt;first benchmarking report&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With SurrealDB 3.0, we’re taking the next major step in that journey. Today, we’re excited to announce our new benchmarks for SurrealDB 3.0 - and more importantly, the architectural changes that make these results possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarking a multi-model database
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://surrealdb.com/blog/beginning-our-benchmarking-journey#the-challenge-of-multi-model-benchmarking" rel="noopener noreferrer"&gt;As mentioned&lt;/a&gt; in our previous benchmarking blog, benchmarking a multi-model database is more complex than comparing single-purpose systems. SurrealDB unifies relational, document, graph, time-series, key-value, vector, geospatial, and full-text workloads in one engine, spanning everything from embedded deployments to distributed clusters.&lt;/p&gt;

&lt;p&gt;This versatility makes fair benchmarking challenging, since different databases vary widely in durability guarantees, disk flushing behaviour, and configuration trade-offs. For our benchmarks, we’ve done our best to keep comparisons as balanced and transparent as possible - and we always welcome feedback on how to improve the methodology further.&lt;/p&gt;

&lt;p&gt;Because SurrealDB can replace entire stacks of databases and supporting tools, the best way to benchmark SurrealDB is often through a like-for-like proof of concept against multiple existing systems (we have customers like Tencent who reduced nine separate tools down to one with SurrealDB).&lt;/p&gt;

&lt;h2&gt;
  
  
  Our open-source benchmarking tool
&lt;/h2&gt;

&lt;p&gt;We built our own internal benchmarking tool - &lt;a href="https://github.com/surrealdb/crud-bench" rel="noopener noreferrer"&gt;crud-bench&lt;/a&gt; - in Rust to properly evaluate SurrealDB across the wide range of workloads it supports. Unlike many generic benchmarking suites, crud-bench is designed to test not just basic CRUD operations, but also the broader feature set that makes SurrealDB unique, across embedded, networked, and remote deployments. It allows us to compare SQL, NoSQL, key-value, and embedded systems in a consistent way, while continuously measuring the performance impact of changes inside SurrealDB itself. For more details on how the tool work, check the &lt;a href="https://github.com/surrealdb/crud-bench" rel="noopener noreferrer"&gt;crud-bench GitHub repo&lt;/a&gt; and &lt;a href="https://surrealdb.com/blog/beginning-our-benchmarking-journey#how-does-it-work" rel="noopener noreferrer"&gt;the explanation in the previous 2.0 benchmark blog&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  SurrealDB 3.0: a new execution engine
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The foundation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SurrealDB 3.0 has been largely focused on stability and getting the right foundations in place. We rearchitected the internals in ways that would have been nearly impossible under the previous design, and the performance results we're seeing now are a direct consequence of that work.&lt;/p&gt;

&lt;p&gt;The biggest change is the query execution engine. We took inspiration from the broader database community and moved SurrealQL's execution model to a more standard pipeline: &lt;strong&gt;AST → LogicalPlan → ExecutionPlan&lt;/strong&gt;, with basic query optimisations applied throughout. The new engine is fully streaming internally, and in a future minor release we will make it stream end-to-end.&lt;/p&gt;

&lt;p&gt;Right now, the new execution engine covers read-only statements. In the coming minor releases we'll expand it to handle all of our workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;br&gt;
Now let’s get to the benchmarks themselves. The tl;dr for the numbers below are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Graph queries are between 8-22x faster.&lt;/li&gt;
&lt;li&gt;Large table scans with &lt;code&gt;LIMIT&lt;/code&gt;, &lt;code&gt;START&lt;/code&gt;, and &lt;code&gt;START + LIMIT&lt;/code&gt; are 3-6x faster.&lt;/li&gt;
&lt;li&gt;The query planner is smarter. Queries like &lt;code&gt;SELECT * FROM table WHERE id = record:42&lt;/code&gt; that previously triggered full table scans now resolve in sub-millisecond time, a 4000x+ improvement. This is especially important for a query language like SurrealQL which is very close to standard SQL in which &lt;code&gt;WHERE id =&lt;/code&gt; is one of the most common patterns.&lt;/li&gt;
&lt;li&gt;ORDER BY queries are 3-4x faster across all storage engines.&lt;/li&gt;
&lt;li&gt;HNSW vector search is up to 8x faster. Indexed vector similarity queries dropped from ~35 seconds to ~4.5 seconds.&lt;/li&gt;
&lt;li&gt;The new execution model enables significantly improved concurrency and throughput.&lt;/li&gt;
&lt;li&gt;Complex GROUP BY queries with multiple aggregations are up to 55% faster, with the biggest gains on multi-aggregation and deduplication workloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benchmark hardware
&lt;/h2&gt;

&lt;p&gt;The benchmarks below were run on the following system:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;AMD Ryzen Threadripper 9970X&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.co.uk/AMD-Ryzen-Threadripper-9970X-5-40GHz/dp/B0FJ6FJN2H" rel="noopener noreferrer"&gt;amazon.co.uk&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;Samsung DDR5 4800MHz 64GB RDIMM&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.co.uk/Samsung-4800MHz-PC5-38400-Registered-M321R8GA0BB0-CQK-Green/dp/B0CFG7THWM" rel="noopener noreferrer"&gt;amazon.co.uk&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSD&lt;/td&gt;
&lt;td&gt;Lexar EQ790 4TB NVMe&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.co.uk/dp/B0DNMK2Z1C" rel="noopener noreferrer"&gt;amazon.co.uk&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Motherboard&lt;/td&gt;
&lt;td&gt;ASUS PRO WS TRX50-SAGE WIFI A&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.ballicom.co.uk/90mb1le0-m0eay0-asus-pro-ws-trx50-sage-wifi-a.p1681724.html" rel="noopener noreferrer"&gt;ballicom.co.uk&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU Cooler&lt;/td&gt;
&lt;td&gt;Arctic Freezer 4U-M&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.scan.co.uk/products/arctic-freezer-4u-m-ampere-server-workstation-cpu-cooler-2x-120mm-fans-altra-4926-xeon-threadripper" rel="noopener noreferrer"&gt;scan.co.uk&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Power Supply&lt;/td&gt;
&lt;td&gt;CORSAIR RM1000x&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.co.uk/CORSAIR-RM1000x-Modular-Low-Noise-Supply/dp/B0D9C1HG19" rel="noopener noreferrer"&gt;amazon.co.uk&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU&lt;/td&gt;
&lt;td&gt;PowerColor AMD Radeon RX 7600 Fighter 8GB&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.amazon.co.uk/dp/B0C488N4BF" rel="noopener noreferrer"&gt;amazon.co.uk&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Case&lt;/td&gt;
&lt;td&gt;In-Win IW-R400-01N 4U&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.theserver.group/products/in-win-iw-r400-01n-4u-rack-server-chassis-for-extended-motherboards-w-lockable-door-ideal-for-cctv-applications" rel="noopener noreferrer"&gt;servercase.co.uk&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Benchmarks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In-Memory&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Test&lt;/th&gt;
      &lt;th&gt;v3 vs. v2&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;SurrealDB 2&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;SurrealDB 3&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;Mean %&lt;/th&gt;
      &lt;th&gt;OP/s %&lt;/th&gt;
      &lt;th&gt;Mean (ms)&lt;/th&gt;
      &lt;th&gt;OP/s&lt;/th&gt;
      &lt;th&gt;Mean (ms)&lt;/th&gt;
      &lt;th&gt;OP/s&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;[C]reate&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-62.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+168.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1.94&lt;/td&gt;
      &lt;td&gt;147538.6&lt;/td&gt;
      &lt;td&gt;0.72&lt;/td&gt;
      &lt;td&gt;396504.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]ead&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+56.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-35.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;0.37&lt;/td&gt;
      &lt;td&gt;757976.6&lt;/td&gt;
      &lt;td&gt;0.58&lt;/td&gt;
      &lt;td&gt;488252.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[U]pdate&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-71.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+249.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2.58&lt;/td&gt;
      &lt;td&gt;111466.6&lt;/td&gt;
      &lt;td&gt;0.73&lt;/td&gt;
      &lt;td&gt;389693.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::count_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-44.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+64.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;204.97&lt;/td&gt;
      &lt;td&gt;1277.8&lt;/td&gt;
      &lt;td&gt;113.99&lt;/td&gt;
      &lt;td&gt;2100.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_id (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-67.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+166.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.32&lt;/td&gt;
      &lt;td&gt;47510.8&lt;/td&gt;
      &lt;td&gt;1.73&lt;/td&gt;
      &lt;td&gt;126780.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-85.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+550.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;17.45&lt;/td&gt;
      &lt;td&gt;15053.5&lt;/td&gt;
      &lt;td&gt;2.46&lt;/td&gt;
      &lt;td&gt;97982.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_start_id (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+45.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-28.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7.45&lt;/td&gt;
      &lt;td&gt;33909.7&lt;/td&gt;
      &lt;td&gt;10.81&lt;/td&gt;
      &lt;td&gt;24360.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_start_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-38.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+64.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;18.11&lt;/td&gt;
      &lt;td&gt;14419.5&lt;/td&gt;
      &lt;td&gt;11.14&lt;/td&gt;
      &lt;td&gt;23733.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_id (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+34.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-20.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;0.49&lt;/td&gt;
      &lt;td&gt;401422.2&lt;/td&gt;
      &lt;td&gt;0.66&lt;/td&gt;
      &lt;td&gt;319614.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_id_eq (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-100.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+436179.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;3935.65&lt;/td&gt;
      &lt;td&gt;70.9&lt;/td&gt;
      &lt;td&gt;0.68&lt;/td&gt;
      &lt;td&gt;309409.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_gt (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+18.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-17.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5242.39&lt;/td&gt;
      &lt;td&gt;52.9&lt;/td&gt;
      &lt;td&gt;6235.40&lt;/td&gt;
      &lt;td&gt;43.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_in (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-36.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+55.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;9745.80&lt;/td&gt;
      &lt;td&gt;28.4&lt;/td&gt;
      &lt;td&gt;6221.91&lt;/td&gt;
      &lt;td&gt;44.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_multi_and (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+2.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-3.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6065.92&lt;/td&gt;
      &lt;td&gt;45.7&lt;/td&gt;
      &lt;td&gt;6238.46&lt;/td&gt;
      &lt;td&gt;44.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_order_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+10.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-8.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5196.36&lt;/td&gt;
      &lt;td&gt;53.3&lt;/td&gt;
      &lt;td&gt;5762.22&lt;/td&gt;
      &lt;td&gt;48.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_order_desc_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+12.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-11.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5191.97&lt;/td&gt;
      &lt;td&gt;53.5&lt;/td&gt;
      &lt;td&gt;5842.42&lt;/td&gt;
      &lt;td&gt;47.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_multi_order_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-7.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+9.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6271.61&lt;/td&gt;
      &lt;td&gt;44.3&lt;/td&gt;
      &lt;td&gt;5830.96&lt;/td&gt;
      &lt;td&gt;48.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_omit_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-84.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+465.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;18.09&lt;/td&gt;
      &lt;td&gt;14490.0&lt;/td&gt;
      &lt;td&gt;2.83&lt;/td&gt;
      &lt;td&gt;81951.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fields_where_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+589.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-85.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;16.15&lt;/td&gt;
      &lt;td&gt;16178.0&lt;/td&gt;
      &lt;td&gt;111.41&lt;/td&gt;
      &lt;td&gt;2305.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_order_by (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-69.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+231.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;17270.90&lt;/td&gt;
      &lt;td&gt;16.1&lt;/td&gt;
      &lt;td&gt;5274.94&lt;/td&gt;
      &lt;td&gt;53.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_order_by_multi (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-70.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+235.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;17387.92&lt;/td&gt;
      &lt;td&gt;16.0&lt;/td&gt;
      &lt;td&gt;5192.82&lt;/td&gt;
      &lt;td&gt;53.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_count (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-9.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+8.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5989.63&lt;/td&gt;
      &lt;td&gt;46.4&lt;/td&gt;
      &lt;td&gt;5436.81&lt;/td&gt;
      &lt;td&gt;50.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_sum (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-16.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+19.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6754.21&lt;/td&gt;
      &lt;td&gt;41.1&lt;/td&gt;
      &lt;td&gt;5635.44&lt;/td&gt;
      &lt;td&gt;49.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_avg (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-18.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+20.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6766.85&lt;/td&gt;
      &lt;td&gt;40.9&lt;/td&gt;
      &lt;td&gt;5529.88&lt;/td&gt;
      &lt;td&gt;49.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_multi_agg (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-36.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+53.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;9738.26&lt;/td&gt;
      &lt;td&gt;28.5&lt;/td&gt;
      &lt;td&gt;6203.12&lt;/td&gt;
      &lt;td&gt;43.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-25.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+33.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7492.08&lt;/td&gt;
      &lt;td&gt;37.1&lt;/td&gt;
      &lt;td&gt;5561.68&lt;/td&gt;
      &lt;td&gt;49.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_order_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-23.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+31.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7603.12&lt;/td&gt;
      &lt;td&gt;36.5&lt;/td&gt;
      &lt;td&gt;5798.60&lt;/td&gt;
      &lt;td&gt;48.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_where (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-12.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+13.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7411.37&lt;/td&gt;
      &lt;td&gt;37.5&lt;/td&gt;
      &lt;td&gt;6459.82&lt;/td&gt;
      &lt;td&gt;42.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_dedup_agg (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-35.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+54.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;9735.38&lt;/td&gt;
      &lt;td&gt;28.5&lt;/td&gt;
      &lt;td&gt;6278.25&lt;/td&gt;
      &lt;td&gt;43.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_split (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-55.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+111.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;19.71&lt;/td&gt;
      &lt;td&gt;13415.2&lt;/td&gt;
      &lt;td&gt;8.85&lt;/td&gt;
      &lt;td&gt;28351.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fetch (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-80.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+401.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;39.07&lt;/td&gt;
      &lt;td&gt;6802.6&lt;/td&gt;
      &lt;td&gt;7.58&lt;/td&gt;
      &lt;td&gt;34134.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fetch_where_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+291.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-74.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;32.16&lt;/td&gt;
      &lt;td&gt;8366.1&lt;/td&gt;
      &lt;td&gt;125.95&lt;/td&gt;
      &lt;td&gt;2146.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth1 (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-60.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+124.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2.30&lt;/td&gt;
      &lt;td&gt;105137.8&lt;/td&gt;
      &lt;td&gt;0.92&lt;/td&gt;
      &lt;td&gt;235676.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth2 (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-72.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+222.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6.16&lt;/td&gt;
      &lt;td&gt;42125.8&lt;/td&gt;
      &lt;td&gt;1.67&lt;/td&gt;
      &lt;td&gt;135637.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth3 (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-83.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+449.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;18.02&lt;/td&gt;
      &lt;td&gt;15001.7&lt;/td&gt;
      &lt;td&gt;3.02&lt;/td&gt;
      &lt;td&gt;82363.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_in_depth1 (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-58.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+129.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2.72&lt;/td&gt;
      &lt;td&gt;90892.6&lt;/td&gt;
      &lt;td&gt;1.12&lt;/td&gt;
      &lt;td&gt;208621.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_bidirectional (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-67.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+193.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4.22&lt;/td&gt;
      &lt;td&gt;57673.0&lt;/td&gt;
      &lt;td&gt;1.39&lt;/td&gt;
      &lt;td&gt;169188.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_edge_filter (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-5.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-4.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1.19&lt;/td&gt;
      &lt;td&gt;197478.8&lt;/td&gt;
      &lt;td&gt;1.12&lt;/td&gt;
      &lt;td&gt;189149.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_out (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-84.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+504.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;148.68&lt;/td&gt;
      &lt;td&gt;1886.3&lt;/td&gt;
      &lt;td&gt;23.32&lt;/td&gt;
      &lt;td&gt;11405.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_out_where (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-72.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+243.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;51.38&lt;/td&gt;
      &lt;td&gt;5401.8&lt;/td&gt;
      &lt;td&gt;14.40&lt;/td&gt;
      &lt;td&gt;18555.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_count (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-72.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+247.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;33.74&lt;/td&gt;
      &lt;td&gt;8098.3&lt;/td&gt;
      &lt;td&gt;9.32&lt;/td&gt;
      &lt;td&gt;28157.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_depth2_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-88.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+696.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;78.83&lt;/td&gt;
      &lt;td&gt;3529.7&lt;/td&gt;
      &lt;td&gt;9.41&lt;/td&gt;
      &lt;td&gt;28104.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_where (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-24.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+32.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2.67&lt;/td&gt;
      &lt;td&gt;91237.9&lt;/td&gt;
      &lt;td&gt;2.01&lt;/td&gt;
      &lt;td&gt;120503.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_group_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-42.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+69.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;3.36&lt;/td&gt;
      &lt;td&gt;74807.0&lt;/td&gt;
      &lt;td&gt;1.93&lt;/td&gt;
      &lt;td&gt;126560.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_group_by (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-42.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+61.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;3.25&lt;/td&gt;
      &lt;td&gt;78504.3&lt;/td&gt;
      &lt;td&gt;1.88&lt;/td&gt;
      &lt;td&gt;126496.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_inline (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-49.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+95.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;724.85&lt;/td&gt;
      &lt;td&gt;377.8&lt;/td&gt;
      &lt;td&gt;368.70&lt;/td&gt;
      &lt;td&gt;738.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_count (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+116.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-52.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;28.49&lt;/td&gt;
      &lt;td&gt;9262.4&lt;/td&gt;
      &lt;td&gt;61.72&lt;/td&gt;
      &lt;td&gt;4406.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_from (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-38.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+60.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2918.72&lt;/td&gt;
      &lt;td&gt;94.6&lt;/td&gt;
      &lt;td&gt;1801.28&lt;/td&gt;
      &lt;td&gt;152.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::pipeline_filter_group_order (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-29.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+41.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;9341.45&lt;/td&gt;
      &lt;td&gt;29.7&lt;/td&gt;
      &lt;td&gt;6613.75&lt;/td&gt;
      &lt;td&gt;42.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_standard (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+34.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-26.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;3910.21&lt;/td&gt;
      &lt;td&gt;71.0&lt;/td&gt;
      &lt;td&gt;5241.54&lt;/td&gt;
      &lt;td&gt;51.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_standard&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-49.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+98.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1003.26&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;504.19&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_standard::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-42.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+60.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4.11&lt;/td&gt;
      &lt;td&gt;61150.4&lt;/td&gt;
      &lt;td&gt;2.36&lt;/td&gt;
      &lt;td&gt;98303.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_standard&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-78.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+370.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;380.54&lt;/td&gt;
      &lt;td&gt;2.6&lt;/td&gt;
      &lt;td&gt;80.67&lt;/td&gt;
      &lt;td&gt;12.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_composite (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+1.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-4.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6101.19&lt;/td&gt;
      &lt;td&gt;45.7&lt;/td&gt;
      &lt;td&gt;6171.27&lt;/td&gt;
      &lt;td&gt;43.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_composite&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+0.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-1.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;502.14&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
      &lt;td&gt;506.50&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_composite::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-50.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+107.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1504.21&lt;/td&gt;
      &lt;td&gt;176.7&lt;/td&gt;
      &lt;td&gt;745.47&lt;/td&gt;
      &lt;td&gt;366.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_composite&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-73.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+278.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;338.82&lt;/td&gt;
      &lt;td&gt;3.0&lt;/td&gt;
      &lt;td&gt;89.50&lt;/td&gt;
      &lt;td&gt;11.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_range_merged (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-25.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+33.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7595.90&lt;/td&gt;
      &lt;td&gt;36.5&lt;/td&gt;
      &lt;td&gt;5641.66&lt;/td&gt;
      &lt;td&gt;48.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_range_merged&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-50.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+99.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1004.29&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;502.14&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_range_merged::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-52.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+114.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;478.07&lt;/td&gt;
      &lt;td&gt;555.2&lt;/td&gt;
      &lt;td&gt;227.80&lt;/td&gt;
      &lt;td&gt;1190.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_range_merged&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-74.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+289.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;360.32&lt;/td&gt;
      &lt;td&gt;2.8&lt;/td&gt;
      &lt;td&gt;92.19&lt;/td&gt;
      &lt;td&gt;10.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_in (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-25.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+34.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7232.84&lt;/td&gt;
      &lt;td&gt;38.4&lt;/td&gt;
      &lt;td&gt;5404.26&lt;/td&gt;
      &lt;td&gt;51.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_in&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-50.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+99.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1004.29&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;502.40&lt;/td&gt;
      &lt;td&gt;2.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_in::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-61.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+160.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;25.35&lt;/td&gt;
      &lt;td&gt;10437.8&lt;/td&gt;
      &lt;td&gt;9.73&lt;/td&gt;
      &lt;td&gt;27169.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_in&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-77.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+352.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;373.89&lt;/td&gt;
      &lt;td&gt;2.7&lt;/td&gt;
      &lt;td&gt;82.78&lt;/td&gt;
      &lt;td&gt;12.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_fulltext&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-93.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+1300.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;51134.46&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;3511.30&lt;/td&gt;
      &lt;td&gt;0.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_fulltext::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+237.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-69.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;225.31&lt;/td&gt;
      &lt;td&gt;1216.5&lt;/td&gt;
      &lt;td&gt;760.70&lt;/td&gt;
      &lt;td&gt;373.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_fulltext&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+98.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-49.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;845.06&lt;/td&gt;
      &lt;td&gt;1.2&lt;/td&gt;
      &lt;td&gt;1679.87&lt;/td&gt;
      &lt;td&gt;0.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_hnsw&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-32.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+60.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;18604.03&lt;/td&gt;
      &lt;td&gt;0.1&lt;/td&gt;
      &lt;td&gt;12529.66&lt;/td&gt;
      &lt;td&gt;0.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_hnsw::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-87.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+671.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;38581.61&lt;/td&gt;
      &lt;td&gt;7.3&lt;/td&gt;
      &lt;td&gt;4847.09&lt;/td&gt;
      &lt;td&gt;56.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_hnsw&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-82.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+456.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2190.34&lt;/td&gt;
      &lt;td&gt;0.5&lt;/td&gt;
      &lt;td&gt;390.78&lt;/td&gt;
      &lt;td&gt;2.6&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;SurrealKV&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Test&lt;/th&gt;
      &lt;th&gt;v3 vs. v2&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;SurrealDB 2&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;SurrealDB 3&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;Mean %&lt;/th&gt;
      &lt;th&gt;OP/s %&lt;/th&gt;
      &lt;th&gt;Mean (ms)&lt;/th&gt;
      &lt;th&gt;OP/s&lt;/th&gt;
      &lt;th&gt;Mean (ms)&lt;/th&gt;
      &lt;th&gt;OP/s&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;[C]reate&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-96.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+2579.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;221.27&lt;/td&gt;
      &lt;td&gt;1299.7&lt;/td&gt;
      &lt;td&gt;8.25&lt;/td&gt;
      &lt;td&gt;34830.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]ead&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+76.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-42.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;0.34&lt;/td&gt;
      &lt;td&gt;828781.5&lt;/td&gt;
      &lt;td&gt;0.60&lt;/td&gt;
      &lt;td&gt;477557.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[U]pdate&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-96.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+2550.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;222.01&lt;/td&gt;
      &lt;td&gt;1295.3&lt;/td&gt;
      &lt;td&gt;8.37&lt;/td&gt;
      &lt;td&gt;34330.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::count_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-53.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+89.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;303.53&lt;/td&gt;
      &lt;td&gt;917.2&lt;/td&gt;
      &lt;td&gt;142.73&lt;/td&gt;
      &lt;td&gt;1738.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_id (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-68.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+170.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6.51&lt;/td&gt;
      &lt;td&gt;40313.3&lt;/td&gt;
      &lt;td&gt;2.07&lt;/td&gt;
      &lt;td&gt;108861.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-84.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+460.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;16.51&lt;/td&gt;
      &lt;td&gt;16063.4&lt;/td&gt;
      &lt;td&gt;2.56&lt;/td&gt;
      &lt;td&gt;89964.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_start_id (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-59.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+132.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;14.27&lt;/td&gt;
      &lt;td&gt;18977.0&lt;/td&gt;
      &lt;td&gt;5.82&lt;/td&gt;
      &lt;td&gt;44194.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_start_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-71.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+241.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;21.48&lt;/td&gt;
      &lt;td&gt;12560.3&lt;/td&gt;
      &lt;td&gt;6.10&lt;/td&gt;
      &lt;td&gt;42894.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_id (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+39.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-25.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;0.51&lt;/td&gt;
      &lt;td&gt;400808.0&lt;/td&gt;
      &lt;td&gt;0.71&lt;/td&gt;
      &lt;td&gt;299184.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_id_eq (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-100.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+393137.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;3478.94&lt;/td&gt;
      &lt;td&gt;81.6&lt;/td&gt;
      &lt;td&gt;0.69&lt;/td&gt;
      &lt;td&gt;320960.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_gt (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+30.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-25.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4666.15&lt;/td&gt;
      &lt;td&gt;60.8&lt;/td&gt;
      &lt;td&gt;6071.58&lt;/td&gt;
      &lt;td&gt;45.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_in (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-34.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+48.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;9275.42&lt;/td&gt;
      &lt;td&gt;30.5&lt;/td&gt;
      &lt;td&gt;6071.93&lt;/td&gt;
      &lt;td&gt;45.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_multi_and (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+9.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-11.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5509.79&lt;/td&gt;
      &lt;td&gt;51.2&lt;/td&gt;
      &lt;td&gt;6044.50&lt;/td&gt;
      &lt;td&gt;45.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_order_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+18.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-17.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4650.07&lt;/td&gt;
      &lt;td&gt;61.0&lt;/td&gt;
      &lt;td&gt;5504.01&lt;/td&gt;
      &lt;td&gt;50.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_order_desc_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+25.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-21.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4623.38&lt;/td&gt;
      &lt;td&gt;61.4&lt;/td&gt;
      &lt;td&gt;5777.77&lt;/td&gt;
      &lt;td&gt;48.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_multi_order_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-1.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+0.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5692.97&lt;/td&gt;
      &lt;td&gt;49.8&lt;/td&gt;
      &lt;td&gt;5590.58&lt;/td&gt;
      &lt;td&gt;50.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_omit_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-76.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+316.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;17.20&lt;/td&gt;
      &lt;td&gt;15569.4&lt;/td&gt;
      &lt;td&gt;3.97&lt;/td&gt;
      &lt;td&gt;64774.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fields_where_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+545.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-85.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;16.06&lt;/td&gt;
      &lt;td&gt;16653.9&lt;/td&gt;
      &lt;td&gt;103.59&lt;/td&gt;
      &lt;td&gt;2450.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_order_by (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-68.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+210.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;15714.24&lt;/td&gt;
      &lt;td&gt;17.9&lt;/td&gt;
      &lt;td&gt;5016.71&lt;/td&gt;
      &lt;td&gt;55.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_order_by_multi (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-68.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+214.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;15943.91&lt;/td&gt;
      &lt;td&gt;17.6&lt;/td&gt;
      &lt;td&gt;5055.99&lt;/td&gt;
      &lt;td&gt;55.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_count (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+19.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-18.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4391.26&lt;/td&gt;
      &lt;td&gt;64.7&lt;/td&gt;
      &lt;td&gt;5236.94&lt;/td&gt;
      &lt;td&gt;53.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_sum (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+9.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-10.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5024.47&lt;/td&gt;
      &lt;td&gt;56.5&lt;/td&gt;
      &lt;td&gt;5485.56&lt;/td&gt;
      &lt;td&gt;50.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_avg (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+8.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-10.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5023.77&lt;/td&gt;
      &lt;td&gt;56.5&lt;/td&gt;
      &lt;td&gt;5437.21&lt;/td&gt;
      &lt;td&gt;50.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_multi_agg (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-23.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+26.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7942.62&lt;/td&gt;
      &lt;td&gt;35.6&lt;/td&gt;
      &lt;td&gt;6081.36&lt;/td&gt;
      &lt;td&gt;45.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-5.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+3.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5757.57&lt;/td&gt;
      &lt;td&gt;49.2&lt;/td&gt;
      &lt;td&gt;5418.85&lt;/td&gt;
      &lt;td&gt;51.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_order_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-5.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+2.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5832.72&lt;/td&gt;
      &lt;td&gt;48.6&lt;/td&gt;
      &lt;td&gt;5534.30&lt;/td&gt;
      &lt;td&gt;49.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_where (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+5.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-7.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6046.34&lt;/td&gt;
      &lt;td&gt;46.9&lt;/td&gt;
      &lt;td&gt;6354.95&lt;/td&gt;
      &lt;td&gt;43.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_dedup_agg (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-23.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+26.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7959.49&lt;/td&gt;
      &lt;td&gt;35.5&lt;/td&gt;
      &lt;td&gt;6055.39&lt;/td&gt;
      &lt;td&gt;45.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_split (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-51.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+92.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;18.00&lt;/td&gt;
      &lt;td&gt;14812.0&lt;/td&gt;
      &lt;td&gt;8.66&lt;/td&gt;
      &lt;td&gt;28485.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fetch (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-75.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+291.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;35.99&lt;/td&gt;
      &lt;td&gt;7481.3&lt;/td&gt;
      &lt;td&gt;8.96&lt;/td&gt;
      &lt;td&gt;29309.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fetch_where_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+271.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-72.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;30.70&lt;/td&gt;
      &lt;td&gt;8690.9&lt;/td&gt;
      &lt;td&gt;114.08&lt;/td&gt;
      &lt;td&gt;2375.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth1 (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-69.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+181.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.00&lt;/td&gt;
      &lt;td&gt;52630.3&lt;/td&gt;
      &lt;td&gt;1.55&lt;/td&gt;
      &lt;td&gt;148160.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth2 (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-77.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+315.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;14.70&lt;/td&gt;
      &lt;td&gt;18594.4&lt;/td&gt;
      &lt;td&gt;3.29&lt;/td&gt;
      &lt;td&gt;77306.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth3 (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-80.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+382.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;42.04&lt;/td&gt;
      &lt;td&gt;6604.0&lt;/td&gt;
      &lt;td&gt;8.30&lt;/td&gt;
      &lt;td&gt;31839.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_in_depth1 (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-71.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+202.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4.95&lt;/td&gt;
      &lt;td&gt;53273.6&lt;/td&gt;
      &lt;td&gt;1.43&lt;/td&gt;
      &lt;td&gt;161031.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_bidirectional (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-77.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+297.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;11.14&lt;/td&gt;
      &lt;td&gt;24621.8&lt;/td&gt;
      &lt;td&gt;2.54&lt;/td&gt;
      &lt;td&gt;97824.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_edge_filter (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-49.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+87.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2.41&lt;/td&gt;
      &lt;td&gt;100491.2&lt;/td&gt;
      &lt;td&gt;1.23&lt;/td&gt;
      &lt;td&gt;188866.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_out (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-83.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+508.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;436.45&lt;/td&gt;
      &lt;td&gt;643.7&lt;/td&gt;
      &lt;td&gt;70.48&lt;/td&gt;
      &lt;td&gt;3918.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_out_where (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-78.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+361.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;173.91&lt;/td&gt;
      &lt;td&gt;1610.2&lt;/td&gt;
      &lt;td&gt;36.83&lt;/td&gt;
      &lt;td&gt;7433.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_count (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-82.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+436.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;94.20&lt;/td&gt;
      &lt;td&gt;2968.2&lt;/td&gt;
      &lt;td&gt;16.70&lt;/td&gt;
      &lt;td&gt;15934.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_depth2_limit (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-80.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+413.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;246.73&lt;/td&gt;
      &lt;td&gt;1139.2&lt;/td&gt;
      &lt;td&gt;47.07&lt;/td&gt;
      &lt;td&gt;5844.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_where (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-61.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+139.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.50&lt;/td&gt;
      &lt;td&gt;48232.8&lt;/td&gt;
      &lt;td&gt;2.10&lt;/td&gt;
      &lt;td&gt;115625.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_group_all (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-61.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+142.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.84&lt;/td&gt;
      &lt;td&gt;45269.5&lt;/td&gt;
      &lt;td&gt;2.27&lt;/td&gt;
      &lt;td&gt;109630.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_group_by (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-62.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+140.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.90&lt;/td&gt;
      &lt;td&gt;44952.6&lt;/td&gt;
      &lt;td&gt;2.21&lt;/td&gt;
      &lt;td&gt;108263.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_inline (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-38.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+57.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;596.32&lt;/td&gt;
      &lt;td&gt;471.7&lt;/td&gt;
      &lt;td&gt;366.11&lt;/td&gt;
      &lt;td&gt;744.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_count (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-43.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+75.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;75.35&lt;/td&gt;
      &lt;td&gt;3716.0&lt;/td&gt;
      &lt;td&gt;42.73&lt;/td&gt;
      &lt;td&gt;6505.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_from (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-36.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+54.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2723.41&lt;/td&gt;
      &lt;td&gt;102.0&lt;/td&gt;
      &lt;td&gt;1730.69&lt;/td&gt;
      &lt;td&gt;157.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::pipeline_filter_group_order (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-17.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+17.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7624.09&lt;/td&gt;
      &lt;td&gt;37.0&lt;/td&gt;
      &lt;td&gt;6318.85&lt;/td&gt;
      &lt;td&gt;43.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_standard (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+46.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-34.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;3454.12&lt;/td&gt;
      &lt;td&gt;82.3&lt;/td&gt;
      &lt;td&gt;5071.34&lt;/td&gt;
      &lt;td&gt;53.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_standard&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-33.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+50.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1513.98&lt;/td&gt;
      &lt;td&gt;0.7&lt;/td&gt;
      &lt;td&gt;1008.38&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_standard::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-56.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+90.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6.04&lt;/td&gt;
      &lt;td&gt;43641.0&lt;/td&gt;
      &lt;td&gt;2.64&lt;/td&gt;
      &lt;td&gt;83230.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_standard&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-76.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+332.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;445.31&lt;/td&gt;
      &lt;td&gt;2.2&lt;/td&gt;
      &lt;td&gt;102.82&lt;/td&gt;
      &lt;td&gt;9.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_composite (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+11.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-13.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5489.27&lt;/td&gt;
      &lt;td&gt;51.6&lt;/td&gt;
      &lt;td&gt;6131.84&lt;/td&gt;
      &lt;td&gt;44.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_composite&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-33.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+50.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1513.98&lt;/td&gt;
      &lt;td&gt;0.7&lt;/td&gt;
      &lt;td&gt;1011.97&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_composite::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+30.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-23.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1509.67&lt;/td&gt;
      &lt;td&gt;183.2&lt;/td&gt;
      &lt;td&gt;1976.73&lt;/td&gt;
      &lt;td&gt;141.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_composite&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-64.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+184.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;421.76&lt;/td&gt;
      &lt;td&gt;2.4&lt;/td&gt;
      &lt;td&gt;148.54&lt;/td&gt;
      &lt;td&gt;6.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_range_merged (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-20.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+23.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7044.30&lt;/td&gt;
      &lt;td&gt;39.8&lt;/td&gt;
      &lt;td&gt;5572.12&lt;/td&gt;
      &lt;td&gt;49.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_range_merged&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-33.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+50.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1513.98&lt;/td&gt;
      &lt;td&gt;0.7&lt;/td&gt;
      &lt;td&gt;1012.48&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_range_merged::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+34.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-24.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;453.72&lt;/td&gt;
      &lt;td&gt;608.9&lt;/td&gt;
      &lt;td&gt;608.72&lt;/td&gt;
      &lt;td&gt;457.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_range_merged&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-67.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+202.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;445.06&lt;/td&gt;
      &lt;td&gt;2.2&lt;/td&gt;
      &lt;td&gt;146.75&lt;/td&gt;
      &lt;td&gt;6.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_in (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-22.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+25.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6744.01&lt;/td&gt;
      &lt;td&gt;41.8&lt;/td&gt;
      &lt;td&gt;5253.10&lt;/td&gt;
      &lt;td&gt;52.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_in&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-33.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+50.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1512.96&lt;/td&gt;
      &lt;td&gt;0.7&lt;/td&gt;
      &lt;td&gt;1011.97&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_in::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-58.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+136.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;30.92&lt;/td&gt;
      &lt;td&gt;8937.5&lt;/td&gt;
      &lt;td&gt;12.73&lt;/td&gt;
      &lt;td&gt;21115.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_in&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-71.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+247.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;456.32&lt;/td&gt;
      &lt;td&gt;2.2&lt;/td&gt;
      &lt;td&gt;131.52&lt;/td&gt;
      &lt;td&gt;7.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_fulltext&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+46.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-50.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;55689.22&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;81756.16&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_fulltext::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+45.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-29.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;204.39&lt;/td&gt;
      &lt;td&gt;1343.2&lt;/td&gt;
      &lt;td&gt;296.32&lt;/td&gt;
      &lt;td&gt;942.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_fulltext&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+130.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-56.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;969.98&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;2239.49&lt;/td&gt;
      &lt;td&gt;0.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_hnsw&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-30.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+40.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;19587.07&lt;/td&gt;
      &lt;td&gt;0.1&lt;/td&gt;
      &lt;td&gt;13545.47&lt;/td&gt;
      &lt;td&gt;0.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_hnsw::indexed (2000)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-87.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+654.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;35710.91&lt;/td&gt;
      &lt;td&gt;7.9&lt;/td&gt;
      &lt;td&gt;4622.45&lt;/td&gt;
      &lt;td&gt;59.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_hnsw&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-67.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+205.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2509.82&lt;/td&gt;
      &lt;td&gt;0.4&lt;/td&gt;
      &lt;td&gt;820.48&lt;/td&gt;
      &lt;td&gt;1.2&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;RocksDB&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Test&lt;/th&gt;
      &lt;th&gt;v3 vs. v2&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;SurrealDB v2&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;SurrealDB v3&lt;/th&gt;
      &lt;th&gt;&lt;/th&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;&lt;/th&gt;
      &lt;th&gt;Mean %&lt;/th&gt;
      &lt;th&gt;OP/s %&lt;/th&gt;
      &lt;th&gt;Mean (ms)&lt;/th&gt;
      &lt;th&gt;OP/s&lt;/th&gt;
      &lt;th&gt;Mean (ms)&lt;/th&gt;
      &lt;th&gt;OP/s&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;[C]reate&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-14.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+17.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;9.99&lt;/td&gt;
      &lt;td&gt;28778.2&lt;/td&gt;
      &lt;td&gt;8.52&lt;/td&gt;
      &lt;td&gt;33768.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]ead&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+59.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-36.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;0.37&lt;/td&gt;
      &lt;td&gt;757343.3&lt;/td&gt;
      &lt;td&gt;0.59&lt;/td&gt;
      &lt;td&gt;484147.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[U]pdate&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+1.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-1.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;8.34&lt;/td&gt;
      &lt;td&gt;34370.8&lt;/td&gt;
      &lt;td&gt;8.50&lt;/td&gt;
      &lt;td&gt;33828.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::count_all (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-13.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-2.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;153.00&lt;/td&gt;
      &lt;td&gt;1821.8&lt;/td&gt;
      &lt;td&gt;132.74&lt;/td&gt;
      &lt;td&gt;1771.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_id (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-63.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+155.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.32&lt;/td&gt;
      &lt;td&gt;48199.9&lt;/td&gt;
      &lt;td&gt;1.94&lt;/td&gt;
      &lt;td&gt;123328.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_all (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-85.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+492.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;16.29&lt;/td&gt;
      &lt;td&gt;16194.5&lt;/td&gt;
      &lt;td&gt;2.45&lt;/td&gt;
      &lt;td&gt;95975.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_start_id (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-49.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+90.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;12.08&lt;/td&gt;
      &lt;td&gt;22395.3&lt;/td&gt;
      &lt;td&gt;6.07&lt;/td&gt;
      &lt;td&gt;42606.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::limit_start_all (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-69.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+210.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;20.45&lt;/td&gt;
      &lt;td&gt;13197.6&lt;/td&gt;
      &lt;td&gt;6.28&lt;/td&gt;
      &lt;td&gt;40970.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_id (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+19.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-12.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;0.63&lt;/td&gt;
      &lt;td&gt;334303.5&lt;/td&gt;
      &lt;td&gt;0.75&lt;/td&gt;
      &lt;td&gt;291755.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_id_eq (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-100.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+352510.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;3313.50&lt;/td&gt;
      &lt;td&gt;85.7&lt;/td&gt;
      &lt;td&gt;0.72&lt;/td&gt;
      &lt;td&gt;302257.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_gt (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+30.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-27.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4576.38&lt;/td&gt;
      &lt;td&gt;62.0&lt;/td&gt;
      &lt;td&gt;5955.45&lt;/td&gt;
      &lt;td&gt;45.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_in (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-34.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+50.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;9134.11&lt;/td&gt;
      &lt;td&gt;30.7&lt;/td&gt;
      &lt;td&gt;5944.41&lt;/td&gt;
      &lt;td&gt;46.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_multi_and (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+12.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-13.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5394.01&lt;/td&gt;
      &lt;td&gt;52.4&lt;/td&gt;
      &lt;td&gt;6047.01&lt;/td&gt;
      &lt;td&gt;45.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_order_limit (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+22.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-19.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4543.69&lt;/td&gt;
      &lt;td&gt;62.4&lt;/td&gt;
      &lt;td&gt;5556.69&lt;/td&gt;
      &lt;td&gt;50.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_order_desc_limit (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+26.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-22.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4505.67&lt;/td&gt;
      &lt;td&gt;63.0&lt;/td&gt;
      &lt;td&gt;5695.88&lt;/td&gt;
      &lt;td&gt;49.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_where_multi_order_limit (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-2.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+0.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5628.40&lt;/td&gt;
      &lt;td&gt;50.4&lt;/td&gt;
      &lt;td&gt;5495.20&lt;/td&gt;
      &lt;td&gt;50.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_omit_limit (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-76.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+314.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;16.68&lt;/td&gt;
      &lt;td&gt;16042.6&lt;/td&gt;
      &lt;td&gt;3.85&lt;/td&gt;
      &lt;td&gt;66553.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fields_where_limit (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+637.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-86.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;14.19&lt;/td&gt;
      &lt;td&gt;18728.2&lt;/td&gt;
      &lt;td&gt;104.58&lt;/td&gt;
      &lt;td&gt;2460.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_order_by (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-68.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+214.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;15667.41&lt;/td&gt;
      &lt;td&gt;17.9&lt;/td&gt;
      &lt;td&gt;4932.92&lt;/td&gt;
      &lt;td&gt;56.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_order_by_multi (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-68.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+215.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;15902.27&lt;/td&gt;
      &lt;td&gt;17.6&lt;/td&gt;
      &lt;td&gt;5037.95&lt;/td&gt;
      &lt;td&gt;55.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_count (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+20.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-20.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4235.10&lt;/td&gt;
      &lt;td&gt;67.0&lt;/td&gt;
      &lt;td&gt;5091.89&lt;/td&gt;
      &lt;td&gt;53.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_sum (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+8.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-11.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4881.45&lt;/td&gt;
      &lt;td&gt;58.1&lt;/td&gt;
      &lt;td&gt;5309.39&lt;/td&gt;
      &lt;td&gt;51.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_avg (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+9.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-11.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4900.75&lt;/td&gt;
      &lt;td&gt;57.8&lt;/td&gt;
      &lt;td&gt;5371.33&lt;/td&gt;
      &lt;td&gt;51.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_multi_agg (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-21.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+25.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7782.85&lt;/td&gt;
      &lt;td&gt;36.0&lt;/td&gt;
      &lt;td&gt;6111.42&lt;/td&gt;
      &lt;td&gt;45.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_all (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-3.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+2.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5624.71&lt;/td&gt;
      &lt;td&gt;50.3&lt;/td&gt;
      &lt;td&gt;5421.68&lt;/td&gt;
      &lt;td&gt;51.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_order_limit (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-3.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+1.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5721.24&lt;/td&gt;
      &lt;td&gt;49.5&lt;/td&gt;
      &lt;td&gt;5529.63&lt;/td&gt;
      &lt;td&gt;50.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_where (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+6.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-7.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5949.82&lt;/td&gt;
      &lt;td&gt;47.5&lt;/td&gt;
      &lt;td&gt;6311.33&lt;/td&gt;
      &lt;td&gt;44.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_group_dedup_agg (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-21.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+27.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7813.47&lt;/td&gt;
      &lt;td&gt;35.8&lt;/td&gt;
      &lt;td&gt;6125.32&lt;/td&gt;
      &lt;td&gt;45.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_split (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-50.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+80.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;17.45&lt;/td&gt;
      &lt;td&gt;15307.9&lt;/td&gt;
      &lt;td&gt;8.70&lt;/td&gt;
      &lt;td&gt;27658.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fetch (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-77.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+327.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;35.19&lt;/td&gt;
      &lt;td&gt;7564.1&lt;/td&gt;
      &lt;td&gt;8.06&lt;/td&gt;
      &lt;td&gt;32357.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::select_fetch_where_limit (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+276.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-73.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;30.11&lt;/td&gt;
      &lt;td&gt;8892.6&lt;/td&gt;
      &lt;td&gt;113.41&lt;/td&gt;
      &lt;td&gt;2388.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth1 (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-79.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+334.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4.78&lt;/td&gt;
      &lt;td&gt;54273.4&lt;/td&gt;
      &lt;td&gt;1.00&lt;/td&gt;
      &lt;td&gt;235728.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth2 (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-88.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+612.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;14.39&lt;/td&gt;
      &lt;td&gt;19118.8&lt;/td&gt;
      &lt;td&gt;1.73&lt;/td&gt;
      &lt;td&gt;136232.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_out_depth3 (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-93.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+1232.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;41.42&lt;/td&gt;
      &lt;td&gt;6724.3&lt;/td&gt;
      &lt;td&gt;2.78&lt;/td&gt;
      &lt;td&gt;89577.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_in_depth1 (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-77.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+254.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;4.90&lt;/td&gt;
      &lt;td&gt;54000.1&lt;/td&gt;
      &lt;td&gt;1.12&lt;/td&gt;
      &lt;td&gt;191369.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_bidirectional (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-89.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+654.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;10.62&lt;/td&gt;
      &lt;td&gt;25771.5&lt;/td&gt;
      &lt;td&gt;1.14&lt;/td&gt;
      &lt;td&gt;194426.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_edge_filter (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-57.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+122.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2.37&lt;/td&gt;
      &lt;td&gt;102130.8&lt;/td&gt;
      &lt;td&gt;1.02&lt;/td&gt;
      &lt;td&gt;227036.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_out (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-94.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+1651.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;426.98&lt;/td&gt;
      &lt;td&gt;659.4&lt;/td&gt;
      &lt;td&gt;23.02&lt;/td&gt;
      &lt;td&gt;11551.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_out_where (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-91.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+1060.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;170.00&lt;/td&gt;
      &lt;td&gt;1648.7&lt;/td&gt;
      &lt;td&gt;13.99&lt;/td&gt;
      &lt;td&gt;19133.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_multi_count (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-90.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+833.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;90.26&lt;/td&gt;
      &lt;td&gt;3098.7&lt;/td&gt;
      &lt;td&gt;8.96&lt;/td&gt;
      &lt;td&gt;28929.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_depth2_limit (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-95.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+2195.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;238.54&lt;/td&gt;
      &lt;td&gt;1176.5&lt;/td&gt;
      &lt;td&gt;9.82&lt;/td&gt;
      &lt;td&gt;27000.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_where (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-59.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+125.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.40&lt;/td&gt;
      &lt;td&gt;49395.5&lt;/td&gt;
      &lt;td&gt;2.17&lt;/td&gt;
      &lt;td&gt;111209.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_group_all (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-62.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+144.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.72&lt;/td&gt;
      &lt;td&gt;46438.1&lt;/td&gt;
      &lt;td&gt;2.12&lt;/td&gt;
      &lt;td&gt;113671.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::graph_sub_group_by (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-65.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+158.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5.65&lt;/td&gt;
      &lt;td&gt;47104.9&lt;/td&gt;
      &lt;td&gt;1.98&lt;/td&gt;
      &lt;td&gt;121859.2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_inline (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-39.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+62.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;585.17&lt;/td&gt;
      &lt;td&gt;476.7&lt;/td&gt;
      &lt;td&gt;352.05&lt;/td&gt;
      &lt;td&gt;774.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_count (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-10.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+8.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;71.02&lt;/td&gt;
      &lt;td&gt;3947.0&lt;/td&gt;
      &lt;td&gt;63.50&lt;/td&gt;
      &lt;td&gt;4299.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::subquery_from (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-36.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+53.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;2708.90&lt;/td&gt;
      &lt;td&gt;102.4&lt;/td&gt;
      &lt;td&gt;1731.46&lt;/td&gt;
      &lt;td&gt;157.7&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::pipeline_filter_group_order (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-13.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+13.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;7471.20&lt;/td&gt;
      &lt;td&gt;37.6&lt;/td&gt;
      &lt;td&gt;6498.49&lt;/td&gt;
      &lt;td&gt;42.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_standard (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+53.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-37.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;3307.64&lt;/td&gt;
      &lt;td&gt;85.9&lt;/td&gt;
      &lt;td&gt;5077.27&lt;/td&gt;
      &lt;td&gt;53.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_standard&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+0.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;+0.0%&lt;/td&gt;
      &lt;td&gt;1010.43&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;1011.97&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_standard::indexed (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-68.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+181.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6.95&lt;/td&gt;
      &lt;td&gt;38213.8&lt;/td&gt;
      &lt;td&gt;2.22&lt;/td&gt;
      &lt;td&gt;107603.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_standard&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-21.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+26.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;275.84&lt;/td&gt;
      &lt;td&gt;3.6&lt;/td&gt;
      &lt;td&gt;217.79&lt;/td&gt;
      &lt;td&gt;4.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_composite (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+10.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-12.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;5403.80&lt;/td&gt;
      &lt;td&gt;52.4&lt;/td&gt;
      &lt;td&gt;5945.42&lt;/td&gt;
      &lt;td&gt;45.9&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_composite&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+1.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-1.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1006.34&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;1018.11&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_composite::indexed (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-49.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+96.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1494.50&lt;/td&gt;
      &lt;td&gt;184.7&lt;/td&gt;
      &lt;td&gt;762.39&lt;/td&gt;
      &lt;td&gt;362.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_composite&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-70.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+243.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;769.28&lt;/td&gt;
      &lt;td&gt;1.3&lt;/td&gt;
      &lt;td&gt;223.81&lt;/td&gt;
      &lt;td&gt;4.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_range_merged (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-21.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+25.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6974.03&lt;/td&gt;
      &lt;td&gt;40.3&lt;/td&gt;
      &lt;td&gt;5446.44&lt;/td&gt;
      &lt;td&gt;50.5&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_range_merged&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+0.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;+0.0%&lt;/td&gt;
      &lt;td&gt;1012.48&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;1014.53&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_range_merged::indexed (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-47.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+92.8%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;456.57&lt;/td&gt;
      &lt;td&gt;595.9&lt;/td&gt;
      &lt;td&gt;237.86&lt;/td&gt;
      &lt;td&gt;1148.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_range_merged&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-20.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+25.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;271.74&lt;/td&gt;
      &lt;td&gt;3.7&lt;/td&gt;
      &lt;td&gt;216.13&lt;/td&gt;
      &lt;td&gt;4.6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_in (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-24.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+27.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;6644.39&lt;/td&gt;
      &lt;td&gt;42.3&lt;/td&gt;
      &lt;td&gt;5037.32&lt;/td&gt;
      &lt;td&gt;53.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_in&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+0.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;+0.0%&lt;/td&gt;
      &lt;td&gt;1010.43&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
      &lt;td&gt;1011.97&lt;/td&gt;
      &lt;td&gt;1.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_in::indexed (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-70.1%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+218.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;30.15&lt;/td&gt;
      &lt;td&gt;9168.9&lt;/td&gt;
      &lt;td&gt;9.00&lt;/td&gt;
      &lt;td&gt;29209.8&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_in&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-18.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+22.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;279.94&lt;/td&gt;
      &lt;td&gt;3.6&lt;/td&gt;
      &lt;td&gt;228.03&lt;/td&gt;
      &lt;td&gt;4.4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_fulltext&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+28.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;+0.0%&lt;/td&gt;
      &lt;td&gt;73302.02&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
      &lt;td&gt;94273.54&lt;/td&gt;
      &lt;td&gt;0.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_fulltext::indexed (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-8.0%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+9.6%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;217.18&lt;/td&gt;
      &lt;td&gt;1267.2&lt;/td&gt;
      &lt;td&gt;199.83&lt;/td&gt;
      &lt;td&gt;1388.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_fulltext&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+598.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-85.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;435.07&lt;/td&gt;
      &lt;td&gt;2.3&lt;/td&gt;
      &lt;td&gt;3038.21&lt;/td&gt;
      &lt;td&gt;0.3&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[I]ndex::index_hnsw&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-6.4%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+16.7%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;15544.32&lt;/td&gt;
      &lt;td&gt;0.1&lt;/td&gt;
      &lt;td&gt;14544.90&lt;/td&gt;
      &lt;td&gt;0.1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[S]can::index_hnsw::indexed (2001)&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-87.3%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+659.9%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;35562.02&lt;/td&gt;
      &lt;td&gt;7.9&lt;/td&gt;
      &lt;td&gt;4524.91&lt;/td&gt;
      &lt;td&gt;60.0&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;[R]emoveIndex::index_hnsw&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;-16.2%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;&lt;strong&gt;+18.5%&lt;/strong&gt;&lt;/td&gt;
      &lt;td&gt;1546.75&lt;/td&gt;
      &lt;td&gt;0.7&lt;/td&gt;
      &lt;td&gt;1295.87&lt;/td&gt;
      &lt;td&gt;0.8&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  More performance improvements coming
&lt;/h2&gt;

&lt;p&gt;We have a roadmap of performance-related improvements that we'll be rolling out across the coming minor releases. Here's what to expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expanding the new executor&lt;/strong&gt; to cover write workloads and all remaining statement types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-to-end streaming&lt;/strong&gt;, pushing the streaming model from internal execution all the way to the client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indexing improvements&lt;/strong&gt;, including better index planning, reduced scan overhead, expanded index types, and optimised composite index strategies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continued optimisation&lt;/strong&gt; of the planning and execution pipeline itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, you can start reaping the benefits of the new executor on read-heavy workloads, and expect it to get better with every release.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;These benchmarks are just the beginning. This release focuses on measuring the performance gains in SurrealDB 3.0 compared directly to SurrealDB v2.x, highlighting the impact of the new foundations.&lt;/p&gt;

&lt;p&gt;Next, we’ll expand this work with a broader benchmarking iteration that compares SurrealDB against other databases and platforms across a wider range of workloads. Stay tuned - there’s much more to come.&lt;/p&gt;

&lt;p&gt;If you have any questions, feedback, or want to dive deeper into the results, feel free to &lt;a href="https://surrealdb.com/contact" rel="noopener noreferrer"&gt;reach out to us&lt;/a&gt; or &lt;a href="https://discord.com/invite/surrealdb" rel="noopener noreferrer"&gt;join our Discord community&lt;/a&gt; to chat directly with the community and team.&lt;/p&gt;

</description>
      <category>surrealdb</category>
      <category>database</category>
      <category>benchmarks</category>
      <category>multimodeldatabase</category>
    </item>
    <item>
      <title>Introducing Surrealism</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Thu, 19 Feb 2026 18:38:16 +0000</pubDate>
      <link>https://forem.com/surrealdb/introducing-surrealism-4b5p</link>
      <guid>https://forem.com/surrealdb/introducing-surrealism-4b5p</guid>
      <description>&lt;p&gt;We are excited to introduce Surrealism which offers something developers have wanted for a long time: true extensibility inside SurrealDB itself. Those of you who have participated in the SurrealDB 3.0 beta might have already gotten a glimpse of Surrealism, and we’re excited to announce it more broadly to the world today.&lt;/p&gt;

&lt;p&gt;Surrealism is a new open-source extension framework for SurrealDB. It allows you to define modular, programmable logic using functions you write in Rust - and execute them directly within the database at runtime. We intend to expand Surrealism to support other languages including JavaScript and Python.&lt;/p&gt;

&lt;p&gt;Traditionally, if you wanted to extend your database, you had to choose between brittle, complex, error-prone, and potentially huge scripts, heavyweight external services, or just push the data to the application layer and perform the logic there. Surrealism changes that. It brings logic into the database in a way that’s fast, secure, testable, and fully integrated with your development workflow. Surrealism redefines the way that developers and organisations work with stored procedures - allowing for custom business logic, and access control layers as version-controlled, testable, and shareable modules. These plugins execute with full transactional guarantees, and can be used to manage everything from dynamic API behaviours to policy enforcement and auditing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foksq6igv791ezs0j48a5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foksq6igv791ezs0j48a5.png" alt="WASM" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Surrealism works
&lt;/h2&gt;

&lt;p&gt;Functions are compiled to WebAssembly, allowing them to run in a secure, deterministic, sandboxed execution environment, whilst delivering near-native performance, with strict isolation between functions. This makes it ideal for both single-tenant systems and multi-tenant clusters, where isolation and efficiency are critical.&lt;/p&gt;

&lt;p&gt;Plugins can be loaded from local disk, object storage, or uploaded directly into SurrealDB. They can be enabled and executed dynamically - without restarting the database - with fine-grained permission controls to determine who can load or invoke them.&lt;/p&gt;

&lt;p&gt;With Surrealism, the development experience has been designed from the ground up for &lt;strong&gt;productivity&lt;/strong&gt;. Developers can use their own libraries, tools, and frameworks. They can write tests in Rust, integrate plugins into CI and CD pipelines, and manage plugin versions just like any other component of their application stack. Plugins are defined in simple projects with metadata in a TOML file, and compiled into portable WebAssembly binaries that can be reused, versioned, and shared.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfmqoihc1lshwlvj0s7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfmqoihc1lshwlvj0s7d.png" alt="A diagram with the 4 steps of Surrealism" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Built-in AI support and agentic workflows
&lt;/h2&gt;

&lt;p&gt;Surrealism enables direct integration with AI models, both local and remote. Plugins can interact with GPU-accelerated inferencing runtimes, or call out to external APIs for services like text or image generation, classification, tokenisation, embedding, translation, and anything else. Surrealism extensions turn SurrealDB into a programmable data and logic layer - perfect for agentic and AI-heavy workloads.&lt;/p&gt;

&lt;p&gt;This means you could define a plugin that takes a user’s input, runs it through a local LLM, and stores the generated result - all within a single SurrealQL query. You can trigger sentiment analysis on inserted documents, run similarity scoring against vector indexes, or use a vision model to extract structured data from uploaded files.&lt;/p&gt;

&lt;p&gt;And because Surrealism plugins have full access to SurrealDB’s query engine, schema, and the new file functionality, they can work with structured data, unstructured content, and multi-model assets in a single unified system.&lt;/p&gt;

&lt;p&gt;For example, a plugin can transcribe an audio file, extract named entities, enrich the results using a remote model, and save the output - all in one transactional flow.&lt;/p&gt;

&lt;p&gt;This allows developers to build intelligent applications entirely within the database. The database becomes more than a data store - it becomes an intelligent, programmable runtime for real-time decision-making, content generation, and AI integration. &lt;/p&gt;

&lt;p&gt;Bringing logic and agentic workflows into the database, rather than pushing the data out to external systems, enables seamless integration for AI and unlocks new possibilities for intelligent applications. It’s fast. It’s flexible. It’s secure. And it’s open source.&lt;/p&gt;

&lt;p&gt;Surrealism is available today for you to start developing your own custom or business logic, and it opens up a whole new frontier in database development - one where you can bring your own code, your own models, and your own ideas directly into the data layer.&lt;/p&gt;

&lt;p&gt;We can’t wait to see what you build.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get started with Surrealism
&lt;/h2&gt;

&lt;p&gt;Go to the &lt;a href="https://surrealdb.com/docs/surrealdb/extensions" rel="noopener noreferrer"&gt;Surrealism Docs&lt;/a&gt; to get started. We are excited to see what extensions you build, be sure to share them in our &lt;a href="https://discord.com/invite/surrealdb" rel="noopener noreferrer"&gt;Discord channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thank you for being on this journey with us!&lt;/p&gt;

</description>
      <category>surrealism</category>
      <category>surrealdb</category>
      <category>database</category>
      <category>ai</category>
    </item>
    <item>
      <title>Introducing SurrealDB 3.0 - the future of AI agent memory</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Wed, 18 Feb 2026 11:44:48 +0000</pubDate>
      <link>https://forem.com/surrealdb/introducing-surrealdb-30-the-future-of-ai-agent-memory-5h19</link>
      <guid>https://forem.com/surrealdb/introducing-surrealdb-30-the-future-of-ai-agent-memory-5h19</guid>
      <description>&lt;p&gt;Since SurrealDB was first released, we’ve been humbled by the reviews and adoption from our users. The possibility of a single database that does it all, can run anywhere, and can scale from embedded to large scale clusters opened up the imagination of users, all the way from building small applications to large scale enterprise AI production systems.&lt;/p&gt;

&lt;p&gt;3.0 looks to take SurrealDB to the next level, with significant improvements across three main areas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stability, performance and tooling&lt;/li&gt;
&lt;li&gt;Improved developer experience&lt;/li&gt;
&lt;li&gt;Building AI agents&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stability, performance and tooling
&lt;/h2&gt;

&lt;p&gt;2.0 was a milestone for SurrealDB in terms of capability, but as usage scaled, some of our internal models made the engine do work it didn’t need to do, and made correctness and predictability harder than they needed to be.&lt;/p&gt;

&lt;p&gt;With SurrealDB 3.0, we’ve made major architectural changes to improve stability, performance, and the developer experience. We’ve separated values from expressions, introduced computed fields, moved core metadata to ID‑based storage, made synced writes the default, and re‑designed how documents are represented on disk. The result is a faster, more stable database - and a cleaner path to future optimisation.&lt;/p&gt;

&lt;p&gt;Let’s dive into these changes and explore why they matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Splitting values and expressions
&lt;/h3&gt;

&lt;p&gt;At the heart of 3.0 is a clean separation between what your data is (values) and how it’s derived (expressions).&lt;/p&gt;

&lt;p&gt;In earlier versions these were entangled. A field could be “computed or un‑computed,” but the engine often had to continuously compute to present a result. That meant extra evaluation, extra serialisation, and edge‑case bugs - especially once you stored logic &lt;em&gt;inside&lt;/em&gt; records.&lt;/p&gt;

&lt;p&gt;By splitting the two, the engine evaluates only when it should, avoiding redundant work and laying a foundation for a more predictable planner and smarter optimisations in future releases. &lt;/p&gt;

&lt;p&gt;This corrects the prior model and directly enables a safer, faster way to express logic in your schema: computed fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Computed fields
&lt;/h3&gt;

&lt;p&gt;SurrealDB’s &lt;code&gt;future&lt;/code&gt; type was used for values that were evaluated at query time. While flexible, they embedded logic into each record and forced per‑row evaluation. In 3.0, they are replaced with computed fields: logic you define once in the schema to be evaluated consistently at query time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;can_drive&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="n"&gt;COMPUTED&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach removes unnecessary per‑record computation, improves stability, and makes behaviour easier to reason about. The result is a reduced workload for the engine and a clearer developer experience, with guardrails that prevent hidden complexity.&lt;/p&gt;

&lt;p&gt;Learn more at &lt;a href="https://surrealdb.com/docs/surrealql/statements/define/field" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealql/statements/define/field&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ID‑based storage
&lt;/h3&gt;

&lt;p&gt;3.0 moves core catalog entries like namespaces, databases and indexes from name‑based to ID‑based storage. Internally, these now use compact, fixed‑size identifiers rather than variable‑length names.&lt;/p&gt;

&lt;p&gt;The benefits are twofold:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistent, smaller keys on disk:&lt;/strong&gt; reducing storage consumption and improving lookups - especially when names are long.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It lays the groundwork for renaming resources:&lt;/strong&gt; since references point to stable IDs that don’t change, it will be possible to rename database resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the catalog more robust today and more flexible for features in the future.&lt;/p&gt;

&lt;p&gt;As an arbitrary example of a smaller key, here’s one of the keys that’s stored in the DB that is used for indexing.&lt;/p&gt;

&lt;p&gt;This is what it would look like before at 80 bytes with the previous Rust code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`/*namespace\0*database\0*order_history\0+order_history__city_time__idx\0!bd\x01\0\0\0\0\0\0\0\x07`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Bd&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"order_history"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"order_history__city_time__idx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Bd&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;b"/*namespace&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s"&gt;*database&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s"&gt;*order_history&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s"&gt;+order_history__city_time__idx&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s"&gt;!bd&lt;/span&gt;&lt;span class="se"&gt;\x01\0\0\0\0\0\0\0\x07&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here it is now at 42 bytes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`/*\0\0\0\x01*\0\0\0\x02*order_history\0+\0\0\0\x03!bd\0\0\0\0\0\0\0\x07`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Bd&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;NamespaceId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;DatabaseId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"order_history"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;IndexId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Bd&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;encode_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="nf"&gt;.len&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;b"/*&lt;/span&gt;&lt;span class="se"&gt;\0\0\0\x01&lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\0\0\0\x02&lt;/span&gt;&lt;span class="s"&gt;*order_history&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\0\0\0\x03&lt;/span&gt;&lt;span class="s"&gt;!bd&lt;/span&gt;&lt;span class="se"&gt;\0\0\0\0\0\0\0\x07&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s a reduction of 38 bytes on this key alone - multiplied across catalogs, it meaningfully trims storage and I/O.&lt;/p&gt;

&lt;h3&gt;
  
  
  Synced writes by default
&lt;/h3&gt;

&lt;p&gt;Synced writes are now the default in 3.0. Rather than relying on the operating system to flush data “soon,” SurrealDB confirms a write only after it’s durably committed.&lt;/p&gt;

&lt;p&gt;This change doesn’t alter what’s possible - it makes the safer behaviour the standard, improving predictability for production workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Document representation
&lt;/h3&gt;

&lt;p&gt;Previously, a record’s value and its metadata lived together, with metadata stored as hidden fields. That made rendering results slower and caused correctness issues - especially with views and table statistics.&lt;/p&gt;

&lt;p&gt;3.0 introduces a proper &lt;strong&gt;document wrapper type&lt;/strong&gt;. Record &lt;strong&gt;content&lt;/strong&gt; and record &lt;strong&gt;metadata&lt;/strong&gt; are stored &lt;strong&gt;explicitly and separately&lt;/strong&gt;. That means cleaner responses with no invisible fields, improved stability for views and table statistics, and room to attach system metadata - such as update timestamps or version stamps - without polluting user data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug fixes &amp;amp; SDK improvements
&lt;/h3&gt;

&lt;p&gt;3.0 also includes a large bug-fixing effort. We have closed over 150 bugs, and are now laser-focused on solving any new reported bugs. We have also implemented improved automated testing, and a CRUD bench workflow that runs on every submitted PR. These fixes address long-standing pain points raised by both community users and enterprise deployments, improving correctness and reliability across the board.&lt;/p&gt;

&lt;p&gt;Alongside this, the Go and Java SDKs have reached &lt;code&gt;1.0&lt;/code&gt; production readiness, giving developers stable integration paths in more ecosystems out of the box.&lt;/p&gt;

&lt;p&gt;SurrealDB 3.0 represents a significant step forward in the stability and maturity of the database. By separating values from expressions, introducing computed fields, adopting ID‑based storage, enabling synced writes by default, and redesigning document handling, SurrealDB becomes more predictable, more durable, and easier to operate.&lt;/p&gt;

&lt;p&gt;As you explore SurrealDB 3.0, you’ll find these improvements make everyday queries faster, behaviour more consistent, and future optimisation substantially easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surreal Sync
&lt;/h3&gt;

&lt;p&gt;Surreal Sync is a new tool that helps you move data from another database or source into SurrealDB. It’s currently in active development, and users are welcome to test it and share feedback or report any issues.&lt;/p&gt;

&lt;p&gt;Surreal Sync is run entirely on the command line and allows you to import data directly from a another source straight into a running SurrealDB instance. For example, the following command will sign into a Neo4J database at &lt;code&gt;bolt://localhost:7687&lt;/code&gt; as the user &lt;code&gt;neo4j&lt;/code&gt; with the password &lt;code&gt;password&lt;/code&gt; into the SurrealDB database &lt;code&gt;graph_data&lt;/code&gt; inside the &lt;code&gt;production&lt;/code&gt; namespace via a user named &lt;code&gt;root&lt;/code&gt; with the password &lt;code&gt;secret&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;surreal-sync sync neo4j \
  --source-uri "bolt://localhost:7687" \
  --source-username "neo4j" \
  --source-password "password" \
  --neo4j-timezone "America/New_York" \
  --to-namespace "production" \
  --to-database "graph_data" \
  --surrealdb-username "root" \
  --surrealdb-password "secret"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Learn more at &lt;a href="https://surrealdb.com/docs/surrealdb/migrating" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealdb/migrating&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL support is now stable
&lt;/h3&gt;

&lt;p&gt;GraphQL has become a critical interface layer for modern applications. It gives frontend teams precise control over the data they fetch, reduces over-fetching and under-fetching, and enables faster iteration across web, mobile, and edge clients. For companies, it provides a strongly typed, self-documenting API surface that scales cleanly across teams and services. With SurrealDB 3.0, we are excited to announce that GraphQL support is now stable.&lt;/p&gt;

&lt;p&gt;Previously gated behind an experimental flag, the GraphQL interface has undergone a comprehensive overhaul to meet real-world requirements - including full mutation support, built-in authentication flows, permission enforcement, and strong protections against abusive queries through configurable depth and complexity limits.&lt;/p&gt;

&lt;p&gt;This release also introduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rich filtering operators and nested object support&lt;/li&gt;
&lt;li&gt;First-class geometry and relation resolution&lt;/li&gt;
&lt;li&gt;N+1 query optimisations for dramatically fewer database round-trips&lt;/li&gt;
&lt;li&gt;Security hardening and safer error handling&lt;/li&gt;
&lt;li&gt;GraphQL enabled by default (no experimental flag required)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’ve been waiting to run SurrealDB with GraphQL, now’s the time. Explore the GraphQL docs and get started: &lt;a href="https://surrealdb.com/docs/surrealdb/querying/graphql" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealdb/querying/graphql&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Improved developer experience
&lt;/h2&gt;

&lt;p&gt;2.0 unlocked new possibilities, but as adoption grew, developers asked for a more predictable, extensible, and expressive way to build with SurrealDB. With &lt;strong&gt;3.0&lt;/strong&gt;, we’ve delivered just that - bringing a refined model that’s simpler to reason about and faster to build on. Developers can now define custom API endpoints directly within the database, manage complex workflows through client-side transactions, and express logic safely with computed fields and record references. These upgrades transform how you handle data relationships and integrate SurrealDB into real-world applications - making the developer experience as seamless as the database itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom API endpoints
&lt;/h3&gt;

&lt;p&gt;With SurrealDB 3.0, &lt;code&gt;DEFINE API&lt;/code&gt; is now fully stabilised - letting you define custom endpoints and middleware directly inside your database, using the same SurrealQL you already know.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DEFINE API&lt;/code&gt; allows you to move from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client → Middleware (API) → Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client → Database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this was already possible with the standard endpoints, we’ve now improved it by also making custom endpoints possible. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;DEFINE API&lt;/code&gt; is customisable, so let’s see an example. Let's say you have a social app that lets users see all the latest comments. You'd like to develop a free API to allow anonymous users to see them too, but sometimes the app gets really busy and you don't want guests to take up valuable resources.&lt;/p&gt;

&lt;p&gt;In other words, you’d like to implement rate limits.&lt;/p&gt;

&lt;p&gt;To begin, pass in the new &lt;code&gt;--deny-arbitrary-query&lt;/code&gt; flag when starting the SurrealDB server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal start &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;--pass&lt;/span&gt; root &lt;span class="nt"&gt;--deny-arbitrary-query&lt;/span&gt; guest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents certain groups, like guests or record users, from putting their own queries together. We'll add &lt;code&gt;guest&lt;/code&gt; here so that any guest users will have to use the API endpoint instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Defining your API endpoint&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, we'll add a &lt;code&gt;DEFINE API&lt;/code&gt; statement to define the endpoint. The statement will start with the path &lt;code&gt;/get_latest&lt;/code&gt; , and to specify that it can only be used for &lt;code&gt;GET&lt;/code&gt; requests, we will include the &lt;code&gt;FOR&lt;/code&gt; keyword.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;OVERWRITE&lt;/span&gt; &lt;span class="nv"&gt;"/get_latest"&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can follow this up with some middleware by passing in some of SurrealDB's &lt;a href="https://surrealdb.com/docs/surrealql/functions/database/api" rel="noopener noreferrer"&gt;API functions&lt;/a&gt;. We'll use  &lt;code&gt;api::timeout&lt;/code&gt; to give guest users up to 50 milliseconds of server time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt;
    &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then we'll finish up the statement by adding what it actually returns: a &lt;code&gt;SELECT&lt;/code&gt; statement for all &lt;code&gt;comment&lt;/code&gt; records that have been created over the past ten minutes. In summary, that gives us a &lt;code&gt;DEFINE API&lt;/code&gt; statement that looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="n"&gt;OVERWRITE&lt;/span&gt; &lt;span class="nv"&gt;"/get_latest"&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;
    &lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt;
        &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="k"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;]..&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can test it from SurrealQL itself through &lt;code&gt;api::invoke&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"/get_latest"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or externally at: &lt;code&gt;/api/:namespace/:database/:endpoint_name&lt;/code&gt;. Here's what our defined endpoint will look like with a namespace called &lt;code&gt;test_ns&lt;/code&gt; and a database called &lt;code&gt;test_db&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;http://localhost:8000/api/test_ns/test_db/get_latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But that’s not all. The &lt;code&gt;MIDDLEWARE&lt;/code&gt; clause in a &lt;code&gt;DEFINE API&lt;/code&gt; statement can take custom middleware as well that you define yourself using a regular &lt;code&gt;DEFINE FUNCTION&lt;/code&gt; statement. These functions are automatically populated with the user request and a way to get the current state of the response as you put it together.&lt;/p&gt;

&lt;p&gt;There are a lot of examples in the documentation to show how it works, but here’s a simple example to get you started.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;add_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;LET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="k"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;": "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;API&lt;/span&gt; &lt;span class="nv"&gt;"/custom_with_args"&lt;/span&gt;
    &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;
        &lt;span class="n"&gt;MIDDLEWARE&lt;/span&gt;
            &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;add_prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"PREFIX"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;"original message"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;DEFINE API&lt;/code&gt;, SurrealDB brings the power of API design directly into the database - no external layers, no additional frameworks. Whether you’re enforcing limits, defining access, or building powerful custom endpoints, this feature helps you simplify your architecture and move faster.&lt;/p&gt;

&lt;p&gt;Dive deeper into SurrealDB's powerful API management capabilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://surrealdb.com/docs/surrealql/functions/database/api" rel="noopener noreferrer"&gt;API Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://surrealdb.com/docs/surrealql/statements/define/api" rel="noopener noreferrer"&gt;DEFINE API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://surrealdb.com/docs/surrealdb/integration/http#custom" rel="noopener noreferrer"&gt;HTTP Integration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://surrealdb.com/docs/surrealdb/security/capabilities#arbitrary-queries" rel="noopener noreferrer"&gt;Managing Arbitrary Queries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Client-side transactions
&lt;/h3&gt;

&lt;p&gt;We’re introducing client-side transactions - a new way of working with transactions that gives developers far more flexibility and control.&lt;/p&gt;

&lt;p&gt;In earlier versions, transactions were handled in a single request. That worked well for many use cases, but it also meant that any decision logic - such as whether to commit or cancel - had to be written directly in SurrealQL.&lt;/p&gt;

&lt;p&gt;Now, with client-side transactions, that logic can live in your programming language of choice. You can begin a transaction, add operations across multiple requests, and commit when ready - all while keeping the same strong, ACID-compliant guarantees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Client-side transactions follow a simple workflow: begin → add operations → commit or cancel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfioh1k5zbvzbxyp8i8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcfioh1k5zbvzbxyp8i8q.png" alt="The workflow for client-side transactions in SurrealDB, showing the relation between the client app, the SurrealDB, and the transaction store as a user continues to add operations to a transaction." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This enhancement lets developers keep application logic in SDK languages such as Rust, Go, or JavaScript, while SurrealDB manages the transactional consistency behind the scenes.&lt;/p&gt;

&lt;p&gt;For developers, this means cleaner, easier-to-read code, and workflows that are simpler to debug and maintain. Complex, multi-step business logic can now be expressed naturally in application code without giving up transactional safety.&lt;/p&gt;

&lt;p&gt;And this is just the beginning. Client-side transactions also unlock new possibilities - like multi-step processes that span time or even human intervention - while still relying on SurrealDB for atomic guarantees.&lt;/p&gt;

&lt;p&gt;With 3.0, transactions become far more adaptable to the way you build applications.&lt;/p&gt;

&lt;p&gt;Learn more at &lt;a href="https://surrealdb.com/docs/surrealql/transactions" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealql/transactions&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Computed fields
&lt;/h3&gt;

&lt;p&gt;As mentioned already, in 3.0 we’re introducing computed fields - a simpler, faster, and more predictable way to define logic directly inside your data.&lt;/p&gt;

&lt;p&gt;In previous versions of SurrealDB, you could attach a &lt;code&gt;future&lt;/code&gt; to a field. While flexible, futures were too tightly coupled to the records themselves, which made it hard to scale well and often led to unexpected behaviour.&lt;/p&gt;

&lt;p&gt;In 3.0, &lt;code&gt;futures&lt;/code&gt; are gone. In their place, we now have computed fields - defined with a clear &lt;code&gt;COMPUTED&lt;/code&gt; keyword, evaluated consistently whenever you query a record.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Before: using a future&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;can_drive&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="n"&gt;VALUE&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;born&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;-- After: using a computed field&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;can_drive&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="n"&gt;COMPUTED&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;born&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This new syntax is shorter, clearer, and always up to date. Developers can now trust that computed values will appear in results without having to update records manually.&lt;/p&gt;

&lt;p&gt;By consolidating logic into computed fields, SurrealDB reduces complexity in the core engine. This improves stability today - and sets us up to &lt;strong&gt;optimise computations at scale in future versions&lt;/strong&gt;, leading to smarter query planning and faster results.&lt;/p&gt;

&lt;p&gt;It also fixes a real pain point: developers no longer need to worry about when or how a future was set. With computed fields, the logic lives in one place, is always evaluated fresh, and returns consistently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A practical example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at how this works in practice. We want to model a driver’s licenses that expire two years after they are issued. We define when the license was issued, who owns it, and then add a computed field that checks if it’s still valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="nb"&gt;datetime&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="k"&gt;valid&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="n"&gt;COMPUTED&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;valid&lt;/code&gt; field automatically returns whether a license is less than two years old. There’s no update step, no manual reset. Just clean, predictable results every time.&lt;/p&gt;

&lt;p&gt;When we query the license, the &lt;code&gt;valid&lt;/code&gt; field is computed on the fly and included right in the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;buvnfunv21em9zncxe03&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2025-09-01T04:23:34.956Z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Guardrails for reliability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Computed fields also introduce guardrails that futures never had. They can only be defined as top-level fields, which avoids hidden complexity in nested objects. And they cannot be combined with clauses like &lt;code&gt;VALUE&lt;/code&gt;, &lt;code&gt;DEFAULT&lt;/code&gt;, or &lt;code&gt;ASSERT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These restrictions make computed fields easier to reason about, more consistent to query, and safer to use in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Looking ahead&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Computed fields aren’t just a replacement for futures. They’re a foundation for the future.&lt;/p&gt;

&lt;p&gt;By moving logic into a consistent, centralised system, we’ve opened the door for optimised computation and query planning in upcoming releases. This means faster queries, smarter evaluation, and a simpler mental model for developers.&lt;/p&gt;

&lt;p&gt;With SurrealDB 3.0, you can now define logic directly in your schema with confidence - cleaner, safer, and faster than ever before.&lt;/p&gt;

&lt;p&gt;Learn more at &lt;a href="https://surrealdb.com/docs/surrealql/statements/define/field" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealql/statements/define/field&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Record references
&lt;/h3&gt;

&lt;p&gt;With the introduction of record references in SurrealDB 3.0, we’re making it possible for simple record links to now work in two directions instead of one.&lt;/p&gt;

&lt;p&gt;This is a big improvement for anyone working with record links if you’ve been using ad-hoc queries like this one to simulate bidirectional links.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="nb"&gt;datetime&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;-- Link from a license to a person&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="c1"&gt;-- Look through all licenses to find the one linking to this person&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="n"&gt;FORM&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;licenses&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record references are defined at the schema level with the new &lt;code&gt;REFERENCE&lt;/code&gt; keyword.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;REFERENCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This turns a unidirectional link into a bidirectional relationship. With the reference in place, linked records can automatically “see” the records linking to them.&lt;/p&gt;

&lt;p&gt;The syntax for traversing them is familiar too. While graph lookups use &lt;code&gt;&amp;lt;-&lt;/code&gt; arrows, record references use a tilde - &lt;code&gt;&amp;lt;~&lt;/code&gt; - to signal the reverse lookup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A practical example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s further develop the license example to see how this works in practice. Here, we’re modelling people and their licenses.&lt;/p&gt;

&lt;p&gt;This time though, &lt;code&gt;owner&lt;/code&gt; is set to be a &lt;code&gt;REFERENCE&lt;/code&gt;, allowing other records to pick up on the incoming link. We have also added two &lt;code&gt;COMPUTED&lt;/code&gt; fields. The one called &lt;code&gt;valid&lt;/code&gt; checks to see if the license is still valid by being less than two years old, while the one called &lt;code&gt;licenses&lt;/code&gt; checks for incoming links from the &lt;code&gt;license&lt;/code&gt; table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="nb"&gt;datetime&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;REFERENCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="k"&gt;valid&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="n"&gt;COMPUTED&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;licenses&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="n"&gt;COMPUTED&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;~&lt;/span&gt;&lt;span class="n"&gt;license&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s test out this new setup by creating a person named Billy, along with two licenses - a current driver’s license and an old pilot’s license:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Billy"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"driver"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"pilot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2003-03-11'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time when we query a person, all we need to do is select the fields we want to see and the built-in schema does the rest of the work. Both licenses are returned automatically, complete with their validity status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;licenses&lt;/span&gt;&lt;span class="p"&gt;.{&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;valid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;licenses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2003-03-11T00:00:00Z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'pilot'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2025-09-03T02:17:04.340Z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'driver'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Billy'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And since record reference syntax works just like graph queries do, you can use the &lt;code&gt;&amp;lt;~&lt;/code&gt; syntax directly inside a query too. Here’s how we can order Billy’s licenses by date if we decided to use a direct query instead of defining a field using &lt;code&gt;&amp;lt;~&lt;/code&gt; in the schema.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="nb"&gt;datetime&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;TYPE&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;REFERENCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;FIELD&lt;/span&gt; &lt;span class="k"&gt;valid&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="n"&gt;COMPUTED&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"Billy"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"driver"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"pilot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2003-03-11'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;valid&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;**&amp;lt;~**&lt;/span&gt;&lt;span class="n"&gt;license&lt;/span&gt; &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;driving_licenses&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;driving_licenses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2003-03-11T00:00:00Z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'pilot'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;since&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2025-09-03T02:18:20.489Z'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'driver'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="k"&gt;valid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'Billy'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Record references make schemas clearer, queries simpler, and traversals faster. By cutting down redundant lookups, they give you the option to keep adding linked tables without seeing degradation in performance.&lt;/p&gt;

&lt;p&gt;For developers working on a schema, it means you can model relationships more naturally, query them more efficiently, and rely on SurrealDB to manage the complexity.&lt;/p&gt;

&lt;p&gt;Learn more at &lt;a href="https://surrealdb.com/docs/surrealql/datamodel/references" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealql/datamodel/references&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Surqlize TypeScript ORM (experimental)
&lt;/h3&gt;

&lt;p&gt;We’re excited to introduce Surqlize - our new, type-safe TypeScript ORM for SurrealDB. Surqlize is designed to bring the full power of SurrealDB to TypeScript developers, with end-to-end type safety, zero code generation, and first-class graph support. It embraces SurrealDB’s flexibility while giving you the confidence and developer experience you expect from modern TypeScript tooling.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Surqlize is currently experimental. We’re sharing it early so you can try it, push it, and help shape its future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why Surqlize?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SurrealDB is powerful - multi-model, graph-native, and highly expressive. Surqlize makes that power ergonomic and type-safe in TypeScript.&lt;/p&gt;

&lt;p&gt;With Surqlize, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type-safe schema definitions&lt;/strong&gt;: define your database schema using intuitive &lt;code&gt;t.*&lt;/code&gt; builders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic type inference&lt;/strong&gt;: full TypeScript types - without code generation or separate schema compilation steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fluent, fully typed query builder&lt;/strong&gt;: chain &lt;code&gt;.select()&lt;/code&gt;, &lt;code&gt;.where()&lt;/code&gt;, &lt;code&gt;.return()&lt;/code&gt;, and more — with complete type safety throughout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive CRUD operations:&lt;/strong&gt; build &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, and &lt;code&gt;UPSERT&lt;/code&gt; queries in a clean, expressive API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First-class graph relationships:&lt;/strong&gt; model edges and traverse relationships naturally - fully typed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich type system:&lt;/strong&gt; objects, arrays, unions, literals, optional fields, and more — all mapped directly to TypeScript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SurrealDB function support:&lt;/strong&gt; integrated string, array, and record operations directly within typed queries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Surqlize isn’t just an abstraction layer - it’s built specifically for SurrealDB’s multi-model architecture, including its graph capabilities and powerful function system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it and help us shape it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because Surqlize is experimental, your feedback is critical. Try it in your projects, push the limits, and tell us what feels great and what doesn’t. You can learn more here: &lt;a href="https://github.com/surrealdb/surqlize" rel="noopener noreferrer"&gt;https://github.com/surrealdb/surqlize&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Join us in the &lt;a href="https://discord.com/invite/surrealdb" rel="noopener noreferrer"&gt;SurrealDB Discord server&lt;/a&gt; and share your feedback, ideas, and issues. We’re actively iterating and want to build this with the community.&lt;/p&gt;

&lt;p&gt;SurrealDB 3.0 is about pushing boundaries - and Surqlize is a big step toward a best-in-class TypeScript developer experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building AI agents
&lt;/h2&gt;

&lt;p&gt;SurrealDB 3.0 moves beyond storing data - it becomes the foundation for agent memory and intelligence. As developers build increasingly autonomous systems, the line between data, logic, and perception starts to blur. With this release, SurrealDB evolves from a multi-model database into a multi-modal platform for ****AI agents - one that can understand, recall, and extend its own context - ideal for context graphs and semantic layers to power complex AI agents and autonomous agentic workflows.&lt;/p&gt;

&lt;p&gt;With file support, SurrealDB now handles structured records alongside images, audio, and documents - all queryable within SurrealQL. Enhanced vector search and indexing let agents store and retrieve embeddings with millisecond precision. Surrealism, our new WebAssembly extension system, enables custom logic and model execution directly inside the database. And we have some exciting agent memory functionalities in the roadmap to build better durable, scalable context for long-running AI agents.&lt;/p&gt;

&lt;p&gt;Together, these capabilities turn SurrealDB 3.0 into more than a database - it’s a persistent memory engine for intelligent systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  File support
&lt;/h3&gt;

&lt;p&gt;Until now, SurrealDB has always encouraged users to store files externally, referencing them with paths stored in the database. We’re now introducing the first step toward native file support - bringing file storage directly into your database workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setting up file storage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To begin, we can start a new SurrealDB server with the &lt;code&gt;files&lt;/code&gt; capability enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;surreal start &lt;span class="nt"&gt;--user&lt;/span&gt; root &lt;span class="nt"&gt;-pass&lt;/span&gt; root &lt;span class="nt"&gt;--allow-experimental&lt;/span&gt; files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setting up file storage can be done with a single &lt;code&gt;DEFINE BUCKET&lt;/code&gt; statement! A bucket can be a physical location, or even in-memory.&lt;/p&gt;

&lt;p&gt;For global usage across all namespaces and databases, you can set a &lt;code&gt;SURREAL_GLOBAL_BUCKET&lt;/code&gt;  environment variable, with an optional &lt;code&gt;SURREAL_GLOBAL_BUCKET_ENFORCED&lt;/code&gt; if you want to ensure that every database uses the same global bucket.&lt;/p&gt;

&lt;p&gt;Let’s define a simple in-memory bucket. Just provide a name, and set the backend to "memory":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="n"&gt;BUCKET&lt;/span&gt; &lt;span class="n"&gt;my_bucket&lt;/span&gt; &lt;span class="n"&gt;BACKEND&lt;/span&gt; &lt;span class="nv"&gt;"memory"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The name of the bucket is also your path to the bucket and everything inside. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storing and accessing files&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once your bucket is defined, you can reference it using a file pointer - a string prefixed with the letter &lt;code&gt;f&lt;/code&gt; to indicate a file path. &lt;/p&gt;

&lt;p&gt;File pointers have their own functions, like &lt;code&gt;.put()&lt;/code&gt; to add bytes, &lt;code&gt;.get()&lt;/code&gt; to see them, &lt;code&gt;.rename()&lt;/code&gt; to rename a file, and &lt;code&gt;.head()&lt;/code&gt; to see its metadata. Let’s use the &lt;code&gt;.get()&lt;/code&gt; function to see if our bucket has a file called &lt;code&gt;tour_of_surrealdb.txt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function returned &lt;code&gt;NONE&lt;/code&gt;, so looks like there’s nothing there.&lt;/p&gt;

&lt;p&gt;Let’s use the &lt;code&gt;.put()&lt;/code&gt; method to add some bytes, creating the file. &lt;code&gt;.put()&lt;/code&gt; can take bytes or a string, which it will automatically convert into bytes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;"Welcome to the Tour of SurrealDB!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we’re adding the string “Welcome to the Tour of SurrealDB!” &lt;/p&gt;

&lt;p&gt;Now that the file exists, the .get() function will return its as bytes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="nv"&gt;"57656C636F6D6520746F2074686520546F7572206F66205375727265616C444221"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Working with file data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you prefer to view the data differently, SurrealDB also now supports improved type conversions for byte-related types.&lt;/p&gt;

&lt;p&gt;For example, you can cast the result to an array to see each byte as a number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;87&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;116&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;116&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;84&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;102&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or cast to a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="nv"&gt;"my_bucket:/tour_of_surrealdb.txt"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In cases where the bytes include invalid UTF-8, you can use &lt;code&gt;type::string_lossy()&lt;/code&gt; - a new function that replaces invalid bytes with the &lt;code&gt;�&lt;/code&gt; Unicode replacement character, rather than throwing an error.&lt;/p&gt;

&lt;p&gt;This function is especially useful when working with partially corrupted or inconsistently encoded data. Instead of failing outright, it preserves and returns all valid parts of the string.&lt;/p&gt;

&lt;p&gt;Here’s an example using &lt;code&gt;type::string&lt;/code&gt;, which returns an error because not all the bytes are valid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;254&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;-- 'Could not cast into `string` using input `b"537572FF726561FE6C4442"`'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here’s the same input passed through &lt;code&gt;type::string_lossy()&lt;/code&gt;. We can see that there are some valid bytes in there - enough to make out the word SurrealDB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string_lossy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;83&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;254&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;68&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;66&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;-- 'Sur�rea�lDB'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re excited to introduce this first stage of native file support in SurrealDB. If you’re building apps that rely on lightweight file storage or embedded assets, give it a try - and let us know what features you’d like to see next. This is just the beginning.&lt;/p&gt;

&lt;p&gt;Learn more at &lt;a href="https://surrealdb.com/docs/surrealql/datamodel/files" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealql/datamodel/files&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Indexing and search
&lt;/h3&gt;

&lt;p&gt;We know that index performance and functionality is important, which is why we’re excited to announce a brand-new indexing foundation - delivering faster queries, higher throughput, and smarter planning across the board.&lt;/p&gt;

&lt;p&gt;Until now, SurrealDB’s queries often had to scan entire tables because the planner couldn’t exploit compound or descending indexes, full‑text search only matched all terms, and index updates ran single-threaded. This made searches and analytics painfully slow and blocked high-write workloads, motivating a comprehensive indexing overhaul for 3.0.&lt;/p&gt;

&lt;p&gt;Indexing now combines a log‑based, multi‑writer engine with normalised key formats and smarter planners. Compound indexes support prefix + range scans, descending order, and LIMIT-aware iterators, while standard indexes handle duplicates gracefully. These changes let 3.0 answer many queries directly from indexes under heavy concurrency.&lt;/p&gt;

&lt;p&gt;Let’s take a closer look at these changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compound indexes with prefix + range scans&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In SurrealDB 3.0, compound indexes have been enhanced to support both ****prefix equality and range queries in the same plan.&lt;/p&gt;

&lt;p&gt;That means queries like this are now served directly from the index, without touching the entire table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_customer_created&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;FIELDS&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"acme"&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;  &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="s1"&gt;'2025-02-01'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By constraining the left prefix (&lt;code&gt;customer = "acme"&lt;/code&gt;) and scanning a precise range on &lt;code&gt;created_at&lt;/code&gt;, the query planner can narrow down results efficiently, even on large datasets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Descending order and LIMIT-aware scans&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Queries with &lt;code&gt;ORDER BY ... DESC&lt;/code&gt; can now be satisfied directly from the index on supported backends, eliminating the need for in-memory sorting. Combined with smarter batching that respects &lt;code&gt;LIMIT&lt;/code&gt;, the database returns exactly what you asked for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;"acme"&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach reduces latency, saves memory, and keeps response times consistent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normalised numeric keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Numeric values are now encoded canonically inside indexes. That means that the integer 0, the float 0.0, and 0 decimal are treated as equal in &lt;code&gt;UNIQUE&lt;/code&gt; constraints and scan operations, and range queries run seamlessly across all numeric variants.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;uniq_price&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="n"&gt;FIELDS&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;-- Duplicate error, as expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change brings greater predictability to numeric comparisons, ensuring that equality and ordering work the way you’d expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smarter full-text search&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Full-text indexing has also been upgraded in 3.0. It now supports &lt;code&gt;OR&lt;/code&gt; boolean operations, so you can query for documents that contain &lt;em&gt;any&lt;/em&gt; of the specified terms, not just all of them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;DEFINE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_docs_ft&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="n"&gt;FIELDS&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="n"&gt;FULLTEXT&lt;/span&gt; &lt;span class="n"&gt;ANALYZER&lt;/span&gt; &lt;span class="n"&gt;english&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="k"&gt;OR&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="nv"&gt;"vector graph"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On top of that, full-text indexing now supports concurrent writers, using a log-based approach with background compaction. This makes SurrealDB’s search engine more scalable under high-write workloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrent index builds&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Index creation itself has been improved with concurrent indexing by default. Instead of building an index in one large transaction, 3.0 handles index builds in batches. That means large indexes can be created more efficiently, without running into memory or file space issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The impact&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Moving from O(N) scans to tight indexed lookups drops latency from seconds to milliseconds on million-row tables, while log‑based multi-writer indexing removes write bottlenecks for full‑text searches.&lt;/p&gt;

&lt;p&gt;For developers, the net effect is faster reads, higher throughput, and more predictable tails as your datasets grow.&lt;/p&gt;

&lt;p&gt;Indexing in SurrealDB 3.0 is now faster, more flexible, and more powerful, and this foundation sets us up for future distributed and specialised indexes - including vector and geospatial - enabling SurrealDB to scale to ever-larger, multi-tenant workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing Surrealism
&lt;/h3&gt;

&lt;p&gt;We are excited to introduce Surrealism which offers something developers have wanted for a long time: true extensibility inside SurrealDB itself.&lt;/p&gt;

&lt;p&gt;Surrealism is a new open-source extension system for SurrealDB. It allows you to define modular, programmable logic using functions you write in Rust - and execute them directly within the database at runtime. We'll also soon be supporting a variety of other languages, including JavaScript and Python too.&lt;/p&gt;

&lt;p&gt;This is a big shift.&lt;/p&gt;

&lt;p&gt;Traditionally, if you wanted to extend your database, you had to choose between brittle, complex, error-prone, and potentially huge SQL scripts, heavyweight external services, or just push the data to the application layer and perform the logic there. Surrealism changes that. It brings logic into the database in a way that’s fast, secure, testable, and fully integrated with your development workflow. Surrealism redefines the way that developers and organisations work with stored procedures - allowing for custom business logic, and access control layers as version-controlled, testable, and shareable modules. These extensions execute with full transactional guarantees, and can be used to manage everything from dynamic API behaviours to policy enforcement and auditing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwe1hx5qh6przmfhrfiqr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwe1hx5qh6przmfhrfiqr.png" alt="The workflow for Surrealism extensions, showing how plugins are compiled to webassembly and then fed into SurrealDB." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Surrealism works&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Extensions are compiled to WebAssembly, allowing them to run in a secure, deterministic, sandboxed execution environment, whilst delivering near-native performance, with strict isolation between functions. This makes it ideal for both single-tenant systems and multi-tenant clusters, where isolation and efficiency are critical.&lt;/p&gt;

&lt;p&gt;Extensions can be loaded from local disk, object storage, or uploaded directly into SurrealDB. They can be enabled and executed dynamically - without restarting the database - with fine-grained permission controls to determine who can load or invoke them.&lt;/p&gt;

&lt;p&gt;With Surrealism, the development experience has been designed from the ground up for &lt;strong&gt;productivity&lt;/strong&gt;. Developers can use their own libraries, tools, and frameworks. They can write tests in Rust, integrate extensions into CI and CD pipelines, and manage extension versions just like any other component of their application stack. Extensions are defined in simple projects with metadata in a TOML file, and compiled into portable WebAssembly binaries that can be reused, versioned, and shared.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built-in AI support and 'datagentic' workflows&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Surrealism enables direct integration with AI models, both local and remote. Extensions can interact with GPU-accelerated inferencing runtimes, or call out to external APIs for services like text or image generation, classification, tokenisation, embedding, translation, and anything else.&lt;/p&gt;

&lt;p&gt;This means you could define an extension that takes a user’s input, runs it through a local LLM, and stores the generated result - all within a single SurrealQL query. You can trigger sentiment analysis on inserted documents, run similarity scoring against vector indexes, or use a vision model to extract structured data from uploaded files.&lt;/p&gt;

&lt;p&gt;And because Surrealism extensions have full access to SurrealDB’s query engine, schema, and the new file functionality, they can work with structured data, unstructured content, and multi-modal assets in a single unified system.&lt;/p&gt;

&lt;p&gt;For example, an extension can transcribe an audio file, extract named entities, enrich the results using a remote model, and save the output - all in one transactional flow.&lt;/p&gt;

&lt;p&gt;This allows developers to build intelligent applications entirely within the database. The database becomes more than a data store - it becomes an intelligent, programmable runtime for real-time decision-making, content generation, and AI integration. &lt;/p&gt;

&lt;p&gt;Bringing logic and agentic workflows into the database, rather than pushing the data out to external systems, enables seamless integration for AI, and unlocks new possibilities for intelligent applications. We call this workflow 'datagentic'. It’s fast. It’s flexible. It’s secure. And it’s open source.&lt;/p&gt;

&lt;p&gt;Surrealism is available today for you to start developing your own custom or business logic, and it opens up a whole new frontier in database development - one where you can bring your own code, your own models, and your own ideas directly into the data layer. We can’t wait to see what you build. Learn more at &lt;a href="https://surrealdb.com/docs/surrealdb/extensions" rel="noopener noreferrer"&gt;https://surrealdb.com/docs/surrealdb/extensions&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;Building a database is never finished, and there’s more work we need to do. We’ll continue working on additional performance improvements, more tooling, integrations, and making SurrealDB the best database in the market.&lt;/p&gt;

&lt;p&gt;Update your SurrealDB instance to 3.0 with our &lt;a href="https://surrealdb.com/docs/surrealdb/installation/upgrading/migrating-data-to-3x" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;, join our &lt;a href="https://discord.com/invite/surrealdb" rel="noopener noreferrer"&gt;Discord server&lt;/a&gt; and share with us what you are building.&lt;/p&gt;

&lt;p&gt;Thank you for helping us shape the future of databases.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>surrealdb</category>
      <category>surrealism</category>
    </item>
  </channel>
</rss>
