pgvector / vector search in Postgres — Crash Course — Practical Guide (Oct 9, 2025)
pgvector / Vector Search in Postgres — Crash Course
Level: Intermediate
As of October 2025, vector search is a common technique in applications like recommendation systems, semantic search, and NLP-driven platforms. PostgreSQL, especially with the pgvector extension, provides robust support for storing and querying vector embeddings directly in the database. This enables developers to incorporate AI-powered similarities without relying fully on external search engines.
Prerequisites
Before you begin, ensure you have the following:
- PostgreSQL version 14 or higher installed (pgvector supports from PostgreSQL 9.6 but newer versions have better indexing and performance features).
- The
pgvectorextension installed and enabled. As of October 2025, pgvector is a stable, widely adopted extension available on GitHub. - A basic understanding of SQL, vector embeddings (typically floating-point arrays), and nearest neighbour search.
- If your vectors come from an ML model, access to embeddings generated from frameworks such as OpenAI, Hugging Face, or your own model.
Hands-on Steps
This section guides you through setting up vector search inside PostgreSQL with pgvector.
1. Install and enable pgvector
If using a Debian/Ubuntu system, pgvector may be installable via package managers. Alternatively, use:
# Using psql CLI to install extension (Postgres 14+)
CREATE EXTENSION IF NOT EXISTS vector;
If pgvector is not pre-installed, compile from source as per the official instructions.
2. Create a table to hold vectors
Create a table that stores document IDs and their embedding vectors. Vectors are declared as type vector with a fixed dimension.
CREATE TABLE items (
id serial PRIMARY KEY,
description text,
embedding vector(1536) -- example for OpenAI text-embedding-ada-002 vectors
);
Adjust the dimension to match your embedding provider or model—commonly 128, 384, or 1536 dimensions.
3. Insert data with embeddings
Assuming you have your embeddings ready in your application code, you can insert like so:
INSERT INTO items (description, embedding)
VALUES ('Example item', ARRAY[
0.12, -0.03, 0.47, ... -- 1536 float values
]::vector);
Note the explicit cast to vector.
4. Query with vector similarity
There are three similarity operators available, corresponding to different distance metrics:
<->: Euclidean (L2) distance<#>: Cosine distance<@>: Inner product distance
Example to find the top 5 most similar items to a query embedding:
SELECT id, description, embedding <#> '[0.15, -0.02, 0.56, ...]'::vector AS distance
FROM items
ORDER BY embedding <#> '[0.15, -0.02, 0.56, ...]'::vector
LIMIT 5;
5. Indexing for performance
Vector search scales poorly without an index for large datasets. pgvector supports ivfflat (inverted file flat) indexing to speed up approximate nearest neighbour search.
Create an index as follows (choose lists based on dataset size, commonly 100-1000):
CREATE INDEX ON items USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
Run ANALYSE items; afterwards. Note that ivfflat indexes require the table to be vacuumed and analysed regularly.
When querying, you must add the ivfflat operator class in the ORDER BY to take advantage of this index efficiently.
Common Pitfalls
- Dimension mismatch: The
vectorcolumn must have a fixed dimension, which must align with all inserted vectors. Mismatched vector sizes will fail insert/update. - Not indexing for large datasets: Sequential scans on large tables are slow. Implement
ivfflator consider external ANN alternatives for millions of vectors. - Improper index operator class: Use the correct operator class for your distance metric:
vector_cosine_ops,vector_l2_ops, orvector_ip_ops. - Forgetting to
ANALYSE: After bulk inserts or updates, runANALYSEto update PostgreSQL’s planner statistics. - Over-reliance on exact search: pgvector’s
ivfflatindex provides approximate nearest neighbour search. For strict exact searches, performance will degrade on large data. - Ignoring array formatting: When inserting vectors as arrays, ensure correct casting to
vectortype, and avoid trailing commas or mismatched brackets.
Validation
Validate your setup with simple tests:
- Insert a known vector and query it directly for similarity. The nearest match should return the inserted row.
- Test differing similarity operators to evaluate which metric best fits your use case. E.g., cosine distance is popular for text embeddings.
- Measure performance impact with and without the
ivfflatindex on a sample of your real dataset.
-- Insert test data
INSERT INTO items (description, embedding)
VALUES ('test vector', ARRAY[0.1, 0.2, 0.3, ..., 0.0]::vector);
-- Query nearest neighbour by cosine distance
SELECT id, description
FROM items
ORDER BY embedding <#> '[0.1, 0.2, 0.3, ..., 0.0]'::vector
LIMIT 1;
Checklist / TL;DR
- Install
pgvectorextension compatible with your PostgreSQL version (pref Postgres 14+ for best support). - Create tables with a fixed-dimension
vectorcolumn matching the embedding vector size. - Insert vectors carefully, casting arrays to
vectortype. - Choose similarity metric wisely: cosine distance for semantic text embeddings, Euclidean for raw numeric vectors.
- Build
ivfflatindexes with the correct operator class for approximate nearest neighbour search to improve query speed. - Remember to ANALYSE the table and keep vacuuming up to date for accurate query plans.
- Test on realistic datasets to balance accuracy and performance.
- Consider alternatives like dedicated vector search engines (e.g. Pinecone, Faiss) if PostgreSQL performance doesn’t meet scale or latency needs.
When to choose pgvector vs alternatives
pgvector fits well when you want a single datastore to hold relational and vector data, with transactional guarantees, and your dataset size is moderate (up to millions of vectors, depending on hardware).
Dedicated vector search engines (like Pinecone, Weaviate, or Faiss) provide specialised optimisations, scaling to billions of vectors and very low latency but add external dependencies and potential consistency trade-offs.
Consider your data architecture, consistency requirements, and query latency expectations before deciding.