Our services
If you can think it, we can make it brainsoft.
If you can think it, we can make it brainsoft.
Written By: BrainSoft In AI & Research
Somebody on the team reads that the new search feature should be semantic, and suddenly there is a vector database in the backlog. Nobody has run one before. The docs assume you already know what an embedding is, what an index type means for recall, and why your query returns eight near-identical results. It is not that hard, but the order of operations matters.
Start with a local instance of a small engine, embed a few hundred real documents, store the vectors with their metadata, and query them. Do that once end to end before you pick a hosted provider or a production schema, and the rest of the decisions get much easier.
An embedding model turns a piece of text into a list of numbers, usually a few hundred to a couple of thousand of them. Texts with similar meaning end up with vectors that sit close together. A vector database stores those lists and answers one question fast: which stored vectors are nearest to this one? That is the whole product. Everything else, filtering, replication, backups, is normal database work layered on top.
The consequence is that you cannot eyeball the data. A wrong row in Postgres looks wrong. A bad embedding looks like a plausible float array and silently returns mediocre results. Budget time for evaluating retrieval quality, not just for getting the service to start.
A minimal pgvector setup looks like this. The dimension has to match your model exactly, so check it first.
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE chunks (
id bigserial PRIMARY KEY,
doc_id text NOT NULL,
content text NOT NULL,
embedding vector(1536)
);
CREATE INDEX ON chunks USING hnsw (embedding vector_cosine_ops);
Inserting is plain SQL. Pass the vector as a string literal in the format pgvector expects.
INSERT INTO chunks (doc_id, content, embedding)
VALUES ('faq-12', 'Reset your password from the account page.', '[0.021,-0.113,0.447,...]');
And the query orders by distance, so smaller is closer with cosine distance. Ask for more rows than you plan to show; you will want to filter and deduplicate afterwards.
SELECT doc_id, content, embedding <=> $1 AS distance
FROM chunks
ORDER BY embedding <=> $1
LIMIT 20;
If you would rather have someone sit with the team for the first iteration, that is the kind of work we do through our services.
The signals are concrete: index builds no longer fit in a maintenance window, p95 query latency drifts past your budget, or you need replication and snapshot restores you do not want to hand-roll. At that point move to a managed instance of the same engine rather than switching engines mid-project. Keeping the client code and query shape identical makes the migration a configuration change.
Often not. pgvector handles a few million chunks well and keeps your backups, access control, and migrations in one place. Move to a dedicated engine when filtering gets complex, the corpus grows past what one Postgres instance should hold, or latency targets demand it.
You pay per token for hosted embedding APIs, once per chunk plus once per query. The one-off indexing pass is the large bill; queries are cheap. Local open models avoid per-token cost but need GPU or patient CPU time, so measure on your own corpus before committing.
Yes, but it is a full re-index. Vectors from different models are not comparable, so keep the model name and dimension in config, version your collection, and plan to rebuild it rather than patch it in place.