Skip to main content

sqlite-hybrid-search

Ultra-fast, in-process hybrid search & agent memory engine in C++17 with Python bindings.

License: MIT C++17 Python PyPI

Drop it inside an app to give a local LLM two things it lacks: knowledge of your private data (RAG) and memory that survives across sessions — with no vector database to run and no cloud. SQLite is the source of truth; the usearch HNSW graph is persisted to a .usearch sidecar and memory-loaded on open, so startup is instant regardless of corpus size.


Why this exists

Embedded / zero-service One process, one SQLite file plus a .usearch sidecar. No Docker, no daemon, no port. A query is a function call — sub-millisecond for dense search (see benchmarks).
Instant startup The vector index is loaded from disk, not rebuilt — ~25 ms to open a 100k-chunk store, versus ~15 s to reconstruct it from SQLite.
Hybrid retrieval Dense vector search (usearch, cosine HNSW) + sparse keyword search (SQLite FTS5 / BM25), fused by Reciprocal Rank Fusion (RRF, k=60). Rank-based fusion — no score normalisation, no per-query tuning.
Agent memory Exponential recency decay — score × e^(−λ·age_days) — so a fresher, slightly-less-similar memory can outrank a stale one. λ = 0 is an exact no-op.
Explainable search_explained() returns the full per-result score trail: dense distance & rank, BM25 score & rank, fused score, recency factor, decayed score.
Bring your own embeddings The core takes caller-supplied vectors and computes nothing. An optional built-in embedder (ONNX Runtime, e.g. all-MiniLM-L6-v2) is layered on top for a text-in path.

Install

Not yet published to PyPI. Build from source. You need a C++17 toolchain, CMake ≥ 3.24, Python ≥ 3.9 with venv, and SQLite (with FTS5 — the default on mainstream builds). usearch and GoogleTest are fetched automatically.

git clone https://github.com/ashray-00/sqlite-hybrid-search
cd sqlite-hybrid-search
python3 -m venv .venv && .venv/bin/pip install -e .

Dependencies:

macOS (Homebrew) Debian / Ubuntu Fedora / RHEL Arch
Toolchain + CMake xcode-select --install
brew install cmake
sudo apt install build-essential cmake python3-venv sudo dnf install gcc-c++ cmake python3-devel sudo pacman -S base-devel cmake
SQLite brew install sqlite sudo apt install libsqlite3-dev sudo dnf install sqlite-devel sudo pacman -S sqlite
  • macOS only: Homebrew's sqlite is keg-only, so configure the C++ build with -DCMAKE_PREFIX_PATH=/opt/homebrew (the CMake project also autodetects it via brew --prefix). On Linux the system SQLite is found with no extra flags.
  • Ubuntu 22.04 ships CMake 3.22; either .venv/bin/pip install "cmake>=3.24" or add the Kitware APT repo.

Optional built-in ONNX embedder. Without ONNX Runtime the engine still builds and every caller-supplied-vector path works unchanged; only load_embedding_model() / add_text() / search_text() are unavailable.

Install ONNX Runtime
macOS brew install onnxruntime
Linux Download a release from microsoft/onnxruntime (onnxruntime-linux-x64-*.tgz), then sudo cp -r onnxruntime-linux-x64-*/include/* /usr/local/include/ and sudo cp -rP onnxruntime-linux-x64-*/lib/* /usr/local/lib/ && sudo ldconfig. Or point CMake at it directly: -DONNXRUNTIME_INCLUDE_DIR=<dir> -DONNXRUNTIME_LIBRARY=<dir>/libonnxruntime.so.

Quickstart (Python)

import sqlite_hybrid_search

engine = sqlite_hybrid_search.Engine("memory.sqlite3", dim=3)

# Ingest documents with caller-supplied embeddings (one vector per document).
engine.add(
    documents=[
        {"id": "home", "text": "I live in Munich, Germany."},
        {"id": "pet",  "text": "My cat is named Pixel."},
    ],
    embeddings=[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]],
)

# Hybrid search: dense vector + BM25 keyword, RRF-fused.
hits = engine.search_hybrid("Where do I live?", [1.0, 0.0, 0.0], top_k=2)
print(hits[0]["document_id"], hits[0]["score"])          # -> home 0.0328

# Memory retrieval: same, discounted by recency (decay_lambda=0 disables it).
recall = engine.search_memory("Where do I live?", [1.0, 0.0, 0.0], top_k=2, decay_lambda=0.1)

# Full score breakdown for one result.
trail = engine.search_explained("Where do I live?", [1.0, 0.0, 0.0], top_k=1)[0]
print(trail["dense_rank"], trail["sparse_rank"], trail["fused_score"], trail["recency_factor"])

Per-chunk timestamps (so recency decay can actually reorder results) are set through the native sqlite_hybrid_search._sqlite_hybrid_search_ext types — see tests/test_memory_search.py.

Quickstart (CLI)

The hybrid-search console script chunks a folder of .txt files into an index in the current directory. It uses a deterministic hashing stand-in for embeddings unless you pass --model / --model-dim.

$ hybrid-search ingest ./docs
Ingested 2 document(s), 2 chunk(s), into /path/to/cwd/.hybrid_search.sqlite3

$ hybrid-search query "where do I live" --decay 0.1
1. [notes.txt] (score=0.0328) I live in Munich. My office is near the river.
2. [work.txt] (score=0.0161) The quarterly report is due on Friday.

$ hybrid-search query "where do I live" --decay 0.1 --explain    # full score trail

Empirical benchmarks (verified)

Reproduce with python benchmarks/run_eval.py; full method and raw data in BENCHMARKS.md and benchmarks/results.json. Machine: Apple Silicon (macOS arm64), single thread, embedding dim 64, 100 labelled queries.

Read the corpus honestly. It is synthetic, and the dense column uses a 64-dim hashed bag-of-words, not a trained model — so sparse is a best case and dense a worst case. Latency and memory are model-independent and transfer directly; the quality table shows how RRF behaves when the two signals disagree, not an absolute score for dense retrieval.

Retrieval quality (Recall@10 / nDCG@10)

Approach 1k 10k 100k
Dense only 0.987 / 0.944 0.783 / 0.752 0.493 / 0.507
Sparse only (BM25) 1.000 / 1.000 1.000 / 1.000 1.000 / 1.000
Hybrid (RRF) 1.000 / 0.992 1.000 / 0.969 1.000 / 0.951
Hybrid + recency decay 1.000 / 0.998 1.000 / 0.998 1.000 / 0.997

Hybrid recovers every point of recall the dense path loses at scale (1.000 vs 0.493 at 100k) — RRF lets the keyword side carry the query when the vector side degrades.

Latency (warm cache, single thread)

Approach 1k p50 / p99 100k p50 / p99 100k throughput
Dense 0.090 / 0.097 ms 0.173 / 0.211 ms 5,705 q/s
Hybrid 0.524 / 0.558 ms 49.4 / 50.5 ms 20 q/s

Dense stays sub-0.2 ms p50 at 100k. Sparse/hybrid latency is dominated by FTS5 and grows with corpus size — the main query-time bottleneck.

Startup: instant, disk-backed index

The usearch graph is serialised to a <db>.usearch sidecar and memory-loaded on the next open, instead of being rebuilt from SQLite:

1k 10k 100k
Index load on open 0.7 ms 3.2 ms 24.5 ms
(previously: rebuild from SQLite) 40 ms 0.7 s 14.6 s

SQLite stays authoritative — a missing, truncated, or out-of-sync sidecar is rejected and the engine rebuilds transparently.

Memory & storage footprint

1k 10k 100k
SQLite on disk 0.57 MB 5.2 MB 52 MB
.usearch sidecar ~0.5 MB ~4 MB 40.5 MB
Peak RSS (Python-driven) 35 MB 79 MB 430 MB

The native C++ cross-check (benchmarks/run_benchmarks.cpp) puts the engine-only peak RSS at ~38 MB for 20,000 documents — most of the Python-driven figure is the benchmark driver holding the corpus, not the engine.


Comparison

sqlite-hybrid-search sqlite-vec + glue ChromaDB Qdrant / Milvus / Weaviate
Deployment in-process library, 1 file in-process (SQLite ext) embedded lib or server separate server / cluster
Process to run none none none (embedded) / one (server) one+
Dense + sparse hybrid built in (RRF) DIY (wire up FTS5 + fusion) dense-first built in (server-side)
Recency / memory semantics built in (search_memory) DIY DIY DIY (metadata + custom scoring)
Score-trail / explainability search_explained() DIY limited varies
Text + metadata storage SQLite (authoritative) second table you design built in built in
Chunking token-window built in DIY some DIY / integrations
Vector-index persistence .usearch sidecar, auto-managed rows in SQLite persisted persisted
Ops surface none none small real (scaling, backups, upgrades)
Best fit desktop / CLI / edge agents, local-first, privacy you already live in SQLite Python RAG prototypes multi-tenant, large-scale, networked

Reach for a dedicated vector DB instead if you need horizontal scale, multi-writer concurrency, or sub-10 ms keyword search over millions of documents.


C++ integration

The public header exposes no usearch or SQLite types (Pimpl idiom); the only hard dependency is SQLite (usearch is fetched by CMake). The C++ symbols live in the retrieval_engine namespace (header path retrieval_engine/) — an internal name kept stable across the Python-package rename.

include(FetchContent)
FetchContent_Declare(sqlite_hybrid_search
    GIT_REPOSITORY https://github.com/ashray-00/sqlite-hybrid-search
    GIT_TAG main)
FetchContent_MakeAvailable(sqlite_hybrid_search)

target_link_libraries(your_target PRIVATE sqlite_hybrid_search::core)
#include "retrieval_engine/retrieval_engine.hpp"

retrieval_engine::RetrievalEngine engine("memory.sqlite3", /*dim=*/384);

retrieval_engine::DocumentInput doc;
doc.document_id = "home";
doc.chunks.push_back({ "I live in Munich.", embedding /*std::vector<float>*/, 0, 4 });
engine.add_documents({ doc });

auto hits = engine.search_hybrid("Where do I live?", query_vec, /*k=*/5);
auto memory = engine.search_memory("Where do I live?", query_vec, /*k=*/5, /*decay_lambda=*/0.1f);

A single instance is not thread-safe — confine it to one thread or lock externally (one instance per thread, each with its own SQLite connection).


Project layout

core/        C++17 engine (chunking, dense index, FTS5, RRF fusion, recency decay)
bindings/    nanobind extension module
python/      sqlite_hybrid_search package (friendly wrapper + `hybrid-search` CLI)
benchmarks/  reproducible recall / latency / memory harness
docs/        architecture decision records + development log

Building & testing

# Linux
cmake -B build && cmake --build build

# macOS (Homebrew SQLite / ONNX Runtime live under /opt/homebrew)
cmake -B build -DCMAKE_PREFIX_PATH=/opt/homebrew && cmake --build build

ctest --test-dir build --output-on-failure      # C++ suite
.venv/bin/pytest                                 # Python + CLI suite

Contributing

Issues and pull requests welcome. The project follows a strict TDD workflow (failing test first, then implementation, then an independent review pass) and enforces formatting with .clang-format (C++) and ruff (Python). Run both test suites before opening a PR. Design rationale is recorded as ADRs in docs/DECISIONS.md.

License

MIT © 2026 Ashray Adhikari

Download files

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

Source Distribution

sqlite_hybrid_search-0.1.0.tar.gz (99.2 kB view details)

Uploaded Source

Built Distributions

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

sqlite_hybrid_search-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.13manylinux: glibc 2.17+ x86-64

sqlite_hybrid_search-0.1.0-cp313-cp313-macosx_11_0_arm64.whl (154.7 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

sqlite_hybrid_search-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.12manylinux: glibc 2.17+ x86-64

sqlite_hybrid_search-0.1.0-cp312-cp312-macosx_11_0_arm64.whl (154.7 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

sqlite_hybrid_search-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.11manylinux: glibc 2.17+ x86-64

sqlite_hybrid_search-0.1.0-cp311-cp311-macosx_11_0_arm64.whl (155.3 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

sqlite_hybrid_search-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.10manylinux: glibc 2.17+ x86-64

sqlite_hybrid_search-0.1.0-cp310-cp310-macosx_11_0_arm64.whl (155.6 kB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

sqlite_hybrid_search-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.9manylinux: glibc 2.17+ x86-64

sqlite_hybrid_search-0.1.0-cp39-cp39-macosx_11_0_arm64.whl (151.5 kB view details)

Uploaded CPython 3.9macOS 11.0+ ARM64

File details

Details for the file sqlite_hybrid_search-0.1.0.tar.gz.

File metadata

  • Download URL: sqlite_hybrid_search-0.1.0.tar.gz
  • Upload date:
  • Size: 99.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sqlite_hybrid_search-0.1.0.tar.gz
Algorithm Hash digest
SHA256 26df4936a9a051b6020ac5d489a9f167852608cd8b9692987b862bf39514fdb0
MD5 9d3a67ae3ae710414cd49d0a8b39e820
BLAKE2b-256 67d015e9e75208ce14cc9b307c66b39eb8ac1983d8e3d1ab213e72ada7f68a2e

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0.tar.gz:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 2f2bd771949c0662e31e08c9af5e512f9fecb5c84287952ffae16c4da979be20
MD5 f275ae0c8286dc1be2d57bde706daf0b
BLAKE2b-256 ade9dda513d667e74d2cfa31fc073cf42cb4bcc35b6cd52156e8fe2c28c27a91

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 3cc31ffc5950ac3f274a507205cd5440e2127a2020a3bdec413f8ad3fddc6ef5
MD5 99554f0626c5dc891bcc751e047b96f6
BLAKE2b-256 e06fc4954529bbbaf96823bb268cc0e6ca54e21505cc2ac223727c437616195e

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 8ebe53447794c61ac5f30ee6cf51f41eaa95469c78b12ddd6d62c546802f0624
MD5 03caa0e3f4aec39d1a46fd725be87452
BLAKE2b-256 eebf841dda0ea5ff27dc627f5db15d305566e5ab7804c0964813f8e4e114c92f

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e63ae62b7d34ed68737c20921cdad47968b3e4fb849c3a477da42df56c3ad88d
MD5 12929474dc9aff635fd74c951fa4a86e
BLAKE2b-256 3bcb225b007d5a969169c435c5ab819b6c2cb130f143f15547d989806f29c2cf

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1382c3ecd7dbbc6f2dc2e15e741a9d89cfcf44f94905993cc1604b2904ab4d3f
MD5 e5a5e8ec9bf635226d0079a5bfa842a3
BLAKE2b-256 4754f17a4374831a8797ff92da84955ea0b2c116ed3d74ed672bab616f2fff07

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0612801deab5c75a326de2b999d489952d98661e86f8ee85e0060b5c48d59750
MD5 066b3c992fa77f34a5dfed80e4770702
BLAKE2b-256 e560aa40f14e86ca7ebb823e173fc6f20dd2cfd563d0e2fdfd0b4a72b8179598

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 9ad121123042a8dd7a37c8d8e278706b5b4104228b3928838071106eb5cb3673
MD5 a983a3a7e44ca0d8c95c18f851c00c40
BLAKE2b-256 8c89d1758e5b952ba608344628a9b9de7d9e5f4c5e3dcbb60ce8e1390806796e

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 9772ea342eb96f2b7ea6d617688757d9bcf546cb138419b64ba2ebf1ff353fb7
MD5 7dda45d0d752f412751bb32280e75873
BLAKE2b-256 7ab9de80b10295ea396781fd268a6daea5bdf812715749acaa17451de2eda76e

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp310-cp310-macosx_11_0_arm64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 05ba341ae8cb5131ceb40605f4f19c1f84e730accf5680ac294c4a9ffbc97987
MD5 35a5415898f3ad54d462a60ded73c352
BLAKE2b-256 7df500d1d4165f727161204a204c482b98ffab812bc46a90594bc8f12910b137

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sqlite_hybrid_search-0.1.0-cp39-cp39-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for sqlite_hybrid_search-0.1.0-cp39-cp39-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d132edeac9b2e5d17c3357eb179b278505fd2c7db4a513eaf7935366b48cf051
MD5 38fb353f6cfca69b72da22b02b43dd90
BLAKE2b-256 866eb262739d7890710b4d53cfed840f951fad83f3a0a6f4effd00712c5a08f2

See more details on using hashes here.

Provenance

The following attestation bundles were made for sqlite_hybrid_search-0.1.0-cp39-cp39-macosx_11_0_arm64.whl:

Publisher: release.yml on ashray-00/sqlite-hybrid-search

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

11 files

This release

0.1.0 This release

11 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page