Part 3: Setting Up pgvector with PostgreSQL

← Part 2: Vector Embeddings | Part 4: TypeScript Implementation β†’

The $400/Month Bill That Made Me Switch

I was running a documentation search system on Pinecone. Everything worked great, until the bill arrived.

Monthly costs:

  • Pinecone: $359/month (2M vectors, p1 pod)

  • OpenAI embeddings: $18/month

  • Total: $377/month for a simple internal docs search

Then I saw this tweet: "Just migrated 2M vectors from Pinecone to pgvector. Monthly cost: $377 β†’ $18. Same performance."

I was skeptical. Could PostgreSQL really replace a specialized vector database?

I tried it. Migration took 2 hours. Results:

  • Monthly cost: $377 β†’ $18 (95% reduction)

  • Query latency: 89ms β†’ 76ms (faster!)

  • Maintenance complexity: Much simpler (one database, not two)

That $359/month saving paid for a lot of coffee. β˜•

This article shows you exactly how to set up pgvector, create vector columns, and implement fast indexes.

Installing PostgreSQL with pgvector

macOS (Homebrew)

Linux (Ubuntu/Debian)

Docker (Easiest for Development)

Docker Compose (My Production Setup)

Enabling and Verifying pgvector Extension

If you see results, pgvector is working!

Creating Tables with Vector Columns

Basic Vector Column

Vector dimension must match your embedding model:

  • OpenAI text-embedding-3-small: 1536

  • OpenAI text-embedding-3-large: 3072

  • Sentence Transformers all-MiniLM-L6-v2: 384

Complete Schema with Metadata

Vector Column for Documentation

Vector Indexes: HNSW vs IVFFlat

Without indexes, vector search is slow (full table scan). Indexes make queries ~100x faster.

Hierarchical Navigable Small World - graph-based index.

HNSW Configuration:

HNSW Characteristics:

  • βœ… Better recall (more accurate results)

  • βœ… Faster queries

  • βœ… Good for high-dimensional vectors

  • ❌ Slower index build time

  • ❌ More memory usage

IVFFlat Index

Inverted File with Flat compression - clustering-based index.

IVFFlat Characteristics:

  • βœ… Faster index build

  • βœ… Lower memory usage

  • βœ… Good for millions of vectors

  • ❌ Lower recall (less accurate)

  • ❌ Slower queries than HNSW

Which Index Should You Use?

Use Case
Index Type
Configuration

< 1M vectors, accuracy critical

HNSW

m=16, ef_construction=64

High-dimensional (>1000)

HNSW

m=24, ef_construction=128

> 10M vectors, memory-constrained

IVFFlat

lists = rows / 1000

Development/testing

None

No index (fast writes, slow reads)

My default: HNSW with cosine distance for text embeddings.

Create Index After Loading Data

Prisma Schema for pgvector

Prisma doesn't natively support pgvector, but we can use Unsupported type and raw SQL:

Note: Prisma doesn't generate TypeScript types for Unsupported fields. Use raw queries for vector operations.

Migration

Or create manual migration:

Loading Embeddings into PostgreSQL

Method 2: Bulk Insert with COPY (Fastest)

Method 3: Transaction for Consistency

Testing Vector Queries

With TypeScript

Verify Index Usage

Performance Tuning

Set Work Memory for Index Building

Query Performance Tuning

Monitor Query Performance

Common Issues and Solutions

Issue: Index not being used

Issue: Slow index creation

Issue: Out of memory

Complete Setup Script

What's Next

In this article, you learned:

  • βœ… Installing PostgreSQL with pgvector (Docker, macOS, Linux)

  • βœ… Enabling pgvector extension

  • βœ… Creating tables with vector columns

  • βœ… HNSW vs IVFFlat indexes (and when to use each)

  • βœ… Prisma schema for vectors

  • βœ… Loading embeddings into PostgreSQL

  • βœ… Query performance tuning

Next: We'll build a complete TypeScript application with semantic search, hybrid queries, and production-ready error handling.


← Part 2: Vector Embeddings | Part 4: TypeScript Implementation β†’

Last updated