SereneDB
> use case

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.

ivf · nprobe = 1 · probe the nearest cluster
query vector[1.0, 0.0, 0.0]
▼ nearest centroid
accountprobed · 4
billingskipped · 1
connectivityskipped · 1
▼ exact distance inside the cluster
1Reset your password0.0002How to reset your password0.0003Recover a locked account0.141
Illustration with nprobe = 1: the query vector [1.0, 0.0, 0.0] is compared with three cluster centroids and only the nearest, account, is scanned. Exact L2 distance inside it ranks two password articles at 0.000 and Recover a locked account at 0.141.
> overview

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.

l2 distance to [1.0, 0.0, 0.0]
Reset your password[1.0, 0.0, 0.0]0.000
Recover a locked account[0.9, 0.1, 0.0]0.141
Change your payment method[0.0, 1.0, 0.0]1.414
Fix a dropped connection[0.0, 0.0, 1.0]1.414
axes · account · billing · connectivity
Four articles with hand-written three-dimensional embeddings. L2 distance to [1.0, 0.0, 0.0]: Reset your password 0.000, Recover a locked account 0.141, Change your payment method and Fix a dropped connection 1.414.
> the problem

Why Vector Search Needs an ANN Index

01 · exact

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.

02 · ann

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.

03 · recall

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.

> architecture

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.

table
articles
id INTEGER title VARCHAR emb FLOAT[3]
one inverted index
articles_idx
emb ivf → ann title → verbatim id → verbatim
three query forms
kNN
ORDER BY emb <-> $q LIMIT k
Range
WHERE emb <-> $q < r
Filtered ANN
WHERE @@ … ORDER BY <->
One inverted index, articles_idx, holds the emb column as IVF and id and title verbatim. kNN, range search and filtered ANN all read that index.

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.

sql
emb <-> $q   -- ivf (metric = 'l2')
emb <=> $q   -- ivf (metric = 'cosine')
emb <#> $q   -- ivf (metric = 'ip')
emb <+> $q   -- ivf (metric = 'l1')
Each distance operator is one metric and is accelerated only by an IVF index built with that metric.
filter + rank

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.

sql
SELECT id, name FROM catalog_idx
WHERE  category @@ 'footwear'
ORDER  BY emb <-> [1.0, 0.0, 0.0]::FLOAT[3]
LIMIT  2;
Rows in the footwear category, ordered by L2 distance to the query vector.
ai_embed()

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.

sql
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;
ai_embed computes an embedding per row at write time and embeds the query text at search time.

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.

sql
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;
A view over Parquet on S3 casts the embeddings to FLOAT[1536]; the index on it is refreshed with REINDEX.
> build it

Step-by-Step: A Vector Search Example in 5 Minutes

01

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.

sql
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'));
Step 1: Create the table and the IVF index.
02

Insert the vectors

Three hand-written axes: account, billing, connectivity. The index is eventually consistent, so refresh before querying.

sql
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;
Step 2: Insert the vectors.
03

kNN — the k nearest vectors

Both password articles at distance 0, then Recover a locked account at 0.141.

sql
SELECT id, title FROM articles_idx
ORDER  BY emb <-> [1.0, 0.0, 0.0]::FLOAT[3]
LIMIT  3;
Step 3: kNN — the k nearest vectors.
04

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.

sql
SELECT id, title FROM articles_idx
WHERE  emb <-> [1.0, 0.0, 0.0]::FLOAT[3] < 0.2;
Step 4: Range search — everything within a radius.
05

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.

sql
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;
Step 5: More like this — a row as the probe.
> vector vs relational

Vector Database vs Relational Database

01 · vector 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.

02 · relational

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.

03 · both

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

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.

> trade-offs

When a Dedicated Vector Database Is the Better Choice

01 · index

You want a graph index

Qdrant's docs name HNSW as its dense vector index. SereneDB documents one ANN index, IVF.

02 · scale-out

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.

03 · evidence

You need sparse vectors or recall numbers

Pinecone and Qdrant index sparse vectors. SereneDB documents no sparse-vector index, and our vector benchmarks are still preliminary: early recall-vs-QPS runs against Qdrant, no final numbers yet.

> faq

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

Get Started with SereneDB

One binary, one table, one IVF index: five statements from an empty database to nearest neighbors.