Vector Search Database with SereneDB
Vector search finds the rows whose vector embeddings sit closest to a query vector. In SereneDB that is one SQL statement — ORDER BY distance LIMIT k — served by an IVF index.
An open-source database with vector search, not a separate vector store: the embedding is a FLOAT[N] column beside your relational data, indexed with full-text and queried over the PostgreSQL wire protocol.
What Is Vector Search?
A machine learning model turns text, an image or a product into an embedding — a fixed-length vector of numbers — so that similar things land close together. Vector search, or vector similarity search, returns the stored vectors nearest to a query vector under a distance metric: Euclidean distance (L2), cosine, inner product or Manhattan (L1).
Exact nearest-neighbor search (brute-force kNN) compares the query with every vector. Approximate nearest neighbor (ANN) search compares it with a small fraction of them through an index and occasionally misses a true neighbor. Semantic search is the usual reason: embed the documents once, embed the question, rank by meaning rather than exact keywords. In the aside, Recover a locked account shares no word with Reset your password but sits on the same account axis and ranks second.
Why Vector Search Needs an ANN Index
How exact vector search works
One distance per stored vector per query: always right, fine for small tables, slow on large ones. SereneDB falls back to it when no vector search index matches the query.
The ANN algorithm: IVF
SereneDB's ANN index is IVF (inverted file): k-means splits the vectors into clusters at build time, and a query scans only the nearest clusters — approximate nearest neighbor search.
Recall is a dial, not a rebuild
sdb_nprobe, the clusters a kNN query scans, is a session setting (default 8). On l2 and ip indexes, quantization (sq8, sq4, pq, rabitq, chosen when the index is built) shrinks the stored vectors, and a rerank pass with exact distances, sized per session, recovers recall. Cosine and L1 indexes stay unquantized.
How SereneDB Runs Vector Search in SQL
A FLOAT[N] column declared with ivf (...) sits in the same inverted index as the table's text and verbatim columns, and every vector query is a plain SELECT against it.
Similarity search in SQL: the distance operators
Four distance operators cover four metrics. Each ivf column declares one — cosine for most text-embedding models — and ORDER BY … LIMIT k uses the index only when the operator matches; otherwise every row is scanned.
emb <-> $q -- ivf (metric = 'l2')
emb <=> $q -- ivf (metric = 'cosine')
emb <#> $q -- ivf (metric = 'ip')
emb <+> $q -- ivf (metric = 'l1')Filtered ANN and hybrid search
A category or full-text match goes in WHERE, the distance in ORDER BY. When both signals should rank, fuse them with hybrid search.
SELECT id, name FROM catalog_idx
WHERE category @@ 'footwear'
ORDER BY emb <-> [1.0, 0.0, 0.0]::FLOAT[3]
LIMIT 2;Embeddings from SQL
ai_embed() calls an embedding model on any OpenAI-compatible endpoint — OpenAI, Gemini, a local Ollama. Embed rows at write time, only the query at search time: each call is a network request.
INSERT INTO catalog
SELECT id, name,
ai_embed(name, 'all-minilm', 'local_ai')::FLOAT[384]
FROM fruits
WHERE name IS NOT NULL;
SELECT id, name FROM catalog_idx
ORDER BY embedding <=> ai_embed('tropical fruit',
'all-minilm', 'local_ai')::FLOAT[384]
LIMIT 3;Vector search over data you never loaded
Index a view over external data — Parquet, CSV, JSON or Iceberg on S3, or an attached PostgreSQL table. The rows stay put; the index is a snapshot refreshed by REINDEX or a reindex_interval. See search over a data lake.
CREATE VIEW chunks_v AS
SELECT id, body, emb::FLOAT[1536] AS emb
FROM read_parquet('s3://my-bucket/chunks/*.parquet');
CREATE INDEX chunks_idx ON chunks_v
USING inverted (id, emb ivf (metric = 'cosine'));
-- later, after new files land:
REINDEX INDEX chunks_idx;Step-by-Step: A Vector Search Example in 5 Minutes
Create the table and the IVF index
Rows and queries share the dimension N; the metric is the one required parameter. On a bulk load, index after loading — the clusters train on the data.
CREATE TABLE articles (id INTEGER PRIMARY KEY, title VARCHAR, emb FLOAT[3]);
CREATE INDEX articles_idx ON articles
USING inverted (id, title, emb ivf (metric = 'l2'));Insert the vectors
Three hand-written axes: account, billing, connectivity. The index is eventually consistent, so refresh before querying.
INSERT INTO articles VALUES
(1, 'Reset your password', [1.0, 0.0, 0.0]::FLOAT[3]),
(2, 'How to reset your password', [1.0, 0.0, 0.0]::FLOAT[3]),
(3, 'Recover a locked account', [0.9, 0.1, 0.0]::FLOAT[3]),
(4, 'Set up two-factor authentication', [0.8, 0.2, 0.0]::FLOAT[3]),
(5, 'Change your payment method', [0.0, 1.0, 0.0]::FLOAT[3]),
(6, 'Fix a dropped connection', [0.0, 0.0, 1.0]::FLOAT[3]);
VACUUM (REFRESH_TABLE) articles;kNN — the k nearest vectors
Both password articles at distance 0, then Recover a locked account at 0.141.
SELECT id, title FROM articles_idx
ORDER BY emb <-> [1.0, 0.0, 0.0]::FLOAT[3]
LIMIT 3;Range search — everything within a radius
The same three rows: nothing else is within 0.2. Add ORDER BY … LIMIT k for the closest k inside it.
SELECT id, title FROM articles_idx
WHERE emb <-> [1.0, 0.0, 0.0]::FLOAT[3] < 0.2;More like this — a row as the probe
Article 1's vector is the probe: its reworded copy at 0.000, then 0.141 and 0.283. On a large table, pass the vector as a literal so the index is used — similar documents.
SELECT id, title,
round((emb <-> (SELECT emb FROM articles WHERE id = 1))::numeric, 3) AS distance
FROM articles_idx WHERE id <> 1
ORDER BY distance, id LIMIT 3;Vector Database vs Relational Database
Built around the vector
A record is a vector plus a metadata payload behind the engine's own API. The source rows usually live in another database, with a sync job between.
Built around the row
Rows, joins and transactions, answered in SQL. Vector search takes three more pieces: a fixed-size vector column, distance functions and an approximate nearest neighbor index.
A SQL database with ANN
SereneDB has all three (FLOAT[N] columns, four distance operators, an IVF index), so vector ranking, column filters and GROUP BY run over the same tables.
Use Cases for Vector Search
Semantic search over text
Embed documents and questions with ai_embed(), rank by cosine distance. When exact terms matter too — SKUs, error codes — add BM25 and fuse the rankings with hybrid search.
More-like-this and near-duplicates
A row's own embedding is the probe for related articles. A self-join that keeps pairs inside a tiny radius flags the same article published twice.
Retrieval-augmented generation (RAG)
Chunks, embeddings and metadata in one table; langchain-serenedb implements LangChain's VectorStore over it. The RAG database page walks the pipeline.
When a Dedicated Vector Database Is the Better Choice
You want a graph index
Qdrant's docs name HNSW as its dense vector index. SereneDB documents one ANN index, IVF.
You need a cluster or a managed service
Pinecone runs as a managed service; Qdrant shards and replicates collections across a cluster. Open-source SereneDB runs on a single node, and its docs describe no managed cloud.
Frequently Asked Questions
SQL is a language. A database does vector search when it can store, compare and index embeddings; SereneDB does all three in SQL over the PostgreSQL wire protocol, as its own engine rather than an extension.
Traditional keyword search matches the query's words and ranks them with BM25. Vector search compares embeddings: same meaning in other words, but exact identifiers slip. Hybrid search runs both.
No. Semantic search is the goal — results by meaning — and vector search the usual mechanism, which also powers recommendation systems. In SereneDB it is ai_embed() plus an IVF index.
Finding the k closest vectors without comparing the query with every one. SereneDB's IVF index scans only the nearest clusters, trading a little recall for a large speed-up.
Often. SereneDB supports L2, cosine, inner product and L1, one per vector column; most text-embedding models are tuned for cosine, queried with <=>.
Accuracy here is recall. Raise sdb_nprobe (default 8) to scan more clusters; on a quantized l2 or ip index, raise sdb_rerank_factor (default 4) to widen the pool re-scored with exact distances. Both are session settings — no rebuild.
It is an open-source database with vector search, not a dedicated vector database: embeddings are one column type among others. It runs on a single node.
Get Started with SereneDB
One binary, one table, one IVF index: five statements from an empty database to nearest neighbors.