Skip to main content

Embedded vector store for local-first AI applications.

Project description

vectlite

PyPI version Python versions License: MIT

Embedded vector store for local-first AI applications.

vectlite is a single-file, zero-dependency vector database written in Rust with Python bindings. It gives you dense + sparse hybrid search, HNSW indexing, metadata filtering, transactions, and crash-safe persistence in a single .vdb file -- no server, no Docker, no network calls.

Installation

pip install vectlite

Requires Python 3.9+. Pre-built wheels are available for macOS (x86_64, arm64), Linux (x86_64, aarch64), and Windows (x86_64).

Quick Start

import vectlite

with vectlite.open("knowledge.vdb", dimension=384) as db:
    # Insert records with vectors, metadata, and sparse terms
    db.upsert("doc1", embedding, {"source": "blog", "title": "Auth Guide"})
    db.upsert("doc2", embedding2, {"source": "notes", "title": "Billing"})

    # Search with filters
    results = db.search(embedding_query, k=5, filter={"source": "blog"})

    # Query-free inspection
    print(db.count(filter={"source": "blog"}))

Features

Core

  • Single-file storage -- one .vdb file per database, portable and easy to back up
  • Dense vectors -- cosine similarity with automatic HNSW indexing for large collections
  • Sparse vectors -- BM25-scored inverted index for keyword retrieval
  • Hybrid search -- dense + sparse fusion with linear or RRF strategies
  • Rich metadata -- str, int, float, bool, None, list, dict values
  • Crash-safe WAL -- writes land in a write-ahead log first, then checkpoint with compact()
  • Transactions -- atomic batched writes with db.transaction()
  • File locking -- advisory locks prevent corruption from concurrent access

Search & Retrieval

  • Metadata filters -- MongoDB-style operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $contains, $exists, $and, $or, $not
  • Nested filters -- dot-path traversal (author.name), $elemMatch, $size on lists and dicts
  • Named vectors -- multiple vector spaces per record (vectors={"title": [...], "body": [...]})
  • Multi-vector queries -- weighted search across vector spaces in a single call
  • MMR diversification -- mmr_lambda controls relevance vs. diversity trade-off
  • Namespaces -- logical isolation with per-namespace or cross-namespace search
  • Rerankers -- built-in text_match(), metadata_boost(), cross_encoder(), bi_encoder(), composable with compose()
  • Observability -- search_with_stats() returns timings, BM25 term scores, ANN stats, and per-result explain payloads

Data Management

  • Physical collections -- vectlite.open_store() manages a directory of independent databases
  • Bulk ingestion -- bulk_ingest() with deferred index rebuilds for fast imports
  • Listing & filtered counts -- list() and count(namespace=..., filter=...) without a vector query
  • Delete by filter -- remove matching records across a namespace slice in one call
  • Snapshots -- db.snapshot(path) creates a self-contained copy
  • Backup / Restore -- db.backup(dir) and vectlite.restore(dir, path) for full roundtrips
  • Read-only mode -- vectlite.open(path, read_only=True) for safe concurrent readers
  • Explicit close -- db.close() or with vectlite.open(...) as db: to release locks deterministically
  • Lock timeouts -- lock_timeout= retries for bounded lock acquisition waits
  • Text analyzers -- configurable tokenizer pipeline with stopwords, stemming, and n-grams

Usage

Hybrid Search with Reranking

import vectlite

db = vectlite.open("knowledge.vdb", dimension=384)

# Upsert with dense + sparse vectors
db.upsert(
    "doc1",
    dense_embedding,
    {"source": "docs", "title": "Auth Setup", "text": "How to configure SSO..."},
    sparse=vectlite.sparse_terms("How to configure SSO authentication"),
)

# Hybrid search with reranking
results = db.search(
    query_embedding,
    k=10,
    sparse=vectlite.sparse_terms("SSO authentication"),
    fusion="rrf",
    filter={"source": "docs"},
    explain=True,
    rerank=vectlite.rerankers.compose(
        vectlite.rerankers.text_match(),
        vectlite.rerankers.metadata_boost("source", {"docs": 0.5}),
    ),
)

for result in results:
    print(result["id"], result["score"])

Bulk Ingestion (Recommended for Large Imports)

For ingesting more than a few hundred records, use bulk_ingest() instead of calling upsert() in a loop. It writes records in WAL batches and rebuilds indexes only once at the end, making it orders of magnitude faster.

records = [
    {
        "id": f"doc{i}",
        "vector": embeddings[i],
        "metadata": {"source": "corpus", "chunk": i},
        "sparse": vectlite.sparse_terms(texts[i]),  # optional
    }
    for i in range(len(texts))
]

count = db.bulk_ingest(records, batch_size=5000)
print(f"Ingested {count} records")

The records parameter is a list[dict] where each dict has keys:

  • id (str, required) -- unique record identifier
  • vector (list[float], required) -- dense embedding vector
  • metadata (dict, optional) -- arbitrary metadata
  • sparse (dict[str, float], optional) -- sparse terms from sparse_terms()
  • vectors (dict[str, list[float]], optional) -- named vectors
  • namespace (str, optional) -- namespace override per record

upsert_many() and insert_many() also accept the same list[dict] format and rebuild indexes once, but don't batch WAL writes internally.

Collections

store = vectlite.open_store("./my_collections")
products = store.create_collection("products", dimension=384)
products.upsert("p1", embedding, {"name": "Widget", "price": 9.99})

logs = store.open_or_create_collection("logs", dimension=128)
print(store.collections())  # ["logs", "products"]

Transactions

with db.transaction() as tx:
    tx.upsert("doc1", emb1, {"source": "a"})
    tx.upsert("doc2", emb2, {"source": "b"})
    tx.delete("old_doc")
# All operations commit atomically or roll back on exception

Text Helpers

# Handles embedding + sparse term generation for you
vectlite.upsert_text(db, "doc1", "Auth setup guide", embed_fn, {"source": "docs"})
results = vectlite.search_text(db, "how to authenticate", embed_fn, k=5)

Analyzers

analyzer = vectlite.analyzers.Analyzer().lowercase().stopwords("en").stemmer("english")
terms = analyzer.sparse_terms("How to authenticate users with SSO")
# Use with upsert: db.upsert("doc1", emb, meta, sparse=terms)

Snapshots & Backup

db.snapshot("/backups/knowledge_2024.vdb")  # Self-contained copy
db.backup("/backups/full/")                 # Full backup with ANN sidecars

restored = vectlite.restore("/backups/full/", "restored.vdb")

Read-Only Mode

ro = vectlite.open("knowledge.vdb", read_only=True, lock_timeout=5.0)
results = ro.search(query, k=5)  # Reads work
ro.upsert(...)                    # Raises VectLiteError

Listing, Counting, and Lifecycle

db = vectlite.open("knowledge.vdb", dimension=384, lock_timeout=5.0)

records = db.list(namespace="docs", filter={"stale": False}, limit=20)
count = db.count(namespace="docs", filter={"source": "blog"})
deleted = db.delete_by_filter({"stale": True}, namespace="docs")

db.close()

Search Diagnostics

outcome = db.search_with_stats(query, k=5, sparse=terms, explain=True)

print(outcome["stats"]["timings"])       # {"dense_us": 120, "sparse_us": 45, ...}
print(outcome["stats"]["used_ann"])      # True
print(outcome["results"][0]["explain"])  # Detailed scoring breakdown

Filter Operators

Operator Example Description
$eq {"field": {"$eq": "value"}} Equal (also {"field": "value"})
$ne {"field": {"$ne": "value"}} Not equal
$gt / $gte {"field": {"$gt": 5}} Greater than (or equal)
$lt / $lte {"field": {"$lt": 20}} Less than (or equal)
$in / $nin {"field": {"$in": ["a", "b"]}} In / not in set
$contains {"field": {"$contains": "auth"}} Substring match
$exists {"field": {"$exists": True}} Field presence
$and / $or {"$and": [{...}, {...}]} Logical combinators
$not {"$not": {...}} Logical negation
$elemMatch {"tags": {"$elemMatch": {"$eq": "rust"}}} Match list elements
$size {"tags": {"$size": 3}} List length
dot-path {"author.name": "Alice"} Nested field access

Database Methods Reference

Write Methods

Method Description
db.upsert(id, vector, metadata, sparse=..., vectors=...) Insert or update a single record
db.insert(id, vector, metadata, sparse=..., vectors=...) Insert a record (raises on duplicate id)
db.upsert_many(records, namespace=None) Upsert a batch of records (single index rebuild)
db.insert_many(records, namespace=None) Insert a batch (raises on duplicate ids)
db.bulk_ingest(records, namespace=None, batch_size=10000) Fastest bulk import with batched WAL writes
db.delete(id, namespace=None) Delete a single record
db.delete_many(ids, namespace=None) Delete multiple records by id
db.delete_by_filter(filter, namespace=None) Delete all matching records in one filtered pass

Read Methods

Method Description
db.get(id, namespace=None) Get a single record by id
db.search(query, k=10, ...) Search and return a list of results
db.search_with_stats(query, k=10, ...) Search with detailed performance stats
db.count(namespace=None, filter=None) or len(db) Count records, optionally scoped by namespace/filter
db.list(namespace=None, filter=None, limit=0, offset=0) List records without issuing a vector query
db.namespaces() List all namespaces
db.dimension Vector dimension (property)
db.path Database file path (property)
db.read_only Whether the database is read-only (property)

Maintenance Methods

Method Description
db.compact() Fold WAL into snapshot and persist ANN indexes
db.flush() Alias for compact()
db.snapshot(dest) Create a self-contained .vdb copy
db.backup(dest_dir) Full backup including ANN sidecar files
db.transaction() Begin an atomic transaction (use as context manager)
db.close() Flush pending state, release the file lock, and invalidate the handle
with vectlite.open(...): Python context-manager form of automatic close

How It Works

  • Records are stored in a compact binary .vdb snapshot file
  • Writes go through a crash-safe WAL (.wal) before being applied in memory
  • compact() folds the WAL into the snapshot and persists HNSW sidecar files
  • Dense search uses HNSW indexes (auto-built for collections above ~128 records)
  • Sparse search uses an inverted index with BM25 scoring
  • Hybrid fusion combines dense + sparse via linear combination or reciprocal rank fusion
  • Advisory file locks (flock) prevent concurrent write corruption

Links

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vectlite-0.1.11.tar.gz (61.8 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

vectlite-0.1.11-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ ARM64

vectlite-0.1.11-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded PyPymanylinux: glibc 2.17+ ARM64

vectlite-0.1.11-cp39-abi3-win_amd64.whl (1.6 MB view details)

Uploaded CPython 3.9+Windows x86-64

vectlite-0.1.11-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.8 MB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

vectlite-0.1.11-cp39-abi3-macosx_11_0_arm64.whl (1.6 MB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

vectlite-0.1.11-cp39-abi3-macosx_10_12_x86_64.whl (1.7 MB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

vectlite-0.1.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.8 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

File details

Details for the file vectlite-0.1.11.tar.gz.

File metadata

  • Download URL: vectlite-0.1.11.tar.gz
  • Upload date:
  • Size: 61.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for vectlite-0.1.11.tar.gz
Algorithm Hash digest
SHA256 bc08416e8ca36fbbb6f79eced9bb373c5a336a04d9b5ddbc41bbfd03ecb3d669
MD5 2a7bffe9617e0aff984f33f2fd980dc6
BLAKE2b-256 40a1df30a85d1984558caa9dea07e51f16af93e51038bb4c4765054431064ace

See more details on using hashes here.

File details

Details for the file vectlite-0.1.11-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for vectlite-0.1.11-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 eda8ffcd1783c688ad5dd593dd5f16f0517f83a479f0c6adb44e6553378e4f44
MD5 27251abec453110cb2430d5c4843e119
BLAKE2b-256 8efa1cbb1e05d1fd7aee2219b6cf99bca84bd3a9d1bd516f24de17bc5ecb3f67

See more details on using hashes here.

File details

Details for the file vectlite-0.1.11-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for vectlite-0.1.11-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 b721205b1f2423e87a3708cb4a421ad27c2da007c536ece218d7362c7886ab00
MD5 c666a5ccd12a2373371aabf7d64d7c86
BLAKE2b-256 38248d3030b502e47e9e0c0b63c385ab5c1aac217b00e6c045304293f5ab05f6

See more details on using hashes here.

File details

Details for the file vectlite-0.1.11-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: vectlite-0.1.11-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 1.6 MB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for vectlite-0.1.11-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 03d9584f1f5348411deed6c60bc6fb50a384494085c41bca44b8042bdf6ec9d5
MD5 334f2d9b7082edd882b18044fea1e1ec
BLAKE2b-256 c036ad5d0e17b703213e216f66507dc51c4f38459e650eff52794c9befff83e7

See more details on using hashes here.

File details

Details for the file vectlite-0.1.11-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for vectlite-0.1.11-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 88655e903778db7288726521c14e0fc984976b9876ac36050b0c4c56ebca8f24
MD5 717d0caad6cf3497942fe1593366801c
BLAKE2b-256 b13305942b70c1e9190c348d5851b0d51d4a53d3ad31b54ff5e818e4b5edc758

See more details on using hashes here.

File details

Details for the file vectlite-0.1.11-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for vectlite-0.1.11-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4dbbc19d7f89d508fa073d4d004af0e83e215188d962f8b818ef4749e2b26edb
MD5 0cf51281a89c6360e03d8c5da79a3a5b
BLAKE2b-256 cfa52b91ffa9aa542170bf798d1cfa1e9e75c444135cc0143eef757e24a4aab0

See more details on using hashes here.

File details

Details for the file vectlite-0.1.11-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for vectlite-0.1.11-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 956b00d6342dc22077fbaf4e4485265bc1a0f1f4bf0a8a1f911a9c7c0b3b745a
MD5 0ebcfd6b03ba37716d61ef3278680ec3
BLAKE2b-256 54a328df1dccb1b9e867c61002574012adb8bd3bf93f79157fc99e7155e2a7a4

See more details on using hashes here.

File details

Details for the file vectlite-0.1.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for vectlite-0.1.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 361e1714a03d53cbd35558d32fdd6d0656975bf6855e3eb6c13b586709747deb
MD5 03d1422a98206aa6beb7a6f32679c0ec
BLAKE2b-256 bfae7acdb8be46935e3cf4a9d953e2455fd34c0265450495892c595aaa9feaf8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page