I Built a Tiny Vector Database (and Pointed It at FAISS)

Vector DBs are everywhere these days: Pinecone, Weaviate, Qdrant, Chroma, FAISS … you name it! Most of them are full-featured systems with servers, APIs, dashboards, the works.
Sometimes the best way to demystify hype is to build it yourself. Here’s my weekend project: a vector DB in a few hundred lines of Python.
I call it NoPokeDB (I don’t even remember why I chose that name). It’s tiny, file-based and durable enough not to fall apart if you pull the plug, unlike me 🤧.
Why bother?
Why not? A couple reasons:
I wanted to learn what’s really behind all the marketing fluff: how hard is ANN indexing, persistence and CRUD?
I needed a hackable playground - a DB I could modify at will without running a cluster.
I thought it’d be easy 💀 (It was fun)
How does it work?
Indexing: hnswlib does the heavy lifting for approximate nearest neighbor search (HNSW graph).
Metadata: SQLite, because it’s everywhere, lightweight, and dead simple.
Persistence: Vectors stored on disk, metadata in a DB file, index saved to
.bin
Crash safety: A tiny write-ahead oplog that replays on startup if something blew up mid-write.
API: Just a Python class with methods like
add
,add_many
,query
,get
,delete
,upsert
.
Tiny code snippet:
from nopokedb import NoPokeDB
db = NoPokeDB(dim=128, max_elements=10_000, path="./data")
vid = db.add(np.random.rand(128).astype(np.float32), {"name": "foo"})
hits = db.query(np.random.rand(128).astype(np.float32), k=5)
print(hits[0])
Features I ended up adding
Batch inserts + automatic
resize_index
growthCosine / L2 / Inner Product support
CRUD helpers:
get
,delete
andupsert
Crash recovery via oplog replay
Query results with both
score
and rawdistance
It’s about a couple hundred lines of Python!
Ah, the benchmarks
Well, building something is fun, but you only know if it’s useful once you put it against the big names.
I wrote a benchmark harness to pit NoPokeDB against FAISS 💀 (Yeah, it’s like putting Max Verstappen against a go-kart kid)
Dataset: 20k vectors, 128 dimensions, sampled from a Gaussian.
- This isn’t million-scale; it’s enough to feel the tradeoffs without waiting an hour.
Queries: 200 random queries
k: 10 nearest neighbours
Metric: cosine similarity
Metrics tracked:
Build throughput (vectors/sec)
Query QPS (queries/sec)
p50 / p95 latency (ms)
recall@k (compared to brute-force exact search)
Results
These numbers are directional — FAISS defaults weren’t tuned here. With efSearch
bumped up, FAISS recall jumps much higher.
Backend | Build v/s | QPS | p50 (ms) | p95 (ms) | recall@10 |
NoPokeDB | 12.8k | 3.6k | 0.27 | 0.31 | 0.63 |
FAISS | 95.6k | 10.7k | 0.09 | 0.12 | 0.31 |
Interpreting the Numbers
FAISS: is blazing fast (not surprising; it’s pure C++), but recall was low in my run because of default params. With tuning (
efSearch
,efConstruction
), it can easily hit >0.9 recall.NoPokeDB: is slower to build (Python + SQLite overhead) but recall was better right out of the box. Raising
ef
improves recall further.
Takeaway: you can trade speed for accuracy with a couple knobs. And even a “toy” DB can hold its own if you don’t need billion-scale throughput.
Lessons learned
Durability matters; the oplog was the trickiest but most important bit. Without it, a crash mid-add corrupts your index.
HNSW is forgiving; you can get decent recall with small effort, but the
ef
parameter is everything.APIs make it feel real; once I added
get
/delete
/upsert
, it stopped feeling like a demo and started feeling like a DB.Benchmarks are humbling; FAISS still wipes the floor with raw speed, but building my own gave me intuition about why.
What’s next?
Metadata filters (
where
clauses on SQLite JSON)Hybrid search (full-text + vectors)
A lightweight REST/gRPC server wrapper
Larger-scale tests (100k–1M vectors, recall curves)
Closing thoughts
NoPokeDB isn’t going to replace FAISS or Pinecone; and that’s the point!
It’s a hackable little lab project that taught me a ton about vector search, and maybe it can do the same for you.
Repo: https://github.com/kashifulhaque/nopokedb
PyPI: https://pypi.org/project/nopokedb/
P.S. if you run your own benchmarks (with bigger datasets, or against Qdrant/Weaviate), I’d love to hear the numbers. (Raise an issue on GitHub)
Subscribe to my newsletter
Read articles from Kashif directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Kashif
Kashif
ML Engineer at AMEX