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
.binCrash 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_indexgrowthCosine / L2 / Inner Product support
CRUD helpers:
get,deleteandupsertCrash recovery via oplog replay
Query results with both
scoreand 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
efimproves 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
efparameter 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 (
whereclauses 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)
