Repository: scylladb/vector-store
Pull request #41: Implement simple create/search/delete integration test
Author: login: ewienik
Created: 2025-04-10T08:39:02Z | Merged: 2025-04-15T09:58:38Z

This is a part of #3.

This change implement simple create index, search ann, delete index process using usearch indexing. It implements a db basic mockup and http client for accessing http api of the service.

Fixes: #3


Diff:
diff --git a/Cargo.lock b/Cargo.lock
index 6dada053..4f97bf1b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -137,6 +137,12 @@ dependencies = [
  "bytemuck",
 ]
 
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
 [[package]]
 name = "autocfg"
 version = "1.4.0"
@@ -625,6 +631,15 @@ version = "1.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
 
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "equivalent"
 version = "1.0.2"
@@ -821,6 +836,25 @@ version = "0.31.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
 
+[[package]]
+name = "h2"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap 2.8.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
 [[package]]
 name = "hashbrown"
 version = "0.12.3"
@@ -916,6 +950,7 @@ dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
+ "h2",
  "http",
  "http-body",
  "httparse",
@@ -927,6 +962,23 @@ dependencies = [
  "want",
 ]
 
+[[package]]
+name = "hyper-rustls"
+version = "0.27.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
+dependencies = [
+ "futures-util",
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
 [[package]]
 name = "hyper-tls"
 version = "0.6.0"
@@ -1741,12 +1793,15 @@ dependencies = [
  "async-compression",
  "base64",
  "bytes",
+ "encoding_rs",
  "futures-core",
  "futures-util",
+ "h2",
  "http",
  "http-body",
  "http-body-util",
  "hyper",
+ "hyper-rustls",
  "hyper-tls",
  "hyper-util",
  "ipnet",
@@ -1762,6 +1817,7 @@ dependencies = [
  "serde_json",
  "serde_urlencoded",
  "sync_wrapper",
+ "system-configuration",
  "tokio",
  "tokio-native-tls",
  "tokio-util",
@@ -1774,6 +1830,20 @@ dependencies = [
  "windows-registry",
 ]
 
+[[package]]
+name = "ring"
+version = "0.17.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.15",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
 [[package]]
 name = "rust-embed"
 version = "8.6.0"
@@ -1836,6 +1906,19 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "rustls"
+version = "0.23.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
+dependencies = [
+ "once_cell",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
 [[package]]
 name = "rustls-pemfile"
 version = "2.2.0"
@@ -1851,6 +1934,17 @@ version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
 
+[[package]]
+name = "rustls-webpki"
+version = "0.103.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
 [[package]]
 name = "rustversion"
 version = "1.0.20"
@@ -2236,6 +2330,12 @@ version = "0.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
 [[package]]
 name = "syn"
 version = "2.0.100"
@@ -2267,6 +2367,27 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "system-configuration"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
 [[package]]
 name = "tempfile"
 version = "3.19.1"
@@ -2399,6 +2520,16 @@ dependencies = [
  "tokio",
 ]
 
+[[package]]
+name = "tokio-rustls"
+version = "0.26.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
+dependencies = [
+ "rustls",
+ "tokio",
+]
+
 [[package]]
 name = "tokio-util"
 version = "0.7.14"
@@ -2602,6 +2733,12 @@ version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
 
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
 [[package]]
 name = "url"
 version = "2.5.4"
@@ -2724,6 +2861,7 @@ dependencies = [
  "opensearch",
  "rayon",
  "regex",
+ "reqwest",
  "scylla 1.0.0",
  "scylla-cdc",
  "serde",
@@ -3201,6 +3339,12 @@ dependencies = [
  "synstructure",
 ]
 
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+
 [[package]]
 name = "zerovec"
 version = "0.10.4"
diff --git a/Cargo.toml b/Cargo.toml
index 53bd5e11..bd020ca8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -34,3 +34,6 @@ utoipa = { version = "5.3.1", features = ["axum_extras"] }
 utoipa-axum = "0.2.0"
 utoipa-swagger-ui = { version = "9.0.0", features = ["axum"] }
 uuid = "1.16.0"
+
+[dev-dependencies]
+reqwest = { version = "0.12.15", features = ["json"] }
diff --git a/src/lib.rs b/src/lib.rs
index 722e4039..67f49545 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -236,6 +236,7 @@ pub struct Dimensions(NonZeroUsize);
     Copy,
     Clone,
     Debug,
+    Default,
     PartialEq,
     Eq,
     Hash,
@@ -252,6 +253,7 @@ pub struct Connectivity(usize);
     Copy,
     Clone,
     Debug,
+    Default,
     PartialEq,
     Eq,
     Hash,
@@ -269,6 +271,7 @@ pub struct ExpansionAdd(usize);
     Copy,
     Clone,
     Debug,
+    Default,
     PartialEq,
     Eq,
     Hash,
diff --git a/tests/integration/db_basic.rs b/tests/integration/db_basic.rs
new file mode 100644
index 00000000..21e12a95
--- /dev/null
+++ b/tests/integration/db_basic.rs
@@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_column: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} does not exist");
+        };
+        let Some(table) = keyspace.tables.get(&index.table_name) else {
+            bail!("a table {} does not exist", index.table_name);
+        };
+        if !table.embeddings.contains_key(&index.target_column) {
+            bail!(
+                "a table {} does not contain a target column {}",
+                index.table_name,
+                index.target_column
+            );
+        }
+        if keyspace.indexes.contains_key(&index_name) {
+            bail!("an index {index_name} already exists");
+        }
+        keyspace.indexes.insert(index_name, IndexStore::new(index));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn del_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: &TableName,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} does not exist");
+        };
+        if keyspace.indexes.remove(index_name).is_none() {
+            bail!("an index {index_name} does not exist");
+        }
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn insert_values(
+        &self,
+        keyspace_name: &KeyspaceName,
+        table_name: &TableName,
+        target_column: &ColumnName,
+        values: impl IntoIterator<Item = (Key, Embeddings)>,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} does not exist");
+        };
+        let Some(table) = keyspace.tables.get_mut(table_name) else {
+            bail!("a table {table_name} does not exist");
+        };
+        let Some(column) = table.embeddings.get_mut(target_column) else {
+            bail!("a column {target_column} does not exist in a table {table_name}");
+        };
+
+        values.into_iter().for_each(|(key, embeddings)| {
+            column.insert(key, (embeddings, false));
+        });
+
+        Ok(())
+    }
+
+    pub(crate) fn get_indexed_elements_count(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: &TableName,
+    ) -> anyhow::Result<IndexItemsCount> {
+        let db = self.0.read().unwrap();
+        let Some(keyspace) = db.keyspaces.get(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} does not exist");
+        };
+        let Some(index) = keyspace.indexes.get(index_name) else {
+            bail!("an index {index_name} does not exist");
+        };
+        Ok(index.elements_count)
+    }
+}
+
+fn process_db(db: &DbBasic, msg: Db) {
+    match msg {
+        Db::GetDbIndex { metadata, tx } => tx
+            .send(new_db_index(db.clone(), metadata))
+            .map_err(|_| anyhow!("Db::GetDbIndex: unable to send response"))
+            .unwrap(),
+
+        Db::LatestSchemaVersion { tx } => tx
+            .send(Ok(Some(db.0.read().unwrap().schema_version)))
+            .map_err(|_| anyhow!("Db::LatestSchemaVersion: unable to send response"))
+            .unwrap(),
+
+        Db::GetIndexes { tx } => tx
+            .send(Ok(db
+                .0
+                .read()
+                .unwrap()
+                .keyspaces
+                .iter()
+                .flat_map(|(keyspace_name, keyspace)| {
+                    keyspace
+                        .indexes
+                        .iter()
+                        .map(|(index_name, index)| DbCustomIndex {
+                            keyspace: keyspace_name.clone(),
+                            index: index_name.clone(),
+                            table: index.index.table_name.clone(),
+                            target_column: index.index.target_column.clone(),
+                        })
+                })
+                .collect()))
+            .map_err(|_| anyhow!("Db::GetIndexes: unable to send response"))
+            .unwrap(),
+
+        Db::GetIndexVersion {
+            keyspace,
+            index,
+            tx,
+        } => tx
+            .send(Ok(db
+                .0
+                .read()
+                .unwrap()
+                .keyspaces
+                .get(&keyspace)
+                .and_then(|keyspace| keyspace.indexes.get(&index))
+                .map(|index| index.version.into())))
+            .map_err(|_| anyhow!("Db::GetIndexVersion: unable to send response"))
+            .unwrap(),
+
+        Db::GetIndexTargetType {
+            keyspace,
+            table,
+            target_column,
+            tx,
+        } => tx
+            .send(Ok(db
+                .0
+                .read()
+                .unwrap()
+                .keyspaces
+                .get(&keyspace)
+                .and_then(|keyspace| keyspace.tables.get(&table))
+                .and_then(|table| table.table.dimensions.get(&target_column))
+                .cloned()))
+            .map_err(|_| anyhow!("Db::GetIndexTargetType: unable to send response"))
+            .unwrap(),
+
+        Db::GetIndexParams { id, tx } => tx
+            .send(Ok(db
+                .0
+                .read()
+                .unwrap()
+                .keyspaces
+                .get(&id.keyspace())
+                .and_then(|keyspace| keyspace.indexes.get(&id.index()))
+                .map(|index| {
+                    (
+                        index.index.connectivity,
+                        index.index.expansion_add,
+                        index.index.expansion_search,
+                    )
+                })))
+            .map_err(|_| anyhow!("Db::GetIndexParams: unable to send response"))
+            .unwrap(),
+
+        Db::UpdateItemsCount { id, items_count } => {
+            if let Some(index) =
+                db.0.write()
+                    .unwrap()
+                    .keyspaces
+                    .get_mut(&id.keyspace())
+                    .and_then(|keyspace| keyspace.indexes.get_mut(&id.index()))
+            {
+                index.elements_count = items_count;
+            }
+        }
+
+        Db::RemoveIndex { id } => {
+            if let Some(keyspace) = db.0.write().unwrap().keyspaces.get_mut(&id.keyspace()) {
+                keyspace.indexes.remove(&id.index());
+            }
+        }
+    }
+}
+
+pub(crate) fn new_db_index(
+    db: DbBasic,
+    metadata: IndexMetadata,
+) -> anyhow::Result<mpsc::Sender<DbIndex>> {
+    let (tx, mut rx) = mpsc::channel(10);
+    tokio::spawn({
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db_index(&db, &metadata, msg).await;
+            }
+        }
+    });
+    Ok(tx)
+}
+
+async fn process_db_index(db: &DbBasic, metadata: &IndexMetadata, msg: DbIndex) {
+    match msg {
+        DbIndex::GetProcessedIds { tx } => tx
+            .send(Ok(stream::iter(
+                db.0.read()
+                    .unwrap()
+                    .keyspaces
+                    .get(&metadata.keyspace_name)
+                    .and_then(|keyspace| keyspace.tables.get(&metadata.table_name))
+                    .and_then(|table| table.embeddings.get(&metadata.target_column))
+                    .map(|rows| {
+                        rows.iter()
+                            .filter_map(|(key, (_, processed))| processed.then_some(Ok(*key)))
+                            .collect_vec()
+                    })
+                    .unwrap_or_default(),
+            )
+            .boxed()))
+            .map_err(|_| anyhow!("DbIndex::GetProcessedIds: unable to send response"))
+            .unwrap(),
+
+        DbIndex::GetItems { tx } => tx
+            .send(Ok(stream::iter(
+                db.0.read()
+                    .unwrap()
+                    .keyspaces
+                    .get(&metadata.keyspace_name)
+                    .and_then(|keyspace| keyspace.tables.get(&metadata.table_name))
+                    .and_then(|table| table.embeddings.get(&metadata.target_column))
+                    .map(|rows| {
+                        rows.iter()
+                            .filter_map(|(key, (embeddings, processed))| {
+                                (!processed).then_some(Ok((*key, embeddings.clone())))
+                            })
+                            .collect_vec()
+                    })
+                    .unwrap_or_default(),
+            )
+            .boxed()))
+            .map_err(|_| anyhow!("DbIndex::GetItems: unable to send response"))
+            .unwrap(),
+
+        DbIndex::ResetItems { keys } => {
+            if let Some(rows) =
+                db.0.write()
+                    .unwrap()
+                    .keyspaces
+                    .get_mut(&metadata.keyspace_name)
+                    .and_then(|keyspace| keyspace.tables.get_mut(&metadata.table_name))
+                    .and_then(|table| table.embeddings.get_mut(&metadata.target_column))
+            {
+                keys.iter().for_each(|key| {
+                    rows.get_mut(key)
+                        .map(|(_, processed)| *processed = false)
+                        .map(|_| ())
+                        .unwrap_or(())
+                });
+            }
+        }
+
+        DbIndex::UpdateItems { keys } => {
+            if let Some(rows) =
+                db.0.write()
+                    .unwrap()
+                    .keyspaces
+                    .get_mut(&metadata.keyspace_name)
+                    .and_then(|keyspace| keyspace.tables.get_mut(&metadata.table_name))
+                    .and_then(|table| table.embeddings.get_mut(&metadata.target_column))
+            {
+                keys.iter().for_each(|key| {
+                    rows.get_mut(key)
+                        .map(|(_, processed)| *processed = true)
+                        .map(|_| ())
+                        .unwrap_or(())
+                });
+            }
+        }
+    }
+}
diff --git a/tests/integration/httpclient.rs b/tests/integration/httpclient.rs
new file mode 100644
index 00000000..0de284a8
--- /dev/null
+++ b/tests/integration/httpclient.rs
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use reqwest::Client;
+use std::net::SocketAddr;
+use vector_store::Distance;
+use vector_store::Embeddings;
+use vector_store::IndexId;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::Limit;
+use vector_store::httproutes::PostIndexAnnRequest;
+use vector_store::httproutes::PostIndexAnnResponse;
+
+pub(crate) struct HttpClient {
+    client: Client,
+    url_api: String,
+}
+
+impl HttpClient {
+    pub(crate) fn new(addr: SocketAddr) -> Self {
+        Self {
+            url_api: format!("http://{addr}/api/v1"),
+            client: Client::new(),
+        }
+    }
+
+    pub(crate) async fn indexes(&self) -> Vec<IndexId> {
+        self.client
+            .get(format!("{}/indexes", self.url_api))
+            .send()
+            .await
+            .unwrap()
+            .json()
+            .await
+            .unwrap()
+    }
+
+    pub(crate) async fn ann(
+        &self,
+        index: &IndexMetadata,
+        embeddings: Embeddings,
+        limit: Limit,
+    ) -> (Vec<Key>, Vec<Distance>) {
+        let resp = self
+            .client
+            .post(format!(
+                "{}/indexes/{}/{}/ann",
+                self.url_api, index.keyspace_name, index.index_name
+            ))
+            .json(&PostIndexAnnRequest { embeddings, limit })
+            .send()
+            .await
+            .unwrap()
+            .json::<PostIndexAnnResponse>()
+            .await
+            .unwrap();
+        (resp.keys, resp.distances)
+    }
+}
diff --git a/tests/integration/main.rs b/tests/integration/main.rs
new file mode 100644
index 00000000..ff2c5868
--- /dev/null
+++ b/tests/integration/main.rs
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+mod db_basic;
+mod httpclient;
+mod usearch;
+
+use tracing_subscriber::EnvFilter;
+use tracing_subscriber::fmt;
+use tracing_subscriber::prelude::*;
+
+fn enable_tracing() {
+    tracing_subscriber::registry()
+        .with(EnvFilter::try_new("info").unwrap())
+        .with(fmt::layer().with_target(false))
+        .init();
+}
diff --git a/tests/integration/usearch.rs b/tests/integration/usearch.rs
new file mode 100644
index 00000000..063a0824
--- /dev/null
+++ b/tests/integration/usearch.rs
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::task;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: Default::default(),
+        expansion_add: Default::default(),
+        expansion_search: Default::default(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_column: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            task::yield_now().await;
+        }
+    })
+    .await
+    .unwrap();
+
+    let indexes = client.indexes().await;
+    assert_eq!(indexes.len(), 1);
+    assert_eq!(indexes.first().unwrap().as_ref(), "vector.ann");
+
+    let (keys, distances) = client
+        .ann(
+            &index,
+            vec![2.1, -2., 2.].into(),
+            NonZeroUsize::new(1).unwrap().into(),
+        )
+        .await;
+    assert_eq!(keys.len(), 1);
+    assert_eq!(distances.len(), keys.len());
+    assert_eq!(keys.first().unwrap().as_ref(), &2u64);
+
+    db.del_index(&index.keyspace_name, &index.index_name)
+        .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while !client.indexes().await.is_empty() {
+            task::yield_now().await;
+        }
+    })
+    .await
+    .unwrap();
+}


Reviews:
user: login: swasik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T11:46:14Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T12:47:13Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T12:47:32Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T12:47:40Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T12:47:49Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T12:48:35Z
user: login: swasik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T08:26:46Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:30:26Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:33:15Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:54:02Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:54:08Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:54:14Z
user: login: ewienik
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:54:22Z
user: login: swasik
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-15T09:55:52Z

Review comments:
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
path: tests/integration/usearch.rs
user: login: swasik
body: Does 0 means default here?
created_at: 2025-04-14T11:18:06Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
path: tests/integration/usearch.rs
user: login: swasik
body: Even if we need just single sleep it will already add 100 Ms which can be much when executing test multiple times. Could you make it e.g 5 Ms?
created_at: 2025-04-14T11:27:52Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    })
+    .await
+    .unwrap();
+
+    assert_eq!(
+        client.indexes().await.first().unwrap().as_ref(),
path: tests/integration/usearch.rs
user: login: swasik
body: What about the number of indexes? We shouldn't create any extra.
created_at: 2025-04-14T11:30:13Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    })
+    .await
+    .unwrap();
+
+    assert_eq!(
+        client.indexes().await.first().unwrap().as_ref(),
+        "vector.ann"
+    );
+    let (keys, distances) = client
+        .ann(
+            &index,
+            vec![2.1, -2., 2.].into(),
+            NonZeroUsize::new(1).unwrap().into(),
+        )
+        .await;
+    assert_eq!(keys.len(), 1);
+    assert_eq!(distances.len(), keys.len());
+    assert_eq!(keys.first().unwrap().as_ref(), &2u64);
+
+    db.del_index(&index.keyspace_name, &index.index_name)
+        .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while !client.indexes().await.is_empty() {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
path: tests/integration/usearch.rs
user: login: swasik
body: Maybe let's add constant for default timeouts and sleep times.
created_at: 2025-04-14T11:41:58Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    })
+    .await
+    .unwrap();
+
+    assert_eq!(
+        client.indexes().await.first().unwrap().as_ref(),
+        "vector.ann"
+    );
+    let (keys, distances) = client
+        .ann(
+            &index,
+            vec![2.1, -2., 2.].into(),
+            NonZeroUsize::new(1).unwrap().into(),
+        )
+        .await;
+    assert_eq!(keys.len(), 1);
+    assert_eq!(distances.len(), keys.len());
+    assert_eq!(keys.first().unwrap().as_ref(), &2u64);
+
+    db.del_index(&index.keyspace_name, &index.index_name)
+        .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while !client.indexes().await.is_empty() {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    })
+    .await
+    .unwrap();
+
+    db.add_index(
path: tests/integration/usearch.rs
user: login: swasik
body: And why do you add the index once again? It is different from what test's name says: simple create, search, delete.
created_at: 2025-04-14T11:45:04Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
path: tests/integration/usearch.rs
user: login: ewienik
body: Fixed in v2 by implementing `Default` trait.
created_at: 2025-04-14T12:47:13Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
path: tests/integration/usearch.rs
user: login: ewienik
body: Fixed in v2 by using `yield_now()`
created_at: 2025-04-14T12:47:31Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    })
+    .await
+    .unwrap();
+
+    assert_eq!(
+        client.indexes().await.first().unwrap().as_ref(),
+        "vector.ann"
+    );
+    let (keys, distances) = client
+        .ann(
+            &index,
+            vec![2.1, -2., 2.].into(),
+            NonZeroUsize::new(1).unwrap().into(),
+        )
+        .await;
+    assert_eq!(keys.len(), 1);
+    assert_eq!(distances.len(), keys.len());
+    assert_eq!(keys.first().unwrap().as_ref(), &2u64);
+
+    db.del_index(&index.keyspace_name, &index.index_name)
+        .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while !client.indexes().await.is_empty() {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
path: tests/integration/usearch.rs
user: login: ewienik
body: Fixed in v2 by using `yield_now()`
created_at: 2025-04-14T12:47:40Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    })
+    .await
+    .unwrap();
+
+    assert_eq!(
+        client.indexes().await.first().unwrap().as_ref(),
+        "vector.ann"
+    );
+    let (keys, distances) = client
+        .ann(
+            &index,
+            vec![2.1, -2., 2.].into(),
+            NonZeroUsize::new(1).unwrap().into(),
+        )
+        .await;
+    assert_eq!(keys.len(), 1);
+    assert_eq!(distances.len(), keys.len());
+    assert_eq!(keys.first().unwrap().as_ref(), &2u64);
+
+    db.del_index(&index.keyspace_name, &index.index_name)
+        .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while !client.indexes().await.is_empty() {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    })
+    .await
+    .unwrap();
+
+    db.add_index(
path: tests/integration/usearch.rs
user: login: ewienik
body: Removed in v2
created_at: 2025-04-14T12:47:49Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,147 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: 0.into(),
+        expansion_add: 0.into(),
+        expansion_search: 0.into(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
+            connectivity: index.connectivity,
+            expansion_add: index.expansion_add,
+            expansion_search: index.expansion_search,
+        },
+    )
+    .unwrap();
+    db.insert_values(
+        &index.keyspace_name,
+        &index.table_name,
+        &index.target_column,
+        vec![
+            (1.into(), vec![1., 1., 1.].into()),
+            (2.into(), vec![2., -2., 2.].into()),
+            (3.into(), vec![3., 3., 3.].into()),
+        ],
+    )
+    .unwrap();
+
+    time::timeout(Duration::from_secs(10), async {
+        while db
+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
+            .unwrap()
+            .as_ref()
+            != &3
+        {
+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
+        }
+    })
+    .await
+    .unwrap();
+
+    assert_eq!(
+        client.indexes().await.first().unwrap().as_ref(),
path: tests/integration/usearch.rs
user: login: ewienik
body: Fixed in v2 by added check of returned indexed vector length.
created_at: 2025-04-14T12:48:35Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
path: tests/integration/db_basic.rs
user: login: swasik
body: nit: Is it on purpose that you first define high level structs (TableStore) and lower level below (Table)? For me, it is rather more difficult to read. However, on the other hand, you rarely read the whole file from top to the bottom.
created_at: 2025-04-14T15:51:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
path: tests/integration/db_basic.rs
user: login: swasik
body: does not exist
created_at: 2025-04-15T06:45:09Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(table) = keyspace.tables.get(&index.table_name) else {
+            bail!("a table {} not exista", index.table_name);
path: tests/integration/db_basic.rs
user: login: swasik
body: does not exist
created_at: 2025-04-15T06:45:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(table) = keyspace.tables.get(&index.table_name) else {
+            bail!("a table {} not exista", index.table_name);
+        };
+        if !table.embeddings.contains_key(&index.target_name) {
+            bail!(
+                "a table {} not contains a target column {}",
path: tests/integration/db_basic.rs
user: login: swasik
body: does not contain
created_at: 2025-04-15T06:45:57Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
path: tests/integration/db_basic.rs
user: login: swasik
body: Here and below
created_at: 2025-04-15T06:46:25Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(table) = keyspace.tables.get(&index.table_name) else {
+            bail!("a table {} not exista", index.table_name);
+        };
+        if !table.embeddings.contains_key(&index.target_name) {
+            bail!(
+                "a table {} not contains a target column {}",
+                index.table_name,
+                index.target_name
+            );
+        }
+        if keyspace.indexes.contains_key(&index_name) {
+            bail!("an index {index_name} already exists");
+        }
+        keyspace.indexes.insert(index_name, IndexStore::new(index));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn del_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: &TableName,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        if keyspace.indexes.remove(index_name).is_none() {
+            bail!("an index {index_name} not exists");
+        }
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn insert_values(
+        &self,
+        keyspace_name: &KeyspaceName,
+        table_name: &TableName,
+        target_name: &ColumnName,
+        values: impl IntoIterator<Item = (Key, Embeddings)>,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(table) = keyspace.tables.get_mut(table_name) else {
+            bail!("a table {table_name} not exists");
+        };
+        let Some(column) = table.embeddings.get_mut(target_name) else {
+            bail!("a column {target_name} not exists in a table {table_name}");
+        };
+
+        values.into_iter().for_each(|(key, embeddings)| {
+            column.insert(key, (embeddings, false));
+        });
+
+        Ok(())
+    }
+
+    pub(crate) fn get_indexed_elements_count(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: &TableName,
+    ) -> anyhow::Result<IndexItemsCount> {
+        let db = self.0.read().unwrap();
+        let Some(keyspace) = db.keyspaces.get(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(index) = keyspace.indexes.get(index_name) else {
+            bail!("an index {index_name} not exists");
+        };
path: tests/integration/db_basic.rs
user: login: swasik
body: This error handling repeats so many times. Couldn't we define some getters for it, e.g. get_column, get_index?
created_at: 2025-04-15T06:49:53Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,118 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::task;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: Default::default(),
+        expansion_add: Default::default(),
+        expansion_search: Default::default(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
path: tests/integration/usearch.rs
user: login: swasik
body: Maybe target_column for consistency with the previous change?
created_at: 2025-04-15T08:26:40Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
path: tests/integration/db_basic.rs
user: login: ewienik
body: As this is a style preference which is hard to define or reasoning (both ways bottom-top and top-bottom seems enough good), I am not changing it. 
created_at: 2025-04-15T09:30:26Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(table) = keyspace.tables.get(&index.table_name) else {
+            bail!("a table {} not exista", index.table_name);
+        };
+        if !table.embeddings.contains_key(&index.target_name) {
+            bail!(
+                "a table {} not contains a target column {}",
+                index.table_name,
+                index.target_name
+            );
+        }
+        if keyspace.indexes.contains_key(&index_name) {
+            bail!("an index {index_name} already exists");
+        }
+        keyspace.indexes.insert(index_name, IndexStore::new(index));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn del_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: &TableName,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        if keyspace.indexes.remove(index_name).is_none() {
+            bail!("an index {index_name} not exists");
+        }
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn insert_values(
+        &self,
+        keyspace_name: &KeyspaceName,
+        table_name: &TableName,
+        target_name: &ColumnName,
+        values: impl IntoIterator<Item = (Key, Embeddings)>,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(table) = keyspace.tables.get_mut(table_name) else {
+            bail!("a table {table_name} not exists");
+        };
+        let Some(column) = table.embeddings.get_mut(target_name) else {
+            bail!("a column {target_name} not exists in a table {table_name}");
+        };
+
+        values.into_iter().for_each(|(key, embeddings)| {
+            column.insert(key, (embeddings, false));
+        });
+
+        Ok(())
+    }
+
+    pub(crate) fn get_indexed_elements_count(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: &TableName,
+    ) -> anyhow::Result<IndexItemsCount> {
+        let db = self.0.read().unwrap();
+        let Some(keyspace) = db.keyspaces.get(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(index) = keyspace.indexes.get(index_name) else {
+            bail!("an index {index_name} not exists");
+        };
path: tests/integration/db_basic.rs
user: login: ewienik
body: Similar error for an index is used in three places - inserting, removing or checking - in different functions. It seems that these functions are correct abstractions. 
created_at: 2025-04-15T09:33:15Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
path: tests/integration/db_basic.rs
user: login: ewienik
body: fixed in 317c16c
created_at: 2025-04-15T09:54:02Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(table) = keyspace.tables.get(&index.table_name) else {
+            bail!("a table {} not exista", index.table_name);
path: tests/integration/db_basic.rs
user: login: ewienik
body: fixed in 317c16c
created_at: 2025-04-15T09:54:08Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,440 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use anyhow::anyhow;
+use anyhow::bail;
+use futures::StreamExt;
+use futures::stream;
+use itertools::Itertools;
+use scylla::value::CqlTimeuuid;
+use std::collections::HashMap;
+use std::sync::Arc;
+use std::sync::RwLock;
+use tokio::sync::mpsc;
+use uuid::Uuid;
+use vector_store::ColumnName;
+use vector_store::Connectivity;
+use vector_store::DbCustomIndex;
+use vector_store::Dimensions;
+use vector_store::Embeddings;
+use vector_store::ExpansionAdd;
+use vector_store::ExpansionSearch;
+use vector_store::IndexItemsCount;
+use vector_store::IndexMetadata;
+use vector_store::Key;
+use vector_store::KeyspaceName;
+use vector_store::TableName;
+use vector_store::db::Db;
+use vector_store::db_index::DbIndex;
+
+#[derive(Clone)]
+pub(crate) struct DbBasic(Arc<RwLock<DbMock>>);
+
+pub(crate) fn new() -> (mpsc::Sender<Db>, DbBasic) {
+    let (tx, mut rx) = mpsc::channel(10);
+    let db = DbBasic::new();
+    tokio::spawn({
+        let db = db.clone();
+        async move {
+            while let Some(msg) = rx.recv().await {
+                process_db(&db, msg);
+            }
+        }
+    });
+    (tx, db)
+}
+
+struct TableStore {
+    table: Table,
+    embeddings: HashMap<ColumnName, HashMap<Key, (Embeddings, bool)>>,
+}
+
+impl TableStore {
+    fn new(table: Table) -> Self {
+        Self {
+            embeddings: table
+                .dimensions
+                .keys()
+                .map(|key| (key.clone(), HashMap::new()))
+                .collect(),
+            table,
+        }
+    }
+}
+
+pub(crate) struct Table {
+    pub(crate) dimensions: HashMap<ColumnName, Dimensions>,
+}
+
+#[derive(Debug)]
+struct IndexStore {
+    index: Index,
+    version: Uuid,
+    elements_count: IndexItemsCount,
+}
+
+impl IndexStore {
+    fn new(index: Index) -> Self {
+        Self {
+            version: Uuid::new_v4(),
+            elements_count: 0.into(),
+            index,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Index {
+    pub(crate) table_name: TableName,
+    pub(crate) target_name: ColumnName,
+    pub(crate) connectivity: Connectivity,
+    pub(crate) expansion_add: ExpansionAdd,
+    pub(crate) expansion_search: ExpansionSearch,
+}
+
+struct Keyspace {
+    tables: HashMap<TableName, TableStore>,
+    indexes: HashMap<TableName, IndexStore>,
+}
+
+impl Keyspace {
+    fn new() -> Self {
+        Self {
+            tables: HashMap::new(),
+            indexes: HashMap::new(),
+        }
+    }
+}
+
+struct DbMock {
+    schema_version: CqlTimeuuid,
+    keyspaces: HashMap<KeyspaceName, Keyspace>,
+}
+
+impl DbMock {
+    fn create_new_schema_version(&mut self) {
+        self.schema_version = Uuid::new_v4().into();
+    }
+}
+
+impl DbBasic {
+    pub(crate) fn new() -> Self {
+        Self(Arc::new(RwLock::new(DbMock {
+            schema_version: CqlTimeuuid::from(Uuid::new_v4()),
+            keyspaces: HashMap::new(),
+        })))
+    }
+
+    pub(crate) fn add_table(
+        &self,
+        keyspace_name: KeyspaceName,
+        table_name: TableName,
+        table: Table,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let keyspace = db
+            .keyspaces
+            .entry(keyspace_name)
+            .or_insert_with(Keyspace::new);
+        if keyspace.tables.contains_key(&table_name) {
+            bail!("a table {table_name} already exists in a keyspace");
+        }
+        keyspace.tables.insert(table_name, TableStore::new(table));
+
+        db.create_new_schema_version();
+        Ok(())
+    }
+
+    pub(crate) fn add_index(
+        &self,
+        keyspace_name: &KeyspaceName,
+        index_name: TableName,
+        index: Index,
+    ) -> anyhow::Result<()> {
+        let mut db = self.0.write().unwrap();
+
+        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
+            bail!("a keyspace {keyspace_name} not exists");
+        };
+        let Some(table) = keyspace.tables.get(&index.table_name) else {
+            bail!("a table {} not exista", index.table_name);
+        };
+        if !table.embeddings.contains_key(&index.target_name) {
+            bail!(
+                "a table {} not contains a target column {}",
path: tests/integration/db_basic.rs
user: login: ewienik
body: fixed in 317c16c
created_at: 2025-04-15T09:54:14Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,118 @@
+/*
+ * Copyright 2025-present ScyllaDB
+ * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
+ */
+
+use crate::db_basic;
+use crate::db_basic::Index;
+use crate::db_basic::Table;
+use crate::httpclient::HttpClient;
+use std::net::SocketAddr;
+use std::num::NonZeroUsize;
+use std::time::Duration;
+use tokio::task;
+use tokio::time;
+use uuid::Uuid;
+use vector_store::IndexMetadata;
+
+#[tokio::test]
+async fn simple_create_search_delete_index() {
+    crate::enable_tracing();
+
+    let (db_actor, db) = db_basic::new();
+
+    let index = IndexMetadata {
+        keyspace_name: "vector".to_string().into(),
+        table_name: "items".to_string().into(),
+        index_name: "ann".to_string().into(),
+        key_name: "id".to_string().into(),
+        target_column: "embeddings".to_string().into(),
+        dimensions: NonZeroUsize::new(3).unwrap().into(),
+        connectivity: Default::default(),
+        expansion_add: Default::default(),
+        expansion_search: Default::default(),
+        version: Uuid::new_v4().into(),
+    };
+
+    let (_server_actor, addr) = vector_store::run(
+        SocketAddr::from(([127, 0, 0, 1], 0)).into(),
+        Some(1),
+        db_actor,
+    )
+    .await
+    .unwrap();
+    let client = HttpClient::new(addr);
+
+    db.add_table(
+        index.keyspace_name.clone(),
+        index.table_name.clone(),
+        Table {
+            dimensions: [(index.target_column.clone(), index.dimensions)]
+                .into_iter()
+                .collect(),
+        },
+    )
+    .unwrap();
+    db.add_index(
+        &index.keyspace_name,
+        index.index_name.clone(),
+        Index {
+            table_name: index.table_name.clone(),
+            target_name: index.target_column.clone(),
path: tests/integration/usearch.rs
user: login: ewienik
body: fixed in 317c16c
created_at: 2025-04-15T09:54:22Z
author_association: COLLABORATOR

PR comments:
user: login: ewienik
created_at: 2025-04-14T12:46:47Z
body: Changelog 45173afb9b9ed02f9912ce69c767ec266476fd10
- Implement `Default` for `Connectivity`, `ExpansionAdd`, `ExpansionSearch`
- Use `yield_now()` instead of `sleep()`
- Remove adding a second index from a simple test

<details>

<summary>Range diff</summary>

```
1:  97cbdaa ! 1:  45173af Implement simple create/search/delete integration test
    @@ Cargo.toml: utoipa = { version = "5.3.1", features = ["axum_extras"] }
     +[dev-dependencies]
     +reqwest = { version = "0.12.15", features = ["json"] }
     
    + ## src/lib.rs ##
    +@@ src/lib.rs: pub struct Dimensions(NonZeroUsize);
    +     Copy,
    +     Clone,
    +     Debug,
    ++    Default,
    +     PartialEq,
    +     Eq,
    +     Hash,
    +@@ src/lib.rs: pub struct Connectivity(usize);
    +     Copy,
    +     Clone,
    +     Debug,
    ++    Default,
    +     PartialEq,
    +     Eq,
    +     Hash,
    +@@ src/lib.rs: pub struct ExpansionAdd(usize);
    +     Copy,
    +     Clone,
    +     Debug,
    ++    Default,
    +     PartialEq,
    +     Eq,
    +     Hash,
    +
      ## tests/integration/db_basic.rs (new) ##
     @@
     +/*
    @@ tests/integration/usearch.rs (new)
     +use std::net::SocketAddr;
     +use std::num::NonZeroUsize;
     +use std::time::Duration;
    ++use tokio::task;
     +use tokio::time;
     +use uuid::Uuid;
     +use vector_store::IndexMetadata;
    @@ tests/integration/usearch.rs (new)
     +        key_name: "id".to_string().into(),
     +        target_column: "embeddings".to_string().into(),
     +        dimensions: NonZeroUsize::new(3).unwrap().into(),
    -+        connectivity: 0.into(),
    -+        expansion_add: 0.into(),
    -+        expansion_search: 0.into(),
    ++        connectivity: Default::default(),
    ++        expansion_add: Default::default(),
    ++        expansion_search: Default::default(),
     +        version: Uuid::new_v4().into(),
     +    };
     +
    @@ tests/integration/usearch.rs (new)
     +            .as_ref()
     +            != &3
     +        {
    -+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
    ++            task::yield_now().await;
     +        }
     +    })
     +    .await
     +    .unwrap();
     +
    -+    assert_eq!(
    -+        client.indexes().await.first().unwrap().as_ref(),
    -+        "vector.ann"
    -+    );
    ++    let indexes = client.indexes().await;
    ++    assert_eq!(indexes.len(), 1);
    ++    assert_eq!(indexes.first().unwrap().as_ref(), "vector.ann");
    ++
     +    let (keys, distances) = client
     +        .ann(
     +            &index,
    @@ tests/integration/usearch.rs (new)
     +
     +    time::timeout(Duration::from_secs(10), async {
     +        while !client.indexes().await.is_empty() {
    -+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
    -+        }
    -+    })
    -+    .await
    -+    .unwrap();
    -+
    -+    db.add_index(
    -+        &index.keyspace_name,
    -+        index.index_name.clone(),
    -+        Index {
    -+            table_name: index.table_name.clone(),
    -+            target_name: index.target_column.clone(),
    -+            connectivity: index.connectivity,
    -+            expansion_add: index.expansion_add,
    -+            expansion_search: index.expansion_search,
    -+        },
    -+    )
    -+    .unwrap();
    -+
    -+    time::timeout(Duration::from_secs(10), async {
    -+        while db
    -+            .get_indexed_elements_count(&index.keyspace_name, &index.index_name)
    -+            .unwrap()
    -+            .as_ref()
    -+            != &3
    -+        {
    -+            tokio::time::sleep(std::time::Duration::from_millis(100)).await;
    ++            task::yield_now().await;
     +        }
     +    })
     +    .await
     +    .unwrap();
    -+    assert_eq!(
    -+        client.indexes().await.first().unwrap().as_ref(),
    -+        "vector.ann"
    -+    );
     +}
```
author_association: COLLABORATOR
user: login: ewienik
created_at: 2025-04-15T09:52:23Z
body: Changelog for 317c16ce18d13699d769166cb44bf24291f34fc0

- fixed error messages
- rename `target_name` into `column_name`

<details>

<summary>range diff</summary>

```
1:  45173af ! 1:  317c16c Implement simple create/search/delete integration test
    @@ tests/integration/db_basic.rs (new)
     +#[derive(Debug)]
     +pub(crate) struct Index {
     +    pub(crate) table_name: TableName,
    -+    pub(crate) target_name: ColumnName,
    ++    pub(crate) target_column: ColumnName,
     +    pub(crate) connectivity: Connectivity,
     +    pub(crate) expansion_add: ExpansionAdd,
     +    pub(crate) expansion_search: ExpansionSearch,
    @@ tests/integration/db_basic.rs (new)
     +        let mut db = self.0.write().unwrap();
     +
     +        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
    -+            bail!("a keyspace {keyspace_name} not exists");
    ++            bail!("a keyspace {keyspace_name} does not exist");
     +        };
     +        let Some(table) = keyspace.tables.get(&index.table_name) else {
    -+            bail!("a table {} not exista", index.table_name);
    ++            bail!("a table {} does not exist", index.table_name);
     +        };
    -+        if !table.embeddings.contains_key(&index.target_name) {
    ++        if !table.embeddings.contains_key(&index.target_column) {
     +            bail!(
    -+                "a table {} not contains a target column {}",
    ++                "a table {} does not contain a target column {}",
     +                index.table_name,
    -+                index.target_name
    ++                index.target_column
     +            );
     +        }
     +        if keyspace.indexes.contains_key(&index_name) {
    @@ tests/integration/db_basic.rs (new)
     +        let mut db = self.0.write().unwrap();
     +
     +        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
    -+            bail!("a keyspace {keyspace_name} not exists");
    ++            bail!("a keyspace {keyspace_name} does not exist");
     +        };
     +        if keyspace.indexes.remove(index_name).is_none() {
    -+            bail!("an index {index_name} not exists");
    ++            bail!("an index {index_name} does not exist");
     +        }
     +
     +        db.create_new_schema_version();
    @@ tests/integration/db_basic.rs (new)
     +        &self,
     +        keyspace_name: &KeyspaceName,
     +        table_name: &TableName,
    -+        target_name: &ColumnName,
    ++        target_column: &ColumnName,
     +        values: impl IntoIterator<Item = (Key, Embeddings)>,
     +    ) -> anyhow::Result<()> {
     +        let mut db = self.0.write().unwrap();
     +
     +        let Some(keyspace) = db.keyspaces.get_mut(keyspace_name) else {
    -+            bail!("a keyspace {keyspace_name} not exists");
    ++            bail!("a keyspace {keyspace_name} does not exist");
     +        };
     +        let Some(table) = keyspace.tables.get_mut(table_name) else {
    -+            bail!("a table {table_name} not exists");
    ++            bail!("a table {table_name} does not exist");
     +        };
    -+        let Some(column) = table.embeddings.get_mut(target_name) else {
    -+            bail!("a column {target_name} not exists in a table {table_name}");
    ++        let Some(column) = table.embeddings.get_mut(target_column) else {
    ++            bail!("a column {target_column} does not exist in a table {table_name}");
     +        };
     +
     +        values.into_iter().for_each(|(key, embeddings)| {
    @@ tests/integration/db_basic.rs (new)
     +    ) -> anyhow::Result<IndexItemsCount> {
     +        let db = self.0.read().unwrap();
     +        let Some(keyspace) = db.keyspaces.get(keyspace_name) else {
    -+            bail!("a keyspace {keyspace_name} not exists");
    ++            bail!("a keyspace {keyspace_name} does not exist");
     +        };
     +        let Some(index) = keyspace.indexes.get(index_name) else {
    -+            bail!("an index {index_name} not exists");
    ++            bail!("an index {index_name} does not exist");
     +        };
     +        Ok(index.elements_count)
     +    }
    @@ tests/integration/db_basic.rs (new)
     +                            keyspace: keyspace_name.clone(),
     +                            index: index_name.clone(),
     +                            table: index.index.table_name.clone(),
    -+                            target_column: index.index.target_name.clone(),
    ++                            target_column: index.index.target_column.clone(),
     +                        })
     +                })
     +                .collect()))
    @@ tests/integration/usearch.rs (new)
     +        index.index_name.clone(),
     +        Index {
     +            table_name: index.table_name.clone(),
    -+            target_name: index.target_column.clone(),
    ++            target_column: index.target_column.clone(),
     +            connectivity: index.connectivity,
     +            expansion_add: index.expansion_add,
     +            expansion_search: index.expansion_search,
```
author_association: COLLABORATOR