pgvector adds vector similarity search capabilities to PostgreSQL, allowing you to store embeddings alongside your relational data.
Installation
Prerequisites
Install the pgvector extension in your PostgreSQL database:
CREATE EXTENSION IF NOT EXISTS vector;
For managed PostgreSQL services:
- Supabase: Enabled by default
- AWS RDS: Install from AWS Extensions
- Neon: Enabled by default
- Railway: Requires PostgreSQL 14+
Configuration
Import PgVector
import { PgVector } from '@mastra/pg';
Create vector store instance
const vectorStore = new PgVector({
id: 'embeddings',
connectionString: process.env.DATABASE_URL!,
});
Configure Mastra
import { Mastra } from '@mastra/core';
const mastra = new Mastra({
vectors: {
embeddings: vectorStore,
},
});
Configuration Options
Unique identifier for the vector store instance
PostgreSQL connection string
Database host (alternative to connectionString)
PostgreSQL schema for vector tables
Vector Operations
Create Index
const vectorStore = new PgVector({
id: 'embeddings',
connectionString: process.env.DATABASE_URL!,
});
// Create with HNSW index (recommended)
await vectorStore.createIndex({
indexName: 'documents',
dimension: 1536,
metric: 'cosine',
indexConfig: {
type: 'hnsw',
hnsw: {
m: 16,
efConstruction: 64,
},
},
});
Index Types
pgvector supports three index types:
HNSW (Recommended)
Best for most use cases:
await vectorStore.createIndex({
indexName: 'documents',
dimension: 1536,
metric: 'cosine',
indexConfig: {
type: 'hnsw',
hnsw: {
m: 16, // Number of connections per layer
efConstruction: 64, // Size of dynamic candidate list
},
},
});
IVFFlat
Good for large datasets where build time matters:
await vectorStore.createIndex({
indexName: 'documents',
dimension: 1536,
metric: 'cosine',
indexConfig: {
type: 'ivfflat',
ivf: {
lists: 100, // Number of clusters
},
},
});
Flat (Exact Search)
No index, always exact but slower:
await vectorStore.createIndex({
indexName: 'documents',
dimension: 1536,
metric: 'cosine',
indexConfig: {
type: 'flat',
},
});
Vector Types
pgvector 0.7.0+ supports halfvec for 2x memory savings:
// Full precision (default) - max 2000 dimensions for indexes
await vectorStore.createIndex({
indexName: 'documents',
dimension: 1536,
vectorType: 'vector',
});
// Half precision - max 4000 dimensions for indexes
await vectorStore.createIndex({
indexName: 'large_embeddings',
dimension: 3072, // text-embedding-3-large
vectorType: 'halfvec',
});
Upsert Vectors
const vectors = [
[0.1, 0.2, 0.3, ...],
[0.4, 0.5, 0.6, ...],
];
const metadata = [
{ text: 'First document', category: 'tech' },
{ text: 'Second document', category: 'business' },
];
const ids = await vectorStore.upsert({
indexName: 'documents',
vectors,
metadata,
});
Query Similar Vectors
const queryVector = [0.15, 0.25, 0.35, ...];
const results = await vectorStore.query({
indexName: 'documents',
queryVector,
topK: 5,
includeVector: false,
});
results.forEach(result => {
console.log(`Score: ${result.score}`);
console.log(`Text: ${result.metadata.text}`);
});
const results = await vectorStore.query({
indexName: 'documents',
queryVector,
topK: 5,
filter: {
category: { $eq: 'tech' },
year: { $gte: 2020 },
},
});
HNSW Query Tuning
const results = await vectorStore.query({
indexName: 'documents',
queryVector,
topK: 5,
ef: 100, // Higher = better recall, slower search
});
Update Vector
await vectorStore.updateVector({
indexName: 'documents',
id: 'vector-id-123',
update: {
vector: [0.2, 0.3, 0.4, ...],
metadata: { text: 'Updated' },
},
});
Delete Operations
// Delete by ID
await vectorStore.deleteVector({
indexName: 'documents',
id: 'vector-id-123',
});
// Delete by filter
await vectorStore.deleteVectors({
indexName: 'documents',
filter: {
category: { $eq: 'archived' },
},
});
// Delete multiple by IDs
await vectorStore.deleteVectors({
indexName: 'documents',
ids: ['id1', 'id2', 'id3'],
});
Index Management
// List indexes
const indexes = await vectorStore.listIndexes();
// Describe index
const stats = await vectorStore.describeIndex({
indexName: 'documents',
});
console.log('Type:', stats.type); // 'hnsw', 'ivfflat', or 'flat'
console.log('Vector Type:', stats.vectorType); // 'vector' or 'halfvec'
console.log('Dimension:', stats.dimension);
console.log('Count:', stats.count);
// Delete index
await vectorStore.deleteIndex({
indexName: 'documents',
});
// Truncate (delete all vectors)
await vectorStore.truncateIndex({
indexName: 'documents',
});
RAG Integration
import { Mastra } from '@mastra/core';
import { PgVector } from '@mastra/pg';
import { createOpenAI } from '@ai-sdk/openai';
import { embed } from 'ai';
const mastra = new Mastra({
vectors: {
embeddings: new PgVector({
id: 'embeddings',
connectionString: process.env.DATABASE_URL!,
}),
},
});
const openai = createOpenAI({
apiKey: process.env.OPENAI_API_KEY!,
});
// Create index
await mastra.vectors.embeddings.createIndex({
indexName: 'docs',
dimension: 1536,
indexConfig: {
type: 'hnsw',
},
});
// Index document
const { embedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: 'Document text',
});
await mastra.vectors.embeddings.upsert({
indexName: 'docs',
vectors: [embedding],
metadata: [{ text: 'Document text', source: 'manual' }],
});
// Query
const { embedding: queryEmbedding } = await embed({
model: openai.embedding('text-embedding-3-small'),
value: 'User question',
});
const results = await mastra.vectors.embeddings.query({
indexName: 'docs',
queryVector: queryEmbedding,
topK: 3,
});
Distance Metrics
cosine - Cosine similarity (recommended)
euclidean - Euclidean distance (L2)
dotproduct - Inner product
Combined Storage and Vectors
import { PostgresStore, PgVector } from '@mastra/pg';
import { Mastra } from '@mastra/core';
const mastra = new Mastra({
storage: new PostgresStore({
id: 'storage',
connectionString: process.env.DATABASE_URL!,
}),
vectors: {
embeddings: new PgVector({
id: 'vectors',
connectionString: process.env.DATABASE_URL!,
}),
},
});
Best Practices
Use HNSW for Most Cases
HNSW provides the best balance of speed and accuracy.
halfvec for Large Embeddings
Use vectorType: 'halfvec' for embeddings > 2000 dimensions.
Tune ef_search
Increase ef parameter at query time for better recall.
Batch Upserts
Insert vectors in batches of 100-1000 for performance.
HNSW Parameters
m: 16 (default) - Higher = better recall, larger index
efConstruction: 64 (default) - Higher = better quality, slower build
- Query
ef: 100+ - Higher = better recall, slower queries
IVFFlat Parameters
lists: sqrt(rows) * 2 - Number of clusters
probes: 10 - Number of clusters to search
PostgreSQL Storage
PostgreSQL storage adapter
Pinecone
Managed vector database alternative
pgvector Docs
Official pgvector documentation
Supabase Vector
pgvector on Supabase