Skip to content
Get Started

SurrealDB

The rig-surrealdb crate implements Rig’s VectorStoreIndex trait on top of SurrealDB, using SurrealDB’s built-in vector functions (for example vector::similarity::cosine). It works with an in-memory instance for local development or a remote instance in production, and supports several distance metrics.

Add the crate to your project:

Terminal window
cargo add rig-surrealdb

Connect to SurrealDB, embed a set of documents, insert them, then run a similarity search. top_n takes a VectorSearchRequest and deserializes each hit into your type.

use rig::client::{EmbeddingsClient, ProviderClient};
use rig::embeddings::EmbeddingsBuilder;
use rig::providers::openai;
use rig::vector_store::{InsertDocuments, VectorSearchRequest, VectorStoreIndex};
use rig::Embed;
use rig_surrealdb::{Mem, SurrealVectorStore};
use serde::{Deserialize, Serialize};
use surrealdb::Surreal;
// Embeds the `definition` field for similarity search. `definition` is skipped
// during serialization since we only use it to generate embeddings.
#[derive(Embed, Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Default)]
struct WordDefinition {
word: String,
#[serde(skip)]
#[embed]
definition: String,
}
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let openai_client = openai::Client::from_env()?;
let model = openai_client.embedding_model(openai::TEXT_EMBEDDING_3_SMALL);
// In-memory instance; swap `Mem` for a remote engine in production.
let surreal = Surreal::new::<Mem>(()).await?;
surreal.use_ns("example").use_db("example").await?;
let words = vec![
WordDefinition {
word: "flurbo".to_string(),
definition: "A fictional currency from Rick and Morty.".to_string(),
},
WordDefinition {
word: "glarb-glarb".to_string(),
definition: "A creature from the marshlands of Glibbo.".to_string(),
},
];
let documents = EmbeddingsBuilder::new(model.clone())
.documents(words)?
.build()
.await?;
let vector_store = SurrealVectorStore::with_defaults(model, surreal);
vector_store.insert_documents(documents).await?;
let req = VectorSearchRequest::builder()
.query("weird alien creature")
.samples(2)
.build();
let results = vector_store.top_n::<WordDefinition>(req).await?;
for (distance, _id, doc) in results {
println!("Distance: {distance:.3}, Word: {}", doc.word);
}
Ok(())
}
  • SurrealVectorStore::with_defaults(model, surreal) builds a store over the given SurrealDB connection using the default table and cosine distance.
  • insert_documents embeds and inserts documents into the vector index.
  • top_n::<T>(request) embeds the query, runs the search, and deserializes each match into T as (distance, id, T).

Add a threshold to the request to drop weak matches. Thresholds are interpreted against the store’s distance function:

let req = VectorSearchRequest::builder()
.query("weird alien creature")
.samples(1)
.threshold(0.5)
.build();
let results = vector_store.top_n::<WordDefinition>(req).await?;

The default is cosine. To customize the table name and distance metric, construct the store directly:

use rig_surrealdb::SurrealDistanceFunction;
let custom_store = SurrealVectorStore::new(
model,
surreal,
Some("my_table".into()),
SurrealDistanceFunction::Jaccard,
);

Available options:

  • Cosine (default)
  • Euclidean
  • Hamming
  • Jaccard
  • Knn