Skip to main content

DatabaseBackend

Trait DatabaseBackend 

Source
pub trait DatabaseBackend:
    Send
    + Sync
    + 'static {
    // Required methods
    fn capabilities(&self) -> CapabilitySet;
    fn catalog(
        &self,
        request: CatalogRequest,
    ) -> impl Future<Output = Result<CatalogResponse, StorageError>> + Send + '_;
    fn data(
        &self,
        request: DataRequest,
    ) -> impl Future<Output = Result<DataResponse, StorageError>> + Send + '_;
    fn query(
        &self,
        request: QueryRequest,
    ) -> impl Future<Output = Result<QueryResponse, StorageError>> + Send + '_;
    fn index(
        &self,
        request: IndexRequest,
    ) -> impl Future<Output = Result<IndexResponse, StorageError>> + Send + '_;
}
Expand description

Portable data, catalog, query, and index behavior.

Implement this trait directly on a caller-owned type; Clone is not required. Operations are authoritative: an optional capability bit does not guarantee that an arbitrary request is supported. Unsupported operations fail before mutation, and responses preserve real ids, counts and score semantics. Supporting DTOs live in crate::database.

§Caller-owned catalog example

This small test double validates one request and returns an empty catalog. It demonstrates direct trait consumption, not a database engine. Other operations reject explicitly; no native storage or service is constructed.

use storage_kit::{DatabaseBackend, StorageError};
use storage_kit::database::CapabilitySet;
use storage_kit::database::catalog::{CatalogRequest, CatalogResponse, ListCollectionsRequest};

struct EmptyCatalog;
impl DatabaseBackend for EmptyCatalog {
    fn capabilities(&self) -> CapabilitySet { CapabilitySet::empty() }
    async fn catalog(&self, request: CatalogRequest) -> Result<CatalogResponse, StorageError> {
        assert_eq!(request, CatalogRequest::ListCollections(ListCollectionsRequest {
            namespace: Vec::new(), limit: Some(10), start_after: None,
        }));
        Ok(CatalogResponse::Collections(Vec::new()))
    }
}
let database = EmptyCatalog;
let response = database.catalog(CatalogRequest::ListCollections(ListCollectionsRequest {
    namespace: Vec::new(), limit: Some(10), start_after: None,
})).await?;
let CatalogResponse::Collections(collections) = response else {
    panic!("expected the catalog response");
};
assert!(collections.is_empty());
assert!(database.capabilities().is_empty());

An application may compose different implementations in its own exhaustive enum, forwarding requests and errors unchanged. See the executable caller composition tests. The local collection workflow uses real embedded storage.

Required Methods§

Source

fn capabilities(&self) -> CapabilitySet

Returns optional retained capabilities.

Use this to discover optional operation families, not to identify the implementation or assume an arbitrary request will succeed.

See the caller-owned catalog example for an executable example.

Source

fn catalog( &self, request: CatalogRequest, ) -> impl Future<Output = Result<CatalogResponse, StorageError>> + Send + '_

Executes a retained catalog operation.

Creation, opening, listing, deletion, and schema evolution remain distinct from data writes and StateStore transactions.

§Errors

Returns StorageError for invalid scope or request data, unsupported operations, and native connection, deadline, or execution failures. An unsupported operation must fail before mutation.

See the caller-owned catalog example for an executable example.

Source

fn data( &self, request: DataRequest, ) -> impl Future<Output = Result<DataResponse, StorageError>> + Send + '_

Executes a retained data operation.

Ordinary writes are not rollback-capable multi-request transactions. A native failure or timeout can leave an uncertain or partial outcome and must not trigger automatic replay. Partition replacement retains its explicit atomic contract.

§Errors

Returns StorageError for invalid input, unsupported request combinations, read-only policy, serialization failures, and native execution/deadline failures. Unsupported behavior must be rejected before mutation.

See the local collection workflow for an executable example.

Source

fn query( &self, request: QueryRequest, ) -> impl Future<Output = Result<QueryResponse, StorageError>> + Send + '_

Executes a retained query operation.

Return continuation tokens unchanged to the same applicable operation. Scores preserve their explicit kind; a missing score is not zero.

§Errors

Returns StorageError for invalid scope, vectors, fields, filters, or tuning, unsupported query combinations, and native execution/deadline failures. Optional capability bits do not override request validation.

§Examples
use storage_kit::{Database, DatabaseBackend};
use storage_kit::database::config::{DatabaseConfig, LanceConfig};
use storage_kit::database::StorageLocator;
use storage_kit::database::catalog::{CatalogRequest, CreateCollectionRequest, CreateCollectionMode};
use storage_kit::database::data::DataRecord;
use storage_kit::database::query::{QueryRequest, ScrollRequest, Projection};
let database = Database::connect(DatabaseConfig::lance(LanceConfig::embedded_path("memory://"))).await?;
let locator = StorageLocator::new("documents");
database.catalog(CatalogRequest::CreateCollection(CreateCollectionRequest {
    locator: locator.clone(), schema: None,
    records: vec![DataRecord::without_id().json_field("text", serde_json::json!("example"))],
    mode: CreateCollectionMode::Create,
})).await?;
let response = database.query(QueryRequest::Scroll(ScrollRequest {
    locator, filter: None, projection: Projection::All, limit: Some(10),
    offset: None, page_token: None,
})).await?;
assert_eq!(response.hits.len(), 1);
Source

fn index( &self, request: IndexRequest, ) -> impl Future<Output = Result<IndexResponse, StorageError>> + Send + '_

Executes a retained index operation.

Creating an index and observing its readiness are separate operations. Readiness requests must finish within both their requested wait bound and the enclosing configured operation deadline.

§Errors

Returns StorageError for malformed index requests, unsupported kinds, read-only policy, missing targets, unconfirmed completion, or native execution/readiness deadline failures.

§Examples
use storage_kit::{Database, DatabaseBackend};
use storage_kit::database::config::{DatabaseConfig, LanceConfig};
use storage_kit::database::StorageLocator;
use storage_kit::database::catalog::{CatalogRequest, CreateCollectionRequest, CreateCollectionMode};
use storage_kit::database::data::DataRecord;
use storage_kit::database::index::{IndexRequest, IndexResponse};
let database = Database::connect(DatabaseConfig::lance(LanceConfig::embedded_path("memory://"))).await?;
let locator = StorageLocator::new("documents");
database.catalog(CatalogRequest::CreateCollection(CreateCollectionRequest {
    locator: locator.clone(), schema: None,
    records: vec![DataRecord::without_id().json_field("text", serde_json::json!("example"))],
    mode: CreateCollectionMode::Create,
})).await?;
let response = database.index(IndexRequest::List { locator }).await?;
assert!(matches!(response, IndexResponse::Indexes(_)));

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§