--- name: postgresql-pgvector description: PostgreSQL 16 with pgvector extension for AI embeddings and semantic search parent-skill: moai-platform-supabase version: 1.0.0 updated: 2026-01-06 --- # PostgreSQL 16 + pgvector Module ## Overview PostgreSQL 16 with pgvector extension enables AI-powered semantic search through vector embeddings storage and similarity search operations. ## Extension Setup Enable required extensions for vector operations: ```sql -- Enable required extensions CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS vector; ``` ## Embeddings Table Schema Create a table optimized for storing AI embeddings: ```sql CREATE TABLE documents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), content TEXT NOT NULL, embedding vector(1536), -- OpenAI ada-002 dimensions metadata JSONB DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT NOW() ); ``` ### Common Embedding Dimensions - OpenAI ada-002: 1536 dimensions - OpenAI text-embedding-3-small: 1536 dimensions - OpenAI text-embedding-3-large: 3072 dimensions - Cohere embed-english-v3.0: 1024 dimensions - Google PaLM: 768 dimensions ## Index Strategies ### HNSW Index (Recommended) HNSW (Hierarchical Navigable Small World) provides fast approximate nearest neighbor search: ```sql CREATE INDEX idx_documents_embedding ON documents USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64); ``` Parameters: - m: Maximum number of connections per layer (default 16, higher = more accurate but slower) - ef_construction: Size of dynamic candidate list during construction (default 64) ### IVFFlat Index (Large Datasets) IVFFlat is better for datasets with millions of rows: ```sql CREATE INDEX idx_documents_ivf ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100); ``` Guidelines for lists parameter: - Less than 1M rows: lists = rows / 1000 - More than 1M rows: lists = sqrt(rows) ## Distance Operations Available distance operators: - `<->` - Euclidean distance (L2) - `<#>` - Negative inner product - `<=>` - Cosine distance For normalized embeddings, cosine distance is recommended. ## Semantic Search Function Basic semantic search with threshold and limit: ```sql CREATE OR REPLACE FUNCTION search_documents( query_embedding vector(1536), match_threshold FLOAT DEFAULT 0.8, match_count INT DEFAULT 10 ) RETURNS TABLE (id UUID, content TEXT, similarity FLOAT) LANGUAGE plpgsql AS $$ BEGIN RETURN QUERY SELECT d.id, d.content, 1 - (d.embedding <=> query_embedding) AS similarity FROM documents d WHERE 1 - (d.embedding <=> query_embedding) > match_threshold ORDER BY d.embedding <=> query_embedding LIMIT match_count; END; $$; ``` ### Usage Example ```sql SELECT * FROM search_documents( '[0.1, 0.2, ...]'::vector(1536), 0.75, 20 ); ``` ## Hybrid Search Combine vector similarity with full-text search for better results: ```sql CREATE OR REPLACE FUNCTION hybrid_search( query_text TEXT, query_embedding vector(1536), match_count INT DEFAULT 10, full_text_weight FLOAT DEFAULT 0.3, semantic_weight FLOAT DEFAULT 0.7 ) RETURNS TABLE (id UUID, content TEXT, score FLOAT) AS $$ BEGIN RETURN QUERY WITH semantic AS ( SELECT e.id, e.content, 1 - (e.embedding <=> query_embedding) AS similarity FROM documents e ORDER BY e.embedding <=> query_embedding LIMIT match_count * 2 ), full_text AS ( SELECT e.id, e.content, ts_rank(to_tsvector('english', e.content), plainto_tsquery('english', query_text)) AS rank FROM documents e WHERE to_tsvector('english', e.content) @@ plainto_tsquery('english', query_text) LIMIT match_count * 2 ) SELECT COALESCE(s.id, f.id), COALESCE(s.content, f.content), (COALESCE(s.similarity, 0) * semantic_weight + COALESCE(f.rank, 0) * full_text_weight) FROM semantic s FULL OUTER JOIN full_text f ON s.id = f.id ORDER BY 3 DESC LIMIT match_count; END; $$ LANGUAGE plpgsql; ``` ## Full-Text Search Index Add GIN index for efficient full-text search: ```sql CREATE INDEX idx_documents_content_fts ON documents USING gin(to_tsvector('english', content)); ``` ## Performance Optimization ### Query Performance Set appropriate ef_search for HNSW queries: ```sql SET hnsw.ef_search = 100; -- Higher = more accurate, slower ``` ### Batch Insertions Use COPY or multi-row INSERT for bulk embeddings: ```sql INSERT INTO documents (content, embedding, metadata) VALUES ('Content 1', '[...]'::vector(1536), '{"source": "doc1"}'), ('Content 2', '[...]'::vector(1536), '{"source": "doc2"}'), ('Content 3', '[...]'::vector(1536), '{"source": "doc3"}'); ``` ### Index Maintenance Reindex after large bulk insertions: ```sql REINDEX INDEX CONCURRENTLY idx_documents_embedding; ``` ## Metadata Filtering Combine vector search with JSONB metadata filters: ```sql CREATE OR REPLACE FUNCTION search_with_filters( query_embedding vector(1536), filter_metadata JSONB, match_count INT DEFAULT 10 ) RETURNS TABLE (id UUID, content TEXT, similarity FLOAT) AS $$ BEGIN RETURN QUERY SELECT d.id, d.content, 1 - (d.embedding <=> query_embedding) AS similarity FROM documents d WHERE d.metadata @> filter_metadata ORDER BY d.embedding <=> query_embedding LIMIT match_count; END; $$; ``` ### Usage with Filters ```sql SELECT * FROM search_with_filters( '[0.1, 0.2, ...]'::vector(1536), '{"category": "technical", "language": "en"}'::jsonb, 10 ); ``` ## Context7 Query Examples For latest pgvector documentation: Topic: "pgvector extension indexes hnsw ivfflat" Topic: "vector similarity search operators" Topic: "postgresql full-text search tsvector" --- Related Modules: - row-level-security.md - Secure vector data access - typescript-patterns.md - Client-side search implementation