Skip to main content

Architecture

vectlite is an embedded vector database written in Rust with first-class bindings for Python and Node.js.

Rust Core

The database engine is implemented entirely in Rust (vectlite-core). This provides:

  • Memory safety without garbage collection
  • Predictable, low-latency performance
  • A single codebase shared by all language bindings
  • Small binary size with no runtime dependencies

Language Bindings

LanguageBridgePackage
PythonPyO3pip install vectlite
Node.jsnapi-rsnpm install vectlite
RustDirect cratevectlite-core

PyO3 and napi-rs generate native extensions that call directly into the Rust core with minimal overhead. There is no IPC, no serialization layer, and no separate server process.

Storage Format

Each vectlite database consists of several files on disk:

FileDescription
<name>.vdbMain database file containing records, metadata, and sparse indexes.
<name>.vdb.walWrite-ahead log for uncommitted writes.
<name>.vdb.annHNSW approximate nearest-neighbor index sidecar.
<name>.vdb.lockAdvisory file lock for concurrency control.

See Storage Format for details.

Indexing

Dense Vectors (HNSW)

vectlite uses the HNSW (Hierarchical Navigable Small Worlds) algorithm for approximate nearest-neighbor search.

  • Auto-build threshold: The HNSW index is built automatically once a database reaches 128 or more records. Below this threshold, search falls back to exact brute-force scan, which is faster for small collections.
  • Incremental updates: New records are added to the graph incrementally. A full rebuild occurs during compact() if the index has drifted significantly.
  • Cosine similarity: All dense search uses cosine similarity as the distance metric. Vectors are normalized internally.

Sparse Vectors (BM25)

Sparse keyword search uses an inverted index with BM25 scoring:

  • Terms are extracted using a configurable text analyzer (see vectlite.analyzers).
  • Each term maps to a posting list of record IDs and term frequencies.
  • At query time, BM25 scores are computed from term frequencies, document frequencies, and average document length.

Fusion Strategies

When both dense and sparse scores are available, vectlite combines them using one of two strategies:

Linear Combination (default)

final_score = dense_weight * dense_score + sparse_weight * sparse_score

Both scores are normalized to [0, 1] before combination. Adjust dense_weight and sparse_weight to control the balance.

Reciprocal Rank Fusion (RRF)

final_score = sum(1 / (rrf_k + rank_i))

RRF combines the rank positions from each retrieval method rather than raw scores. The rrf_k constant (default 60) controls smoothing. RRF is more robust when dense and sparse scores are on different scales.

File Locking and Concurrency

vectlite uses advisory file locks via the .lock file to manage concurrent access:

  • Single writer: Only one process can open a database for read-write at a time. Attempting to open a second writer raises VectLiteError.
  • Multiple readers: Any number of processes can open the same database in read_only mode simultaneously. Read-only mode acquires a shared lock.
  • In-process concurrency: Within a single process, the Database object is not thread-safe. Use a mutex or confine access to a single thread. Searches on a read-only database are safe to run concurrently from multiple threads.

Repository Layout

vectlite/
core/ Rust core library (vectlite-core crate)
src/
storage/ File format, WAL, compaction
index/ HNSW and inverted index implementations
search/ Query planning, fusion, MMR
ffi/ Shared FFI helpers
bindings/
python/ PyO3 bindings and Python package
node/ napi-rs bindings and npm package
tests/ Integration tests

The Rust core is the single source of truth. The binding layers are thin wrappers that translate language-native types to and from Rust structures.