Vector Database
We have chunks and embeddings - but where do we store millions of vectors so that top-K search runs in milliseconds? That's what vector databases solve.
An embedding is just an array of floats. A thousand embeddings are a small JSON file. A million embeddings are roughly 4 GB, and you still want to find the nearest neighbour in milliseconds. That's the territory of vector databases.
What a vector DB has to do
- Store high-dimensional vectors in volume
- Top-K nearest-neighbour search - given a query vector, find the K most similar stored vectors, e.g. by cosine distance
- Filter on metadata - "only chunks with
locale='en'", "only chunks from documents authored in 2025" - Index all of that so it stays fast - non-negotiable at scale
The options
There are dedicated vector DBs (Qdrant, Weaviate, Pinecone, Milvus, Chroma) and extensions over existing databases (pgvector for Postgres, Lucene plugins for Elasticsearch, Redis Vector). Both paths are valid.
For this project - and for many smaller setups - pgvector is the most honest pick: Postgres is already there, schema management is familiar, and you keep embeddings transactionally consistent with your domain data.
At ten thousand chunks pgvector with an HNSW index is plenty fast. The case for switching only kicks in around many millions of vectors or under very high write load.
What we need next to the vector
A chunk vector by itself is useless - after the search you want to know:
- Which document the chunk came from (
document_id,slug,title) - Which section? Heading path, page number, position
- Which locale? For filtering German vs. English answers
- Which strategy? So you can A/B chunking strategies later
Our schema looks like this (simplified):
rag_chunks (id, document_id, chunk_index, strategy, content, metadata, content_tsv)
rag_embeddings (id, chunk_id, model, vector_768, vector_1024, vector_384)
rag_documents (id, slug, locale, title, doc_kind, source_path)
Three vector columns side by side - one per tested model, each with its own dimensionality and its own HNSW index.
Freshness and updates
When a document changes, its chunks and embeddings have to go stale. In practice: re-chunk, re-embed, drop the old rows. Idempotent seeders help - they detect what's already there and only replace what actually changed. That's what the rag-seeder container does in this project.
A full update pipeline (document hashing, diffs, hot reload) stays out of scope for v1. Here it's enough: "corpus committed → run the seeder again".
Discussion· no posts yet
Our comment agent reads every new post, says thanks or recommends related content.
Be the first voice - what do you think?
Sign in to join the discussion.
Sign in