Vector Search Examples and Use Cases¶
Version: v7.0 Status: Production Ready
Overview¶
This document provides production-ready examples for common vector search use cases including semantic search, image similarity, recommendation systems, RAG pipelines, and hybrid search.
1. Semantic Search¶
Find documents based on meaning rather than exact keyword matches.
Schema Setup¶
-- Create documents table
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title VARCHAR(500),
content TEXT,
author VARCHAR(100),
published_date DATE,
category VARCHAR(50),
embedding VECTOR(768) -- BERT-base dimension
);
-- Create HNSW index for semantic search
CREATE INDEX idx_articles_semantic ON articles
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
-- Create supporting indexes
CREATE INDEX idx_articles_category ON articles(category);
CREATE INDEX idx_articles_date ON articles(published_date);
Insert Documents with Embeddings¶
-- Insert article with pre-computed embedding
INSERT INTO articles (title, content, author, published_date, category, embedding)
VALUES (
'Introduction to Machine Learning',
'Machine learning is a subset of artificial intelligence that enables systems to learn and improve from experience without being explicitly programmed...',
'Dr. Jane Smith',
'2024-01-15',
'Technology',
'[0.023, -0.041, 0.087, ...]'::VECTOR -- 768 dimensions
);
-- Batch insert from staging table
INSERT INTO articles (title, content, author, published_date, category, embedding)
SELECT
title,
content,
author,
published_date,
category,
generate_embedding(content, 'bert-base') -- Built-in embedding function
FROM staging_articles;
Semantic Search Queries¶
-- Basic semantic search
SELECT
id,
title,
LEFT(content, 200) AS snippet,
embedding <=> query_embedding AS relevance
FROM articles
ORDER BY embedding <=> '[0.025, -0.041, ...]'::VECTOR
LIMIT 10;
-- Semantic search with category filter
SELECT id, title, relevance
FROM (
SELECT
id,
title,
embedding <=> query_embedding AS relevance
FROM articles
WHERE category = 'Technology'
) sub
WHERE relevance < 0.5 -- Similarity threshold
ORDER BY relevance
LIMIT 10;
-- Semantic search with date range
SELECT
id,
title,
published_date,
embedding <=> query_embedding AS relevance
FROM articles
WHERE published_date BETWEEN '2024-01-01' AND '2024-12-31'
ORDER BY embedding <=> query_embedding
LIMIT 20;
2. Image Similarity Search¶
Find visually similar images using CNN embeddings.
Schema Setup¶
-- Create images table
CREATE TABLE images (
id SERIAL PRIMARY KEY,
filename VARCHAR(255),
file_path TEXT,
file_size BIGINT,
width INT,
height INT,
format VARCHAR(10),
tags TEXT[],
uploaded_at TIMESTAMP DEFAULT NOW(),
embedding VECTOR(2048) -- ResNet-50 dimension
);
-- Create L2 index (better for image embeddings)
CREATE INDEX idx_images_visual ON images
USING hnsw (embedding vector_l2_ops)
WITH (m = 24, ef_construction = 300);
-- Create GIN index for tag search
CREATE INDEX idx_images_tags ON images USING gin(tags);
Insert Images¶
-- Insert image with embedding
INSERT INTO images (filename, file_path, width, height, format, tags, embedding)
VALUES (
'sunset_beach.jpg',
'/uploads/2024/01/sunset_beach.jpg',
1920,
1080,
'jpeg',
ARRAY['sunset', 'beach', 'nature', 'orange'],
'[0.234, 0.567, -0.123, ...]'::VECTOR -- ResNet-50 embedding
);
Image Similarity Queries¶
-- Find similar images to a reference image
SELECT
i2.id,
i2.filename,
i2.tags,
i1.embedding <-> i2.embedding AS visual_distance
FROM images i1
JOIN images i2 ON i1.id != i2.id
WHERE i1.id = 12345 -- Reference image ID
ORDER BY i1.embedding <-> i2.embedding
LIMIT 20;
-- Find similar images by uploading query image
SELECT
id,
filename,
file_path,
embedding <-> query_embedding AS distance
FROM images
ORDER BY embedding <-> '[0.234, 0.567, ...]'::VECTOR
LIMIT 12;
-- Reverse image search with tag filter
SELECT
id,
filename,
tags,
embedding <-> query_embedding AS distance
FROM images
WHERE 'nature' = ANY(tags) -- Must have 'nature' tag
ORDER BY embedding <-> query_embedding
LIMIT 10;
-- Find images similar to multiple references (multi-query)
WITH reference_images AS (
SELECT embedding
FROM images
WHERE id IN (100, 200, 300) -- Multiple reference images
)
SELECT DISTINCT ON (i.id)
i.id,
i.filename,
MIN(r.embedding <-> i.embedding) AS min_distance
FROM images i
CROSS JOIN reference_images r
WHERE i.id NOT IN (100, 200, 300)
GROUP BY i.id, i.filename
ORDER BY i.id, min_distance
LIMIT 20;
3. Recommendation System¶
Build a product recommendation engine using collaborative filtering embeddings.
Schema Setup¶
-- Products table
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(500),
description TEXT,
category VARCHAR(100),
price DECIMAL(10, 2),
rating DECIMAL(3, 2),
review_count INT,
in_stock BOOLEAN DEFAULT true,
embedding VECTOR(384) -- Product embedding
);
-- User preferences table
CREATE TABLE user_preferences (
user_id INT PRIMARY KEY,
preference_embedding VECTOR(384), -- Aggregated user taste
last_updated TIMESTAMP DEFAULT NOW()
);
-- Purchase history for collaborative filtering
CREATE TABLE purchases (
id SERIAL PRIMARY KEY,
user_id INT,
product_id INT,
purchased_at TIMESTAMP DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_products_rec ON products
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
CREATE INDEX idx_user_prefs ON user_preferences
USING hnsw (preference_embedding vector_cosine_ops);
Recommendation Queries¶
-- Content-based: Similar products to one the user liked
SELECT
p.id,
p.name,
p.price,
p.rating,
ref.embedding <=> p.embedding AS similarity
FROM products ref
JOIN products p ON ref.id != p.id
WHERE ref.id = 12345 -- Product user liked
AND p.in_stock = true
AND p.category = ref.category
ORDER BY ref.embedding <=> p.embedding
LIMIT 10;
-- Collaborative filtering: Products for user based on taste profile
SELECT
p.id,
p.name,
p.price,
p.rating,
u.preference_embedding <=> p.embedding AS match_score
FROM user_preferences u
JOIN products p ON p.in_stock = true
WHERE u.user_id = 98765
ORDER BY u.preference_embedding <=> p.embedding
LIMIT 20;
-- Hybrid recommendation: Similar products + popularity boost
SELECT
p.id,
p.name,
p.price,
p.rating,
(0.7 * (1 - (ref.embedding <=> p.embedding)) +
0.2 * (p.rating / 5.0) +
0.1 * LEAST(p.review_count / 1000.0, 1.0)) AS hybrid_score
FROM products ref
JOIN products p ON ref.id != p.id
WHERE ref.id = 12345
AND p.in_stock = true
ORDER BY hybrid_score DESC
LIMIT 10;
-- "Customers who bought this also bought" (collaborative)
WITH buyers AS (
SELECT DISTINCT user_id
FROM purchases
WHERE product_id = 12345
),
co_purchases AS (
SELECT product_id, COUNT(*) AS purchase_count
FROM purchases
WHERE user_id IN (SELECT user_id FROM buyers)
AND product_id != 12345
GROUP BY product_id
)
SELECT
p.id,
p.name,
p.price,
cp.purchase_count,
ref.embedding <=> p.embedding AS similarity
FROM co_purchases cp
JOIN products p ON cp.product_id = p.id
JOIN products ref ON ref.id = 12345
WHERE p.in_stock = true
ORDER BY (0.6 * (1 - (ref.embedding <=> p.embedding)) +
0.4 * (cp.purchase_count::float / (SELECT MAX(purchase_count) FROM co_purchases))) DESC
LIMIT 10;
4. RAG Pipeline (Retrieval-Augmented Generation)¶
Build a knowledge base for LLM-powered question answering.
Schema Setup¶
-- Knowledge base chunks
CREATE TABLE knowledge_chunks (
id SERIAL PRIMARY KEY,
document_id INT,
chunk_index INT,
content TEXT,
token_count INT,
source_file VARCHAR(500),
page_number INT,
created_at TIMESTAMP DEFAULT NOW(),
embedding VECTOR(1536) -- OpenAI text-embedding-ada-002
);
-- Source documents
CREATE TABLE source_documents (
id SERIAL PRIMARY KEY,
title VARCHAR(500),
file_path TEXT,
doc_type VARCHAR(50), -- pdf, docx, md, html
upload_date DATE,
metadata JSONB
);
-- High-accuracy index for RAG
CREATE INDEX idx_knowledge_rag ON knowledge_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 32, ef_construction = 400);
-- Full-text search index for hybrid RAG
CREATE INDEX idx_knowledge_fts ON knowledge_chunks
USING gin(to_tsvector('english', content));
RAG Retrieval Queries¶
-- Basic RAG retrieval
SELECT
kc.id,
kc.content,
sd.title AS source_title,
kc.page_number,
kc.embedding <=> query_embedding AS relevance
FROM knowledge_chunks kc
JOIN source_documents sd ON kc.document_id = sd.id
ORDER BY kc.embedding <=> '[0.023, -0.041, ...]'::VECTOR
LIMIT 5;
-- Hybrid RAG: Vector + keyword search
WITH vector_results AS (
SELECT id, content, document_id,
embedding <=> query_embedding AS vector_score
FROM knowledge_chunks
ORDER BY embedding <=> query_embedding
LIMIT 20
),
keyword_results AS (
SELECT id, content, document_id,
ts_rank(to_tsvector('english', content),
plainto_tsquery('english', 'machine learning')) AS keyword_score
FROM knowledge_chunks
WHERE to_tsvector('english', content) @@
plainto_tsquery('english', 'machine learning')
LIMIT 20
)
SELECT
COALESCE(v.id, k.id) AS id,
COALESCE(v.content, k.content) AS content,
COALESCE(v.document_id, k.document_id) AS document_id,
(0.7 * (1 - COALESCE(v.vector_score, 1)) +
0.3 * COALESCE(k.keyword_score, 0)) AS hybrid_score
FROM vector_results v
FULL OUTER JOIN keyword_results k ON v.id = k.id
ORDER BY hybrid_score DESC
LIMIT 5;
-- Context window management (stay within token budget)
WITH ranked_chunks AS (
SELECT
id,
content,
token_count,
embedding <=> query_embedding AS relevance,
SUM(token_count) OVER (ORDER BY embedding <=> query_embedding) AS cumulative_tokens
FROM knowledge_chunks
ORDER BY embedding <=> query_embedding
)
SELECT id, content, relevance
FROM ranked_chunks
WHERE cumulative_tokens <= 3000 -- Max context tokens
ORDER BY relevance;
-- Multi-source RAG with source attribution
SELECT
kc.content,
sd.title AS source,
sd.doc_type,
kc.page_number,
kc.embedding <=> query_embedding AS relevance
FROM knowledge_chunks kc
JOIN source_documents sd ON kc.document_id = sd.id
WHERE sd.doc_type IN ('pdf', 'docx') -- Only official documents
ORDER BY kc.embedding <=> query_embedding
LIMIT 5;
Building RAG Context¶
-- Create function to build RAG context
CREATE OR REPLACE FUNCTION build_rag_context(
query_text TEXT,
max_tokens INT DEFAULT 3000
)
RETURNS TABLE (context TEXT, sources TEXT[]) AS $$
DECLARE
query_embedding VECTOR(1536);
BEGIN
-- Generate query embedding (pseudo-code)
query_embedding := generate_embedding(query_text, 'text-embedding-ada-002');
RETURN QUERY
WITH chunks AS (
SELECT
kc.content,
sd.title,
kc.token_count,
SUM(kc.token_count) OVER (ORDER BY kc.embedding <=> query_embedding) AS cum_tokens
FROM knowledge_chunks kc
JOIN source_documents sd ON kc.document_id = sd.id
ORDER BY kc.embedding <=> query_embedding
)
SELECT
string_agg(content, E'\n\n---\n\n') AS context,
array_agg(DISTINCT title) AS sources
FROM chunks
WHERE cum_tokens <= max_tokens;
END;
$$ LANGUAGE plpgsql;
-- Usage
SELECT * FROM build_rag_context('What are the best practices for vector indexing?');
5. Hybrid Search (Vector + Text)¶
Combine semantic similarity with traditional full-text search for best results.
Schema Setup¶
-- Documents with both vector and text indexes
CREATE TABLE searchable_docs (
id SERIAL PRIMARY KEY,
title VARCHAR(500),
content TEXT,
category VARCHAR(100),
created_at TIMESTAMP DEFAULT NOW(),
embedding VECTOR(768)
);
-- Vector index
CREATE INDEX idx_docs_vector ON searchable_docs
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
-- Full-text search index
CREATE INDEX idx_docs_fts ON searchable_docs
USING gin(to_tsvector('english', title || ' ' || content));
Hybrid Search Queries¶
-- Reciprocal Rank Fusion (RRF) hybrid search
WITH vector_ranked AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY embedding <=> query_embedding) AS rank
FROM searchable_docs
ORDER BY embedding <=> query_embedding
LIMIT 100
),
keyword_ranked AS (
SELECT id, ROW_NUMBER() OVER (ORDER BY ts_rank(to_tsvector('english', content),
query) DESC) AS rank
FROM searchable_docs
WHERE to_tsvector('english', content) @@ plainto_tsquery('english', 'machine learning')
LIMIT 100
)
SELECT
d.id,
d.title,
LEFT(d.content, 200) AS snippet,
(1.0 / (60 + COALESCE(v.rank, 1000)) +
1.0 / (60 + COALESCE(k.rank, 1000))) AS rrf_score
FROM searchable_docs d
LEFT JOIN vector_ranked v ON d.id = v.id
LEFT JOIN keyword_ranked k ON d.id = k.id
WHERE v.id IS NOT NULL OR k.id IS NOT NULL
ORDER BY rrf_score DESC
LIMIT 10;
-- Weighted hybrid search
SELECT
d.id,
d.title,
(0.7 * (1 - (d.embedding <=> query_embedding)) +
0.3 * COALESCE(ts_rank(to_tsvector('english', d.content),
plainto_tsquery('english', 'neural networks')), 0)) AS hybrid_score
FROM searchable_docs d
WHERE d.embedding <=> query_embedding < 0.8
OR to_tsvector('english', d.content) @@ plainto_tsquery('english', 'neural networks')
ORDER BY hybrid_score DESC
LIMIT 10;
-- Adaptive hybrid search (adjust weights based on query type)
CREATE OR REPLACE FUNCTION adaptive_hybrid_search(
query_text TEXT,
query_embedding VECTOR(768),
result_count INT DEFAULT 10
)
RETURNS TABLE (id INT, title VARCHAR, score FLOAT) AS $$
DECLARE
has_specific_terms BOOLEAN;
vector_weight FLOAT;
keyword_weight FLOAT;
BEGIN
-- Detect if query has specific/technical terms
has_specific_terms := query_text ~* '\m(API|SDK|UUID|JSON|SQL)\M';
-- Adjust weights
IF has_specific_terms THEN
vector_weight := 0.3;
keyword_weight := 0.7;
ELSE
vector_weight := 0.7;
keyword_weight := 0.3;
END IF;
RETURN QUERY
SELECT
d.id,
d.title,
(vector_weight * (1 - (d.embedding <=> query_embedding)) +
keyword_weight * COALESCE(ts_rank(to_tsvector('english', d.content),
plainto_tsquery('english', query_text)), 0))::FLOAT
FROM searchable_docs d
ORDER BY 3 DESC
LIMIT result_count;
END;
$$ LANGUAGE plpgsql;
6. Multi-Modal Search¶
Search across text and images using unified embeddings (CLIP).
Schema Setup¶
-- Multi-modal content
CREATE TABLE multi_modal_content (
id SERIAL PRIMARY KEY,
content_type VARCHAR(20), -- 'text', 'image', 'video'
title VARCHAR(500),
description TEXT,
file_path TEXT,
thumbnail_path TEXT,
metadata JSONB,
created_at TIMESTAMP DEFAULT NOW(),
clip_embedding VECTOR(512) -- CLIP embedding (unified space)
);
-- Unified index for cross-modal search
CREATE INDEX idx_multimodal_clip ON multi_modal_content
USING hnsw (clip_embedding vector_cosine_ops)
WITH (m = 24, ef_construction = 300);
Cross-Modal Search Queries¶
-- Search images with text query
-- Text "sunset on beach" embedding searches image embeddings
SELECT
id,
content_type,
title,
file_path,
clip_embedding <=> text_query_embedding AS relevance
FROM multi_modal_content
WHERE content_type = 'image'
ORDER BY clip_embedding <=> '[...]'::VECTOR -- CLIP text embedding
LIMIT 12;
-- Search all content types with image query
SELECT
id,
content_type,
title,
description,
clip_embedding <=> image_query_embedding AS relevance
FROM multi_modal_content
ORDER BY clip_embedding <=> '[...]'::VECTOR -- CLIP image embedding
LIMIT 20;
-- Find text descriptions for similar images
WITH image_results AS (
SELECT id, clip_embedding
FROM multi_modal_content
WHERE content_type = 'image'
ORDER BY clip_embedding <=> query_embedding
LIMIT 10
)
SELECT
t.id,
t.title,
t.description,
MIN(ir.clip_embedding <=> t.clip_embedding) AS similarity
FROM image_results ir
CROSS JOIN multi_modal_content t
WHERE t.content_type = 'text'
GROUP BY t.id, t.title, t.description
ORDER BY similarity
LIMIT 5;
7. Anomaly Detection¶
Detect outliers and anomalies using vector distance.
Schema Setup¶
-- System metrics table
CREATE TABLE system_metrics (
id SERIAL PRIMARY KEY,
timestamp TIMESTAMP,
server_id VARCHAR(50),
cpu_usage FLOAT,
memory_usage FLOAT,
disk_io FLOAT,
network_io FLOAT,
request_count INT,
error_count INT,
feature_vector VECTOR(128) -- Normalized metric embedding
);
-- Index for fast neighbor search
CREATE INDEX idx_metrics_anomaly ON system_metrics
USING hnsw (feature_vector vector_l2_ops)
WITH (m = 16, ef_construction = 200);
Anomaly Detection Queries¶
-- Find outliers: points with no close neighbors
WITH neighbor_distances AS (
SELECT
m1.id,
m1.timestamp,
m1.server_id,
MIN(m1.feature_vector <-> m2.feature_vector) AS nearest_distance
FROM system_metrics m1
JOIN system_metrics m2 ON m1.id != m2.id
AND m2.timestamp BETWEEN m1.timestamp - INTERVAL '1 hour'
AND m1.timestamp + INTERVAL '1 hour'
GROUP BY m1.id, m1.timestamp, m1.server_id
)
SELECT *
FROM neighbor_distances
WHERE nearest_distance > (
SELECT PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY nearest_distance)
FROM neighbor_distances
)
ORDER BY nearest_distance DESC;
-- Real-time anomaly detection for new data point
SELECT
CASE
WHEN MIN(new_point.feature_vector <-> m.feature_vector) > 2.5
THEN 'ANOMALY'
ELSE 'NORMAL'
END AS status,
MIN(new_point.feature_vector <-> m.feature_vector) AS min_distance
FROM (
SELECT '[0.5, 0.3, ...]'::VECTOR AS feature_vector
) new_point
CROSS JOIN system_metrics m
WHERE m.timestamp > NOW() - INTERVAL '24 hours';
-- Cluster-based anomaly detection
WITH cluster_centers AS (
-- Use K-means or pre-defined cluster centers
SELECT id, feature_vector
FROM (VALUES
(1, '[0.1, 0.2, ...]'::VECTOR),
(2, '[0.5, 0.3, ...]'::VECTOR),
(3, '[0.8, 0.1, ...]'::VECTOR)
) AS centers(id, feature_vector)
)
SELECT
m.id,
m.timestamp,
MIN(m.feature_vector <-> c.feature_vector) AS cluster_distance,
CASE
WHEN MIN(m.feature_vector <-> c.feature_vector) > 3.0
THEN 'ANOMALY'
ELSE 'NORMAL'
END AS status
FROM system_metrics m
CROSS JOIN cluster_centers c
GROUP BY m.id, m.timestamp
ORDER BY cluster_distance DESC
LIMIT 100;
8. Code Search¶
Search code repositories by functionality description.
Schema Setup¶
-- Code functions table
CREATE TABLE code_functions (
id SERIAL PRIMARY KEY,
repo_name VARCHAR(200),
file_path TEXT,
function_name VARCHAR(200),
signature TEXT,
docstring TEXT,
body TEXT,
language VARCHAR(50),
complexity_score INT,
embedding VECTOR(768) -- CodeBERT embedding
);
-- Index for semantic code search
CREATE INDEX idx_code_semantic ON code_functions
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);
-- Full-text index for identifier search
CREATE INDEX idx_code_fts ON code_functions
USING gin(to_tsvector('simple', function_name || ' ' || COALESCE(docstring, '')));
Code Search Queries¶
-- Semantic code search: "function to parse JSON with error handling"
SELECT
repo_name,
file_path,
function_name,
signature,
LEFT(docstring, 200) AS docstring_preview,
embedding <=> query_embedding AS relevance
FROM code_functions
WHERE language = 'python'
ORDER BY embedding <=> '[...]'::VECTOR
LIMIT 10;
-- Find similar implementations
SELECT
cf2.repo_name,
cf2.file_path,
cf2.function_name,
cf2.signature,
cf1.embedding <=> cf2.embedding AS similarity
FROM code_functions cf1
JOIN code_functions cf2 ON cf1.id != cf2.id
WHERE cf1.id = 12345 -- Reference function
AND cf2.language = cf1.language
ORDER BY cf1.embedding <=> cf2.embedding
LIMIT 10;
-- Hybrid: semantic + identifier search
SELECT
function_name,
file_path,
(0.6 * (1 - (embedding <=> query_embedding)) +
0.4 * COALESCE(ts_rank(to_tsvector('simple', function_name || ' ' || COALESCE(docstring, '')),
plainto_tsquery('simple', 'parse json')), 0)) AS score
FROM code_functions
ORDER BY score DESC
LIMIT 10;
Performance Tips for All Examples¶
1. Use Appropriate Index Settings¶
-- For high-throughput (web search)
WITH (m = 16, ef_construction = 200) -- SET hnsw.ef_search = 40
-- For high accuracy (RAG, recommendations)
WITH (m = 32, ef_construction = 400) -- SET hnsw.ef_search = 200
2. Pre-filter When Possible¶
-- Good: Filter then search
SELECT * FROM products
WHERE category = 'Electronics' AND in_stock = true
ORDER BY embedding <=> query
LIMIT 10;
-- Less efficient: Search then filter
SELECT * FROM (
SELECT * FROM products ORDER BY embedding <=> query LIMIT 1000
) sub
WHERE category = 'Electronics';
3. Use Connection Pooling¶
-- Configure for concurrent vector queries
SET max_parallel_workers_per_gather = 4;
SET work_mem = '256MB';
4. Monitor Query Performance¶
-- Check query plan
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM documents
ORDER BY embedding <=> query_vector
LIMIT 10;
Status: Production Ready Version: v7.0 Last Updated: January 2026