Storing and querying vectors v0

Store vector data in a regular Postgres table, query it with ivfplus's distance operators, and build an index once you need one.

Storing vectors

Add a vector column to a table, specifying the number of dimensions:

CREATE TABLE items (
    id        bigserial PRIMARY KEY,
    content   text,
    embedding vector(768)
);

The dimension count must match the output of your embedding model. Common values:

Embedding modelDimensions
OpenAI text-embedding-3-small1536
OpenAI text-embedding-3-large3072
Google text-embedding-004768
Nomic Embed Text768

Insert a row with a vector value:

INSERT INTO items (content, embedding)
VALUES ('example text', '[...]');

Querying vectors

ivfplus supports the same distance operators as pgvector, through the matching opclass (see Access method and operator classes):

OperatorDistance metricUse case
<->L2 (Euclidean)General-purpose similarity, default for most embedding models
<=>CosineWhen vector magnitude varies, normalized embeddings
<#>Inner productMaximum inner product search

Find the 10 nearest neighbors by L2 distance:

SELECT id, content FROM items ORDER BY embedding <-> '[...]' LIMIT 10;

Building an index

Without an index, a query scans every row and returns exact results. To check whether that's still fast enough for your workload, run EXPLAIN (ANALYZE, BUFFERS) on your query as-is and compare its actual time against your latency budget. If it's within budget, skip the index for now; add one once it isn't.

Build an ivfplus index and set a starting probes value for your session:

CREATE INDEX ON items USING ivfplus (embedding vector_l2_ops) WITH (lists = 1000);

SET ivfplus.probes = 32;

Once the index exists, your queries don't change — ivfplus only changes which rows the planner considers first. See Quickstart for a complete walkthrough with example data, and Tuning and sizing for how to choose lists and probes for your table.

Maintaining the index

Keep an eye on where the index lives in storage and how it handles writes after the initial build.

Storage

For best query performance, keep the index in memory (shared_buffers plus the OS page cache). If it's too large to fit, use fast NVMe storage to minimize the latency of index page reads.

Vacuuming and REINDEX

Plain VACUUM doesn't reclaim space in an ivfplus index — deleted or updated rows tombstone their lane, and only REINDEX repacks the index and reuses that space. See Writes, VACUUM, and REINDEX for the full behavior and how to monitor drift with check_ivfplus_index_health().