<?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: Mark Gyles</title>
    <description>The latest articles on Forem by Mark Gyles (@mgyles).</description>
    <link>https://forem.com/mgyles</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%2Fuser%2Fprofile_image%2F1602559%2Ff82af600-3936-4f4a-a067-dfee7a3228d4.jpg</url>
      <title>Forem: Mark Gyles</title>
      <link>https://forem.com/mgyles</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mgyles"/>
    <language>en</language>
    <item>
      <title>Graph RAG does not need a graph database. It needs a database that does everything.</title>
      <dc:creator>Mark Gyles</dc:creator>
      <pubDate>Tue, 07 Apr 2026 15:57:35 +0000</pubDate>
      <link>https://forem.com/surrealdb/graph-rag-does-not-need-a-graph-database-it-needs-a-database-that-does-everything-117k</link>
      <guid>https://forem.com/surrealdb/graph-rag-does-not-need-a-graph-database-it-needs-a-database-that-does-everything-117k</guid>
      <description>&lt;p&gt;Author: &lt;a href="https://www.linkedin.com/in/mpenaroza/" rel="noopener noreferrer"&gt;Matthew Penaroza&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How SurrealDB compares to Neo4j, Amazon Neptune, and ArangoDB for production graph RAG.&lt;/p&gt;

&lt;p&gt;Graph RAG is the right idea. Using relationships between entities to scope and improve retrieval produces better results than vector similarity alone. The research is clear on this. What the research does not address is where those operations execute, and that turns out to be the question that actually matters in production. Here is what happens when you try to build production graph RAG across a typical multi-database stack, and what changes when every operation composes in a single system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap: what graph RAG actually needs in production
&lt;/h2&gt;

&lt;p&gt;Most graph RAG implementations follow the pattern from Microsoft's GraphRAG paper: extract entities from documents, build a knowledge graph of those entities and their relationships, then traverse that graph at query time to pull relevant context. The graph is derived from the content. It is an inferred structure that helps you find documents that are semantically connected through shared entities.&lt;/p&gt;

&lt;p&gt;That works for document discovery over a static corpus. It does not work for a production agent that needs to serve a specific customer, respect access control, enforce tenancy, check data freshness, and ground its answers in real business relationships rather than inferred ones.&lt;/p&gt;

&lt;p&gt;A production graph RAG pipeline needs all of the following in a single retrieval step:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ground truth relationships.&lt;/strong&gt; This customer purchased this product. This product has this known defect. This defect maps to these knowledge base articles. These are not inferred from document content. They are transactional facts from your purchase system, your CRM, your product catalog. The graph traversal that scopes retrieval needs to follow these real edges, not entity co-occurrence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LLM-inferred relationships.&lt;/strong&gt; This knowledge base article documents a fix for this issue. This article is related to that article. These connections are useful and hard to create manually at scale. An extraction pipeline reads the article and the issue description and decides they are connected. This is exactly what the GraphRAG pattern provides, and it is valuable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured filters.&lt;/strong&gt; Type constraints, tenant isolation, permission checks. Not every document that is relevant is valid for this agent to return to this customer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Temporal constraints.&lt;/strong&gt; The article was updated within 30 days. The product has not been discontinued. The issue is still active.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hybrid retrieval.&lt;/strong&gt; Vector similarity and full-text search, blended into a single relevance score, running against the candidates that survived everything above.&lt;/p&gt;

&lt;p&gt;None of these are controversial. Every serious production RAG system needs them. The question is where they execute and whether they compose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The comparison: the same pipeline in SurrealQL and Cypher
&lt;/h2&gt;

&lt;p&gt;Neo4j is the most established graph database. Since version 5.11, it has native vector indexes. It has full-text indexes. It can store properties on nodes and relationships. There is no technical reason you cannot build this entire pipeline in Neo4j.&lt;/p&gt;

&lt;p&gt;Here is the retrieval operation in SurrealQL. Same scoping, graph traversal, hybrid search, and result set as you would build in Cypher, but expressed as a single statement with co-equal predicates:&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;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;knn&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;vec_dist&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="n"&gt;score&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;AS&lt;/span&gt; &lt;span class="n"&gt;ft_score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;knn&lt;/span&gt;&lt;span class="p"&gt;())&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;65&lt;/span&gt;
    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;search&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;score&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="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;35&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;blend&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="n"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;em&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;/em&amp;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="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;snippet&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;knowledge_base&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'support'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;tenant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;tenant&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;agent_principal&lt;/span&gt; &lt;span class="n"&gt;INSIDE&lt;/span&gt; &lt;span class="n"&gt;allowed_principals&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;&amp;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;30&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;id&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;customer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;owns&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;
      &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;has_issue&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;knowledge_base&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;content_embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;|&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;COSINE&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;query_embedding&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;query_text&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;blend&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;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One statement. Co-equal predicates. One consistent snapshot.&lt;/p&gt;

&lt;p&gt;In SurrealQL, the graph traversal, structured filters, vector search, and full-text search are co-equal predicates in a single &lt;code&gt;WHERE&lt;/code&gt; clause. They compose the same way boolean conditions always compose: with &lt;code&gt;AND&lt;/code&gt;. The query reads like a single thought. You can rearrange the predicates, add new ones, or remove them, and the statement still works.&lt;/p&gt;

&lt;p&gt;In Cypher, the same operations require separate procedural steps. Vector search is a &lt;code&gt;CALL db.index.vector.queryNodes()&lt;/code&gt; block. Full-text search is another &lt;code&gt;CALL db.index.fulltext.queryNodes()&lt;/code&gt; block. The graph traversal is a &lt;code&gt;MATCH&lt;/code&gt; pattern. The metadata filters are a &lt;code&gt;WHERE&lt;/code&gt; clause. The blending is manual arithmetic in the &lt;code&gt;RETURN&lt;/code&gt;. You are orchestrating five steps and correlating their results yourself. It works, but it reads like a script, not a query.&lt;/p&gt;

&lt;p&gt;The readability gap is the obvious problem. The less obvious one is memory. Each procedural step in the Cypher pipeline &lt;code&gt;YIELD&lt;/code&gt;s a result set into the execution context, and those intermediate results stay in memory while the next step runs. The vector &lt;code&gt;CALL&lt;/code&gt; yields 100 nodes with scores. Those 100 nodes sit in memory while the &lt;code&gt;MATCH&lt;/code&gt; clause runs graph pattern matching against them. The &lt;code&gt;MATCH&lt;/code&gt; can fan out fast: if a customer owns 5 products, each product has 10 known issues, and each issue maps to 20 knowledge base articles, the pattern expands to 1,000 intermediate rows before any &lt;code&gt;WHERE&lt;/code&gt; filter trims them. All materialized. All held. Then the full-text &lt;code&gt;CALL&lt;/code&gt; runs and yields another result set that needs to be correlated with everything already in memory.&lt;/p&gt;

&lt;p&gt;At production scale, this is not a theoretical concern. Combinatorial expansion in multi-hop &lt;code&gt;MATCH&lt;/code&gt; patterns is one of the most common causes of out-of-memory failures in Neo4j. The intermediate state accumulates faster than the downstream filters can prune it, and the query planner cannot optimize across the boundaries between &lt;code&gt;CALL&lt;/code&gt; blocks and &lt;code&gt;MATCH&lt;/code&gt; clauses because they are procedurally separate operations. You end up tuning heap sizes, adding &lt;code&gt;LIMIT&lt;/code&gt; hints mid-pipeline, or splitting queries into multiple round-trips to keep memory under control. All of which is managing a problem that does not need to exist.&lt;/p&gt;

&lt;p&gt;In SurrealQL, because all predicates are co-equal in one &lt;code&gt;WHERE&lt;/code&gt; clause, the engine sees every constraint upfront. It can start with the most selective predicate and narrow early before touching the expensive operations. There is no intermediate materialization between steps because there are no steps. The result set is built once, not accumulated across a pipeline.&lt;/p&gt;

&lt;p&gt;This matters for day-to-day development too. When retrieval logic is a single composable statement, you can modify it, extend it, and reason about it as a unit. When it is a multi-step procedure, every change requires understanding the data flow between steps. Adding a new filter means figuring out which step it belongs to and whether it changes what gets passed to the next step. Removing a constraint means tracing its effects through the pipeline. The cognitive overhead scales with the number of operations, and production retrieval pipelines have a lot of operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deeper problem: your graph database is not your system of record
&lt;/h2&gt;

&lt;p&gt;Neo4j is almost never where the data originates. The purchase happens in Postgres. The ticket gets created in a CRM. The permission change happens in an auth service. Neo4j receives all of that via sync jobs, CDC pipelines, or batch imports. It is a read replica of graph-shaped data. The same is true of Neptune and, in most deployments, ArangoDB. The graph database is downstream of the systems that produce the data your agent needs to reason over.&lt;/p&gt;

&lt;p&gt;That is why the consistency problem exists in the first place. The graph is always behind the source of truth because it is not the source of truth. A permission gets revoked in the auth service, but the graph database does not see it for 50ms and the vector index does not see it for 200ms. An article gets updated in the CMS, but the vector store still has embeddings from the old version for up to 5 minutes. Each system is internally consistent. The inconsistency lives in the spaces between them. Your agent is reasoning over multiple realities simultaneously and does not know it.&lt;/p&gt;

&lt;p&gt;SurrealDB is designed as a transactional system of record. The purchase record, the customer relationship, the product graph, the knowledge base articles, the embeddings, the agent's memory, and the LLM-inferred edges from extraction pipelines all live in the same database as first-class data. The ground truth edges are not synced from somewhere else. They are the transaction records:&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;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- The purchase record IS the graph edge&lt;/span&gt;
&lt;span class="n"&gt;RELATE&lt;/span&gt; &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;4821&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;purchased&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;drip_coffee_maker&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt;
  &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ORD-29481'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;purchased_at&lt;/span&gt; &lt;span class="o"&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;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'transaction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;confidence&lt;/span&gt; &lt;span class="o"&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;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Product-to-known-issue edge (from engineering)&lt;/span&gt;
&lt;span class="n"&gt;RELATE&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;drip_coffee_maker&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;has_issue&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;product_issue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gasket_defect&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt;
  &lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'moderate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;confirmed_at&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;'2026-02-15'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'engineering'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;confidence&lt;/span&gt; &lt;span class="o"&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;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;COMMIT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment that transaction commits, &lt;code&gt;customer:4821-&amp;gt;purchased-&amp;gt;product:drip_coffee_maker&lt;/code&gt; is traversable. There is no sync job. There is no CDC pipeline. There is no replication lag. An agent querying one millisecond later sees it.&lt;/p&gt;

&lt;p&gt;The LLM-inferred edges write to the same database. An extraction pipeline reads a knowledge base article and the issue description, decides they are connected, and creates the edge:&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;RELATE&lt;/span&gt; &lt;span class="n"&gt;product_issue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;gasket_defect&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;documented_in&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;kb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;fix_base_leaks&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt;
  &lt;span class="k"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'llm_extraction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;confidence&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;94&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'claude-sonnet-4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;verified_by&lt;/span&gt; &lt;span class="o"&gt;=&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;created_at&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single traversal now crosses both: &lt;code&gt;$customer-&amp;gt;purchased-&amp;gt;product-&amp;gt;has_issue-&amp;gt;documented_in-&amp;gt;knowledge_base&lt;/code&gt;. The first two hops are ground truth from the transaction. The last hop is LLM-inferred from the extraction pipeline. Every predicate in the query evaluates against the same ACID snapshot.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is not a convenience feature. It eliminates an entire category of infrastructure: the sync jobs, the CDC pipelines, the cache invalidation, the eventual consistency windows. Every one of those is a place where accuracy silently degrades. A database that is both the system of record and the query engine removes the gaps where those failures live.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The landscape: how other graph databases handle this
&lt;/h2&gt;

&lt;p&gt;SurrealDB is not the only database attempting to unify these operations. Every major graph database has recognized that vector search matters for graph RAG, and each has added some form of it. The question is not whether they support it. It is whether the operations compose, and what breaks when they do not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Neo4j
&lt;/h3&gt;

&lt;p&gt;Neo4j is the most established graph database and the most natural comparison. As of v2026.01, Neo4j introduced a native &lt;code&gt;SEARCH&lt;/code&gt; clause with in-index filtering as a preview feature. Before this, vector search could not be pre-filtered at all. You ran the vector index, got results, and filtered afterward. For years, this was a blocking issue for teams building multi-tenant RAG, and community threads going back to 2023 show developers hitting this wall repeatedly.&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;SEARCH&lt;/code&gt; clause is a step forward, but the &lt;code&gt;WHERE&lt;/code&gt; subclause inside &lt;code&gt;SEARCH&lt;/code&gt; supports only a subset of the full Cypher &lt;code&gt;WHERE&lt;/code&gt; clause. Full-text search is still a separate procedure call that cannot compose with the &lt;code&gt;SEARCH&lt;/code&gt; clause in a single statement. The graph traversal is still a separate &lt;code&gt;MATCH&lt;/code&gt; pattern. So even with the improvement, you are still orchestrating procedural steps and correlating results between them, with the memory accumulation problem described earlier in this article. Neo4j is actively closing the gap, but as of today the composition is partial.&lt;/p&gt;

&lt;p&gt;Where Neo4j remains the clear winner is deep graph analytics. The Graph Data Science library (community detection, centrality algorithms, pathfinding, node similarity at scale) is purpose-built and has no equivalent in SurrealDB. If your primary workload is analytical graph algorithms rather than transactional agent retrieval, Neo4j is the right tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Amazon Neptune
&lt;/h3&gt;

&lt;p&gt;Amazon Neptune is the default choice for teams already on AWS, and it illustrates the composition problem at the product level. Neptune is actually two separate products. Neptune Database is a transactional graph database with no native vector search. Neptune Analytics is an analytics engine with vector search and graph algorithms but designed for analytical workloads, not transactional retrieval. AWS's own "unified" graph solution is itself a two-database architecture.&lt;/p&gt;

&lt;p&gt;Neptune Analytics has vector search, but with fundamental constraints. Vector index updates are explicitly not ACID compliant: changes to vector embeddings are non-atomic and become visible to concurrent queries immediately, even if the query that wrote them fails later. If a bulk load with embeddings fails midway, you end up with a partial set of embeddings and need to retry the entire operation. You can only create one vector index per graph, and it must be specified at graph creation time. There is no native full-text search. And because Neptune Analytics is an analytics engine, not a transactional store, it is designed for loading data from S3 or snapshotting from Neptune Database, not for serving live agent queries against data that changes in real time.&lt;/p&gt;

&lt;h3&gt;
  
  
  ArangoDB
&lt;/h3&gt;

&lt;p&gt;ArangoDB is the closest multi-model competitor to SurrealDB's architecture. It supports documents, graphs, key-value, full-text search (via ArangoSearch), and as of version 3.12.4, vector search via FAISS integration. On paper, it has every primitive under one roof.&lt;/p&gt;

&lt;p&gt;ArangoDB's vector index requires a &lt;code&gt;--vector-index&lt;/code&gt; startup flag that permanently alters the RocksDB storage engine and cannot be reversed. Since v3.12.6, ArangoDB has supported attribute pre-filtering on vector searches — you can place &lt;code&gt;FILTER&lt;/code&gt; operations between &lt;code&gt;FOR&lt;/code&gt; and &lt;code&gt;SORT&lt;/code&gt; to narrow candidates before the vector index runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FOR doc IN knowledge_base
  FILTER doc.tenant == @tenant
  FILTER doc.type == 'support'
  SORT APPROX_NEAR_COSINE(doc.embedding, @query_emb) DESC
  LIMIT 10
  RETURN doc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This addresses the multi-tenant filtering problem for attribute-based constraints and is a genuine improvement over earlier versions. Where the composition breaks down is the full retrieval pipeline. ArangoSearch (the full-text engine) uses a separate &lt;code&gt;SEARCH&lt;/code&gt; operation with its own syntax and optimizer path. Graph traversals use &lt;code&gt;FOR v, e, p IN ... GRAPH&lt;/code&gt; with a different iteration model. Vector search requires iterating over a collection directly with &lt;code&gt;APPROX_NEAR_*&lt;/code&gt; functions. Composing all three — traversing a customer's product graph to scope candidates, running vector similarity against those scoped candidates, and blending in full-text relevance scores — requires orchestrating these as separate operations within AQL rather than expressing them as co-equal predicates in a single statement.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The pattern across all three is the same. Each database has added vector search as a capability. None of them have made it compose natively with graph traversal, structured filters, and full-text search in a single atomic statement. The operations exist. The composition does not. That composition is the specific thing SurrealDB was designed to provide.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Honest tradeoffs: when SurrealDB is not the right choice
&lt;/h2&gt;

&lt;p&gt;If your primary workload is deep graph analytics (community detection, centrality algorithms, pathfinding over billions of edges), Neo4j's Graph Data Science library or TigerGraph's analytical engine are purpose-built for that. SurrealDB does not have an equivalent analytics library.&lt;/p&gt;

&lt;p&gt;The honest question is whether your current retrieval accuracy is good enough. If your agents are producing correct results at an acceptable rate and the failure modes described in this article are not showing up in production, there is no reason to migrate. The architecture you have is working.&lt;/p&gt;

&lt;p&gt;If you are trying to push accuracy higher and you are hitting a ceiling, the pattern is almost always the same: the individual operations work, but they do not compose. The vector search returns good candidates. The graph traversal follows the right paths. The filters check the right fields. But they run separately, against different snapshots, with intermediate results accumulating in memory, and the accuracy loss lives in the gaps between them. That is not a problem you can solve by tuning parameters. It is a structural limitation of running these operations across systems that were not designed to compose them. Closing that gap requires a database where graph traversal, vector search, full-text search, structured filters, and permission checks are co-equal predicates in a single atomic statement. That is what SurrealDB provides.&lt;/p&gt;

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

&lt;p&gt;Every graph database covered in this article can store graphs, run traversals, and return results. Most of them can now do vector search. The thing none of them can do is compose all of it in a single atomic statement against a single consistent snapshot while also being the transactional system of record for the data your agent reasons over.&lt;/p&gt;

&lt;p&gt;That is not a feature gap. It is an architectural one. And it is the gap where retrieval accuracy goes to die.&lt;/p&gt;

</description>
      <category>graphrag</category>
      <category>rag</category>
      <category>surrealdb</category>
      <category>database</category>
    </item>
    <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>
  </channel>
</rss>
