{"id":50379323,"url":"https://github.com/jamesgober/iqdb","last_synced_at":"2026-06-10T03:01:04.811Z","repository":{"id":361342480,"uuid":"1253972707","full_name":"jamesgober/iQDB","owner":"jamesgober","description":"High-performance embedded vector database for Rust. HNSW, IVF, and flat indexes with first-class metadata filtering. In-process, persistent, zero-network similarity search.","archived":false,"fork":false,"pushed_at":"2026-05-30T09:43:45.000Z","size":109,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-30T11:05:52.742Z","etag":null,"topics":["ai","database","embedded-database","hnsw","in-process","ivf","persistent","reps","rust","vector-database"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamesgober.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-30T02:05:51.000Z","updated_at":"2026-05-30T09:09:05.000Z","dependencies_parsed_at":null,"dependency_job_id":"adc8c3b7-db4e-415e-a513-d7b1802263d0","html_url":"https://github.com/jamesgober/iQDB","commit_stats":null,"previous_names":["jamesgober/iqdb"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/jamesgober/iQDB","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2FiQDB","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2FiQDB/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2FiQDB/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2FiQDB/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamesgober","download_url":"https://codeload.github.com/jamesgober/iQDB/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamesgober%2FiQDB/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34134633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","database","embedded-database","hnsw","in-process","ivf","persistent","reps","rust","vector-database"],"created_at":"2026-05-30T11:02:03.388Z","updated_at":"2026-06-10T03:01:04.805Z","avatar_url":"https://github.com/jamesgober.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n    \u003cimg width=\"99\" alt=\"Rust logo\" src=\"https://raw.githubusercontent.com/jamesgober/rust-collection/72baabd71f00e14aa9184efcb16fa3deddda3a0a/assets/rust-logo.svg\"\u003e\n    \u003cbr\u003e\n    \u003cstrong\u003eiQDB\u003c/strong\u003e\n    \u003cbr\u003e\n    \u003csup\u003e\u003csub\u003eEMBEDDED VECTOR DATABASE FOR RUST\u003c/sub\u003e\u003c/sup\u003e\n\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://crates.io/crates/iqdb\"\u003e\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/v/iqdb.svg\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://crates.io/crates/iqdb\" alt=\"Download iqdb\"\u003e\u003cimg alt=\"Crates.io Downloads\" src=\"https://img.shields.io/crates/d/iqdb?color=%230099ff\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://docs.rs/iqdb\"\u003e\u003cimg alt=\"docs.rs\" src=\"https://docs.rs/iqdb/badge.svg\"\u003e\u003c/a\u003e\n    \u003cimg alt=\"MSRV\" src=\"https://img.shields.io/badge/MSRV-1.87%2B-blue.svg?style=flat-square\" title=\"Rust Version\"\u003e\n    \u003ca href=\"https://github.com/jamesgober/iqdb/actions\"\u003e\u003cimg alt=\"CI\" src=\"https://github.com/jamesgober/iqdb/actions/workflows/ci.yml/badge.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    FASTER - LIGHTER - SMARTER \n    \u003cbr\u003e\n    Intelligence-grade vector storage for AI-native applications.\n    \u003cbr\u003e\n    Sub-millisecond vector search. Zero network hops. - i\u003cb\u003eQD\u003c/b\u003eB\n\u003c/p\u003e\n\n\u003cbr\u003e\n\n\u003cdiv align=\"left\"\u003e\n    \u003cp\u003e\n        \u003cstrong\u003eiQDB\u003c/strong\u003e is an \u003cb\u003eembedded vector database\u003c/b\u003e for Rust, a single-process, in-application similarity-search engine designed for \u003cem\u003ehigh-dimensional\u003c/em\u003e workloads where every microsecond on the query path matters.\n        It targets the same operational shape as \u003ccode\u003esqlite\u003c/code\u003e or \u003ccode\u003eredb\u003c/code\u003e: no daemon, no network hop, no separate runtime. Open a handle, write vectors, query nearest neighbours — all from inside your binary.\n    \u003c/p\u003e\n    \u003cp\u003e\n        The engine is built against a \u003cb\u003elock-free hot path\u003c/b\u003e, \u003cb\u003eallocation-free steady state\u003c/b\u003e, and a \u003cb\u003ecache-aware\u003c/b\u003e on-disk layout. Exact brute-force search, approximate-nearest-neighbour indices (HNSW and IVF), payload metadata, declarative filters, and durable write-ahead-logged storage are all in scope. Indices and storage are pluggable, so workloads can trade recall for latency without rewriting the surrounding application.\n    \u003c/p\u003e\n    \u003cp\u003e\n        Built for \u003cb\u003ecross-platform\u003c/b\u003e deployment from day one — Linux, macOS, and Windows are first-class targets, with the strongest power-loss sync each platform offers and atomic file replacement everywhere.\n    \u003c/p\u003e\n    \u003cbr\u003e\n    \u003chr\u003e\n    \u003cp\u003e\n        \u003cstrong\u003eMSRV is 1.87+.\u003c/strong\u003e The crate is dual-licensed under \u003ccode\u003eApache-2.0 OR MIT\u003c/code\u003e at your option.\n    \u003c/p\u003e\n    \u003cblockquote\u003e\n        \u003cstrong\u003e1.0.0 — stable.\u003c/strong\u003e The public API and on-disk format are frozen. iQDB composes the published iqdb crate family for vocabulary (\u003ccode\u003eiqdb-types\u003c/code\u003e), index seam (\u003ccode\u003eiqdb-index\u003c/code\u003e), exact and approximate indices (\u003ccode\u003eiqdb-flat\u003c/code\u003e, \u003ccode\u003eiqdb-hnsw\u003c/code\u003e, \u003ccode\u003eiqdb-ivf\u003c/code\u003e), durable storage (\u003ccode\u003eiqdb-persist\u003c/code\u003e), and optional result caching (\u003ccode\u003eiqdb-cache\u003c/code\u003e). A database fixes its dimensionality and distance metric at open time; the index is selectable — exact \u003ccode\u003eFlat\u003c/code\u003e by default, or approximate \u003ccode\u003eHnsw\u003c/code\u003e / \u003ccode\u003eIvf\u003c/code\u003e through \u003ccode\u003eIqdbConfig\u003c/code\u003e. An opt-in async surface (\u003ccode\u003eAsyncIqdb\u003c/code\u003e) is available behind the \u003ccode\u003easync\u003c/code\u003e feature. Migrating from 0.4.x? See the \u003ca href=\"./CHANGELOG.md#migration-from-04x--100\"\u003emigration guide\u003c/a\u003e. See \u003ca href=\"./CHANGELOG.md\"\u003e\u003ccode\u003eCHANGELOG.md\u003c/code\u003e\u003c/a\u003e for the full release history and \u003ca href=\"./docs/API.md\"\u003e\u003ccode\u003edocs/API.md\u003c/code\u003e\u003c/a\u003e for the API reference.\n    \u003c/blockquote\u003e\n\u003c/div\u003e\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Design Goals\n\niQDB is engineered against the \u003ca href=\"./REPS.md\"\u003eRust Efficiency \u0026amp; Performance Standards\u003c/a\u003e (REPS). Every architectural decision is graded against the same hard constraints:\n\n- **Embedded by default** — no daemon, no socket, no separate process. The library opens a path and gets out of the way.\n- **Sub-millisecond queries** — exact and approximate search paths are budgeted in microseconds, not milliseconds, with the hot path measured under Criterion every release.\n- **Enum-dispatched hot paths** — the index seam dispatches through a closed `match`, never `dyn`, so the query loop sees a concrete index with no virtual indirection.\n- **Allocation-aware steady state** — vector payloads are shared as `Arc\u003c[f32]\u003e` between the authoritative store and the derived index, so a vector that lives in both costs one allocation, not two.\n- **Pluggable indices** — flat, IVF, and HNSW share the `iqdb-index` trait surface so callers swap strategies through `IqdbConfig` without touching their query code. Flat is the exact recall ground truth the approximate indices are measured against.\n- **Crash-safe writes** — the durable path uses write-ahead logging and atomic snapshot replacement. A pulled power cord must never corrupt the database.\n- **Tier-1 cross-platform** — Linux (x86_64, aarch64), macOS (x86_64, Apple Silicon), Windows (x86_64) all compile and pass the full test suite on every commit.\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Status \u0026amp; Roadmap\n\niQDB ships milestone-by-milestone. Each tag below corresponds to a published release; everything above the current line is shipped, everything below is planned.\n\n| Milestone | Status | Surface delivered |\n|-----------|--------|-------------------|\n| `v0.1.0` — scaffolding | shipped | Crate scaffolding, lifecycle handle, `Error` type, CI matrix on all three Tier-1 platforms. |\n| `v0.2.0` — vector primitives | shipped | Validated vectors, distance metrics, typed payloads, in-memory store with thread-safe CRUD. |\n| `v0.3.0` — search | shipped | Flat top-`k` search, filters, batch variants, NaN-aware ranking, property-based tests. |\n| `v0.4.0` — durable storage | shipped | Directory-backed store, snapshot + WAL, cross-platform sync, atomic compaction, corrupt-tail recovery. |\n| `v0.5.0` — family composition + approximate indices | shipped | Re-platformed onto the iqdb crate family. Re-exported vocabulary (`Vector`, `VectorId`, `Metadata`, `Value`, `Hit`, `Filter`, `DistanceMetric`). Selectable index — exact `Flat`, plus `Hnsw` and `Ivf` through `IqdbConfig`. Durable storage via `iqdb-persist`; optional result cache via `iqdb-cache`. Recall validated against the flat oracle. |\n| `v0.6.0` — async surface | shipped | `async`-feature-gated `AsyncIqdb`: a Tokio adapter that offloads each blocking call via `spawn_blocking`. Additive; the synchronous API and default build are unchanged. |\n| `v0.7.0` — durability tuning (alpha) | shipped | `IqdbConfig::fsync` (WAL fsync cadence) and `IqdbConfig::compression` (snapshot `zstd` / `lz4`), wiring the compression features through. Additive; defaults unchanged. |\n| `v0.8.0` — decoder hardening (beta) | shipped | Bounded every on-disk-decoder allocation against hostile length fields; fuzz-style robustness tests for the frame decoder; verified `cargo deny` / `cargo audit` pass. No API change. |\n| `v0.9.0` — release candidate | shipped | Crash-recovery integration tests (corrupt WAL tail / corrupt snapshot); captured `criterion` benchmark baselines. No API change. |\n| `v1.0.0` — stable | **current** | Public API and on-disk format frozen. SemVer guarantees. `IndexKind` marked `#[non_exhaustive]`. Migration guide from 0.4.x. |\n\nThe per-release detail — what was added, what changed, and what was verified — lives in the [`CHANGELOG`](./CHANGELOG.md) and the per-version notes under [`docs/release/`](./docs/release/).\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Installation\n\nAdd to your `Cargo.toml`:\n\n```toml\n[dependencies]\niqdb = \"1\"\n```\n\nOptional features (all additive):\n\n```toml\n[dependencies]\niqdb = { version = \"1\", features = [\"serde\", \"parallel\", \"zstd\"] }\n```\n\niQDB compiles on stable Rust **1.87** and newer. It composes the published iqdb family crates (`iqdb-types`, `iqdb-index`, `iqdb-flat`, `iqdb-hnsw`, `iqdb-ivf`, `iqdb-build`, `iqdb-persist`, `iqdb-cache`, and their transitive dependencies).\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Quick Start\n\nA database fixes its dimensionality and distance metric at open time, then exposes a small surface: `upsert` / `get` / `delete` for records, `search` / `search_with` for queries.\n\n```rust\nuse iqdb::{DistanceMetric, Iqdb, Result, Vector, VectorId};\n\nfn main() -\u003e Result\u003c()\u003e {\n    // A 3-dimensional, in-memory database compared under cosine distance.\n    let db = Iqdb::open_in_memory(3, DistanceMetric::Cosine)?;\n\n    db.upsert(VectorId::from(1u64), Vector::new(vec![1.0, 0.0, 0.0])?, None)?;\n    db.upsert(VectorId::from(2u64), Vector::new(vec![0.99, 0.10, 0.0])?, None)?;\n\n    // Top-k similarity search. Results are sorted nearest-first under the\n    // smaller-is-closer rule; ties break on insertion order for determinism.\n    let hits = db.search(\u0026Vector::new(vec![1.0, 0.0, 0.0])?, 5)?;\n    assert_eq!(hits[0].id, VectorId::from(1u64));\n\n    db.close()\n}\n```\n\n### Filtered and batch search\n\nFilters are declarative [`Filter`] expressions evaluated against each record's [`Metadata`]. On the exact flat index the filter is applied before scoring, so the result is exact.\n\n```rust\nuse iqdb::{DistanceMetric, Filter, Iqdb, Metadata, Result, Value, Vector, VectorId};\n\nfn main() -\u003e Result\u003c()\u003e {\n    let db = Iqdb::open_in_memory(2, DistanceMetric::Cosine)?;\n\n    let doc: Metadata = [(\"kind\".to_string(), Value::String(\"doc\".into()))]\n        .into_iter().collect();\n    let img: Metadata = [(\"kind\".to_string(), Value::String(\"image\".into()))]\n        .into_iter().collect();\n    db.upsert(VectorId::from(1u64), Vector::new(vec![1.0, 0.0])?, Some(doc))?;\n    db.upsert(VectorId::from(2u64), Vector::new(vec![0.99, 0.10])?, Some(img))?;\n\n    // Only documents, ranked by cosine distance.\n    let filter = Filter::eq(\"kind\", Value::String(\"doc\".into()));\n    let hits = db.search_with(\u0026Vector::new(vec![1.0, 0.0])?, 5, filter)?;\n    assert_eq!(hits.len(), 1);\n    assert_eq!(hits[0].id, VectorId::from(1u64));\n\n    // Batch — one top-k result list per query, preserving input order.\n    let queries = vec![Vector::new(vec![1.0, 0.0])?, Vector::new(vec![0.0, 1.0])?];\n    let batches = db.search_batch(\u0026queries, 1)?;\n    assert_eq!(batches.len(), 2);\n\n    Ok(())\n}\n```\n\n### Choosing an index\n\nTier 1 (`open_in_memory` / `open`) defaults to the exact flat index. Tier 2 (`open_in_memory_with` / `open_with`) takes an [`IqdbConfig`] that selects an approximate index and tunes it, and can attach a result cache.\n\n```rust\nuse iqdb::{CacheConfig, DistanceMetric, HnswConfig, IndexKind, Iqdb, IqdbConfig, Result};\n\nfn main() -\u003e Result\u003c()\u003e {\n    let cfg = IqdbConfig::new(128, DistanceMetric::Cosine)\n        .index(IndexKind::Hnsw(HnswConfig::default().with_ef_search(96)))\n        .cache(CacheConfig::new().capacity(10_000));\n    let db = Iqdb::open_in_memory_with(cfg)?;\n    assert!(db.is_empty());\n    Ok(())\n}\n```\n\nOn the approximate indices the metadata filter is applied after graph / cluster traversal, so a highly selective filter can return fewer than `k` hits — widen the search (HNSW `filter_widen`, IVF `n_probes`) when that matters. IVF is trained lazily from the stored vectors on the first search; after many writes, `Iqdb::optimize` retrains its centroids.\n\n### Durable, file-backed storage\n\n`Iqdb::open(path, dim, metric)` opens or creates a durable database. The path is the snapshot file; a write-ahead log lives beside it. Acknowledged writes survive a crash; reopening replays the log onto the snapshot.\n\n```rust,no_run\nuse iqdb::{DistanceMetric, Iqdb, Result, Vector, VectorId};\n\nfn main() -\u003e Result\u003c()\u003e {\n    let db = Iqdb::open(\"./data/vectors.iqdb\", 3, DistanceMetric::Cosine)?;\n    db.upsert(VectorId::from(1u64), Vector::new(vec![0.1, 0.2, 0.3])?, None)?;\n    db.flush()?; // compact: fold the WAL into a fresh snapshot\n    db.close()\n}\n```\n\nA reopen whose requested `dim` / `metric` disagrees with the stored database fails with `Error::Config`. The stored index kind is part of the database identity and is restored from the snapshot regardless of the kind requested on reopen.\n\nBy default every acknowledged write is `fsync`ed and the snapshot is uncompressed. Trade durability for throughput, or shrink the snapshot, through `IqdbConfig`:\n\n```rust,no_run\nuse iqdb::{Compression, DistanceMetric, FsyncPolicy, Iqdb, IqdbConfig};\nuse std::time::Duration;\n\n# fn run() -\u003e iqdb::Result\u003c()\u003e {\nlet cfg = IqdbConfig::new(128, DistanceMetric::Cosine)\n    .fsync(FsyncPolicy::Periodic(Duration::from_millis(50))) // bound the un-synced window\n    .compression(Compression::Zstd { level: 3 });            // requires the `zstd` feature\nlet db = Iqdb::open_with(\"./data/vectors.iqdb\", cfg)?;\n# let _ = db;\n# Ok(())\n# }\n```\n\n### Async (the `async` feature)\n\nThe family is synchronous by design, so the async surface is a thin Tokio adapter: `AsyncIqdb` holds an `Arc\u003cIqdb\u003e` and runs each blocking call on Tokio's blocking pool via `spawn_blocking`, so awaiting a search or a write never stalls the executor. It is `Clone` + `Send` + `Sync`. Enable the `async` feature and bring your own runtime.\n\n```rust,ignore\nuse iqdb::{AsyncIqdb, DistanceMetric, Result, Vector, VectorId};\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c()\u003e {\n    let db = AsyncIqdb::open_in_memory(3, DistanceMetric::Cosine).await?;\n    db.upsert(VectorId::from(1u64), Vector::new(vec![1.0, 0.0, 0.0])?, None).await?;\n\n    let hits = db.search(Vector::new(vec![1.0, 0.0, 0.0])?, 1).await?;\n    assert_eq!(hits[0].id, VectorId::from(1u64));\n    db.close().await\n}\n```\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## API Overview\n\nThe full API reference lives at [`docs/API.md`](./docs/API.md); the rustdoc at [docs.rs/iqdb](https://docs.rs/iqdb) carries the same information in browseable form. The public surface:\n\n- [`Iqdb`](./src/handle.rs) — the database handle.\n  - `Iqdb::open_in_memory(dim, metric)` — an ephemeral, exact-flat database.\n  - `Iqdb::open_in_memory_with(config)` — an in-memory database from a full [`IqdbConfig`].\n  - `Iqdb::open(path, dim, metric)` / `Iqdb::open_with(path, config)` — a durable, file-backed database.\n  - `Iqdb::upsert(id, vector, metadata)` — insert or replace. Rejects a wrong-dimension vector at the boundary.\n  - `Iqdb::get(id)` — look up the stored vector and metadata. `Ok(None)` when absent.\n  - `Iqdb::delete(id)` — remove by id; returns whether it was present.\n  - `Iqdb::len()` / `Iqdb::is_empty()` — cardinality.\n  - `Iqdb::search(query, k)` — top-`k` similarity search under the database metric.\n  - `Iqdb::search_with(query, k, filter)` — top-`k` restricted by a metadata [`Filter`].\n  - `Iqdb::search_batch(...)` / `search_batch_with(...)` — order-preserving batch variants.\n  - `Iqdb::optimize()` — rebuild / retrain the approximate index over the current vectors.\n  - `Iqdb::cache_stats()` — cache hit/miss statistics, when a cache is configured.\n  - `Iqdb::flush()` — compact a file-backed store; no-op in memory.\n  - `Iqdb::close(self)` — final compaction, then release.\n- [`AsyncIqdb`](./src/async_db.rs) — *(`async` feature)* a Tokio adapter mirroring the `Iqdb` surface; offloads each blocking call via `spawn_blocking`. `Clone` + `Send` + `Sync`.\n- [`IqdbConfig`](./src/config.rs) — fluent construction config: `dim`, `metric`, an [`IndexKind`], and an optional [`CacheConfig`].\n- [`IndexKind`](./src/config.rs) — `Flat` (exact), `Hnsw(HnswConfig)`, `Ivf(IvfConfig)`.\n- [`HnswConfig`] / [`IvfConfig`] / [`CacheConfig`] — re-exported tuning structs for the approximate indices and the cache.\n- [`Vector`] / [`VectorId`] / [`Metadata`] / [`Value`] / [`Hit`] / [`Filter`] / [`DistanceMetric`] — the shared vocabulary, re-exported from `iqdb-types`.\n- [`Error`](./src/error.rs) / [`Result\u003cT\u003e`](./src/error.rs) — the unified error type (`#[non_exhaustive]`) and its `Result` alias.\n\n[`Filter`]: https://docs.rs/iqdb-types\n[`Metadata`]: https://docs.rs/iqdb-types\n[`Value`]: https://docs.rs/iqdb-types\n[`Vector`]: https://docs.rs/iqdb-types\n[`VectorId`]: https://docs.rs/iqdb-types\n[`Hit`]: https://docs.rs/iqdb-types\n[`DistanceMetric`]: https://docs.rs/iqdb-types\n[`HnswConfig`]: https://docs.rs/iqdb-hnsw\n[`IvfConfig`]: https://docs.rs/iqdb-ivf\n[`CacheConfig`]: https://docs.rs/iqdb-cache\n[`IqdbConfig`]: ./src/config.rs\n[`IndexKind`]: ./src/config.rs\n\n### Error variants\n\n| Variant | Meaning | Recovery |\n|---------|---------|----------|\n| `Error::Index(IqdbError)` | A failure from the index / vocabulary layer — dimension mismatch, absent id, invalid metric for the chosen index, malformed filter. | Inspect the wrapped [`iqdb_types::IqdbError`] kind and fix the construction or query site. |\n| `Error::Persist(PersistError)` | A failure from the durable-storage layer — snapshot / WAL I/O, a corrupt or truncated file, a checksum mismatch, or an unsupported compression feature. | Inspect the wrapped [`iqdb_persist::PersistError`]. A corrupt WAL tail is truncated automatically; a corrupt snapshot fails the open. |\n| `Error::Config(\u0026'static str)` | A handle-level consistency check failed — most often a reopen whose `dim` / `metric` does not match the stored database. | Open with the values the database was created with. |\n\nThe enum is `#[non_exhaustive]`; always include a `_` arm in a `match`.\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Examples\n\nSelf-contained examples live in [`examples/`](./examples). Run them with `cargo run --example \u003cname\u003e`.\n\n- **`basic`** — open, upsert, get, search, delete. [`examples/basic.rs`](./examples/basic.rs)\n- **`in_memory_store`** — metadata, replace-on-upsert, and a metadata-filtered search. [`examples/in_memory_store.rs`](./examples/in_memory_store.rs)\n- **`search`** — top-`k`, batch, and the effect of the distance metric. [`examples/search.rs`](./examples/search.rs)\n- **`persistence`** — three sessions against one durable file, showing data survives reopen. [`examples/persistence.rs`](./examples/persistence.rs)\n- **`index_selection`** — flat vs HNSW vs IVF through `IqdbConfig`, plus a cache and `optimize`. [`examples/index_selection.rs`](./examples/index_selection.rs)\n- **`async_search`** *(`async` feature)* — concurrent searches fanned out across Tokio tasks. [`examples/async_search.rs`](./examples/async_search.rs)\n\n```sh\ncargo run --example index_selection\ncargo run --example async_search --features async\n```\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Benchmarks\n\nA Criterion harness lives in [`benches/search.rs`](./benches/search.rs):\n\n- **`flat/search_dim64_n1000_k10`** / **`hnsw/search_dim64_n1000_k10`** — top-`k` query throughput on the exact and approximate paths over 1 000 vectors at dim 64.\n- **`flat/upsert_dim64`** — write throughput building a fresh database.\n\n```sh\ncargo bench --bench search\n```\n\nIndicative baselines on a developer machine (dim 64, 1 000 vectors): flat `search` ≈ 7.9 µs, HNSW `search` ≈ 35.8 µs, flat `upsert` of 1 000 vectors ≈ 185 µs. At this corpus size the exact flat scan beats HNSW's graph traversal — the approximate index earns its overhead at much larger scale.\n\nCriterion writes reports to `target/criterion/`. A regression beyond the REPS threshold (5% on a tracked metric) blocks a release.\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Testing\n\nEvery public path has happy / error / edge-case coverage:\n\n- Unit tests live in `#[cfg(test)] mod tests` blocks inside each source file.\n- Integration tests live in [`tests/`](./tests):\n  - [`tests/persistence.rs`](./tests/persistence.rs) — durable lifecycle: open / upsert / close / reopen, delete and metadata persistence, WAL replay without close, dim/metric-mismatch rejection, IVF round-trip, multi-session accumulation.\n  - [`tests/properties.rs`](./tests/properties.rs) — `proptest`-driven invariants: flat ranking (sorted, bounded, unique) and the durable round-trip preserving arbitrary record sets.\n  - [`tests/recall.rs`](./tests/recall.rs) — recall@k of HNSW and IVF measured against the exact flat oracle on deterministic synthetic data.\n  - [`tests/recovery.rs`](./tests/recovery.rs) — crash recovery: a torn WAL tail is truncated (prior records survive), a corrupt snapshot fails the open, a non-database file is rejected.\n- Doc tests run as part of `cargo test` and validate every `# Examples` block.\n\n```sh\ncargo test\ncargo test --all-features\nRUSTDOCFLAGS=\"-D warnings\" cargo doc --no-deps\nRUSTDOCFLAGS=\"-D warnings\" cargo doc --no-deps --all-features\ncargo clippy --all-targets -- -D warnings\ncargo clippy --all-targets --all-features -- -D warnings\ncargo fmt --all -- --check\n```\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Cross-Platform Support\n\n**Tier 1 targets** — every commit is built and tested on:\n\n- Linux (`x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`)\n- macOS (`x86_64-apple-darwin`, `aarch64-apple-darwin`)\n- Windows (`x86_64-pc-windows-msvc`)\n\nDurable storage is provided by `iqdb-persist`, which takes the strongest power-loss sync each platform offers and replaces snapshots atomically. The on-disk format is little-endian on every platform, so a database written on one architecture reads back identically on another.\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Configuration\n\n### Feature flags\n\nFeature flags are strictly additive (per REPS) — enabling any combination never removes or weakens existing functionality.\n\n| Feature    | Default | Description |\n|------------|---------|-------------|\n| `serde`    | off     | Derive `Serialize` / `Deserialize` on the public data types (forwards to the family `serde` features). |\n| `parallel` | off     | Rayon-backed parallel distance scan on the flat index (forwards to `iqdb-flat`). |\n| `zstd`     | off     | Zstandard snapshot compression (forwards to `iqdb-persist`). |\n| `lz4`      | off     | LZ4 snapshot compression (forwards to `iqdb-persist`). |\n| `async`    | off     | Tokio-driven `AsyncIqdb` mirror of the public API. Pulls `tokio` (only the `rt` feature). |\n\n```toml\niqdb = { version = \"1\", features = [\"serde\"] }\n```\n\n### Runtime configuration\n\n`Iqdb::open_in_memory(dim, metric)` and `Iqdb::open(path, dim, metric)` cover the common case. Index selection, tuning, and caching are configured through the fluent [`IqdbConfig`](./src/config.rs) passed to the `_with` constructors.\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Architecture\n\nThe crate is the integration layer over the iqdb family; each module owns one concern:\n\n- `src/lib.rs` — crate root, lint profile, vocabulary re-exports.\n- `src/handle.rs` — the [`Iqdb`](./src/handle.rs) handle and its `RwLock`-guarded in-memory / file-backed storage seam.\n- `src/config.rs` — the fluent [`IqdbConfig`](./src/config.rs) and the [`IndexKind`](./src/config.rs) union.\n- `src/error.rs` — the unified [`Error`](./src/error.rs) wrapping the family error vocabularies.\n- `src/engine/mod.rs` — `IqdbCore`, the owned engine that implements the `iqdb-index` and `iqdb-persist` traits over an authoritative row store plus a derived index.\n- `src/engine/store.rs` — the authoritative, insertion-ordered row store (the single source of truth for `len` and rebuilds).\n- `src/engine/index.rs` — `AnyIndex`, the closed enum over `FlatIndex` / `HnswIndex` / `IvfIndex` with the IVF training hooks.\n- `src/engine/codec.rs` — the little-endian on-disk payload codec inside the `iqdb-persist` frame.\n\n### Compile-time guarantees\n\nThe crate root enables the strict REPS lint profile in [`src/lib.rs`](./src/lib.rs):\n\n```text\n#![deny(warnings)]\n#![deny(missing_docs)]\n#![deny(unsafe_op_in_unsafe_fn)]\n#![deny(unused_must_use)]\n#![deny(unused_results)]\n#![deny(clippy::unwrap_used)]\n#![deny(clippy::expect_used)]\n#![deny(clippy::todo)]\n#![deny(clippy::unimplemented)]\n#![deny(clippy::print_stdout)]\n#![deny(clippy::print_stderr)]\n#![deny(clippy::dbg_macro)]\n#![deny(clippy::unreachable)]\n#![deny(clippy::undocumented_unsafe_blocks)]\n```\n\nTest modules locally relax the `unwrap_used` / `expect_used` lints — the strict profile is for production library code, not assertion scaffolding inside `#[cfg(test)]` blocks. The crate contains no `unsafe` code.\n\n\u003chr\u003e\n\u003cbr\u003e\n\n## Contributing\n\nPull requests are welcome. Before opening one, please make sure the full CI gate passes locally:\n\n```sh\ncargo fmt --all -- --check\ncargo clippy --all-targets -- -D warnings\ncargo clippy --all-targets --all-features -- -D warnings\ncargo test\ncargo test --all-features\nRUSTDOCFLAGS=\"-D warnings\" cargo doc --no-deps\nRUSTDOCFLAGS=\"-D warnings\" cargo doc --no-deps --all-features\ncargo deny check\ncargo audit\n```\n\nEvery contribution is expected to honour the standards in [`REPS.md`](./REPS.md) — performance, security, error handling, testing, documentation, and dependency hygiene are all enforced as merge gates, not afterthoughts. Commit messages are imperative, lowercase, and scoped to a single logical change.\n\n\u003chr\u003e\n\u003cbr\u003e\n\n\u003cdiv align=\"center\"\u003e\n    \u003csup\u003e\n        \u003cspan\u003eHOME\u003c/span\u003e\n        \u003cspan\u003e\u0026nbsp;│\u0026nbsp;\u003c/span\u003e\n        \u003ca href=\"https://github.com/jamesgober/iqdb/blob/main/CHANGELOG.md\" title=\"Changelog\"\u003e\u003cb\u003eCHANGELOG\u003c/b\u003e\u003c/a\u003e\n        \u003cspan\u003e\u0026nbsp;│\u0026nbsp;\u003c/span\u003e\n        \u003ca href=\"https://github.com/jamesgober/iqdb/blob/main/REPS.md\" title=\"Standards\"\u003e\u003cb\u003eSTANDARDS\u003c/b\u003e\u003c/a\u003e\n        \u003cspan\u003e\u0026nbsp;│\u0026nbsp;\u003c/span\u003e\n        \u003ca href=\"https://docs.rs/iqdb\" title=\"API Reference\"\u003e\u003cb\u003eDOCS\u003c/b\u003e\u003c/a\u003e\n    \u003c/sup\u003e\n\u003c/div\u003e\n\u003cbr\u003e\n\n## Links\n\n- [Documentation (docs.rs)](https://docs.rs/iqdb)\n- [Crates.io](https://crates.io/crates/iqdb)\n- [Repository](https://github.com/jamesgober/iqdb)\n- [Issues](https://github.com/jamesgober/iqdb/issues)\n- [Changelog](./CHANGELOG.md)\n- [Standards (REPS)](./REPS.md)\n\n\u003cbr\u003e\n\n\u003chr\u003e\n\u003cbr\u003e\n\n\u003c!-- LICENSE\n############################################ --\u003e\n\u003cdiv id=\"license\"\u003e\n    \u003ch2\u003eLICENSE\u003c/h2\u003e\n    \u003cp\u003eLicensed under either of\u003c/p\u003e\n    \u003cb\u003eApache License, Version 2.0\u003c/b\u003e: \u003ca href=\"./LICENSE-APACHE\"\u003eLICENSE-APACHE\u003c/a\u003e\n      \u0026mdash; \u003ca href=\"http://www.apache.org/licenses/LICENSE-2.0\" target=\"_blank\"\u003ehttp://www.apache.org/licenses/LICENSE-2.0\u003c/a\u003e\n    \u003cbr\u003e\u003cbr\u003e\n    \u003cb\u003eMIT License\u003c/b\u003e: \u003ca href=\"./LICENSE-MIT\"\u003eLICENSE-MIT\u003c/a\u003e \u0026mdash;\n    \u003ca href=\"http://opensource.org/licenses/MIT\" target=\"_blank\"\u003ehttp://opensource.org/licenses/MIT\u003c/a\u003e\n    \u003cbr\u003e\u003cbr\u003e\n    \u003cp\u003eAt your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.\u003c/p\u003e\n\u003c/div\u003e\n\n\n\u003c!-- COPYRIGHT\n------------------------------\u003e\n\u003cdiv align=\"center\"\u003e\n    \u003ch2\u003e\u003c/h2\u003e\n    Copyright \u0026copy; 2026 James Gober.\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesgober%2Fiqdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamesgober%2Fiqdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamesgober%2Fiqdb/lists"}