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

KashifKashif
4 min read

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 growth

  • Cosine / L2 / Inner Product support

  • CRUD helpers: get, delete and upsert

  • Crash recovery via oplog replay

  • Query results with both score and raw distance

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.

BackendBuild v/sQPSp50 (ms)p95 (ms)recall@10
NoPokeDB12.8k3.6k0.270.310.63
FAISS95.6k10.7k0.090.120.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)

0
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