Skip to main content

PG Vector Store

The PgVector class provides vector search using PostgreSQL with pgvector extension. It provides robust vector similarity search capabilities within your existing PostgreSQL database.

Constructor Options

connectionString:

string
PostgreSQL connection URL

schemaName?:

string
The name of the schema you want the vector store to use. Will use the default schema if not provided.

Constructor Examples

You can instantiate PgVector using a config object (with optional schemaName):

import { PgVector } from "@mastra/pg";

const vectorStore = new PgVector({
connectionString: "postgresql://user:password@localhost:5432/mydb",
schemaName: "custom_schema", // optional
});

Methods

createIndex()

indexName:

string
Name of the index to create

dimension:

number
Vector dimension (must match your embedding model)

metric?:

'cosine' | 'euclidean' | 'dotproduct'
= cosine
Distance metric for similarity search

indexConfig?:

IndexConfig
= { type: 'ivfflat' }
Index configuration

buildIndex?:

boolean
= true
Whether to build the index

IndexConfig

type:

'flat' | 'hnsw' | 'ivfflat'
= ivfflat
Index type
string

flat:

flat
Sequential scan (no index) that performs exhaustive search.

ivfflat:

ivfflat
Clusters vectors into lists for approximate search.

hnsw:

hnsw
Graph-based index offering fast search times and high recall.

ivf?:

IVFConfig
IVF configuration
object

lists?:

number
Number of lists. If not specified, automatically calculated based on dataset size. (Minimum 100, Maximum 4000)

hnsw?:

HNSWConfig
HNSW configuration
object

m?:

number
Maximum number of connections per node (default: 8)

efConstruction?:

number
Build-time complexity (default: 32)

Memory Requirements

HNSW indexes require significant shared memory during construction. For 100K vectors:

  • Small dimensions (64d): ~60MB with default settings
  • Medium dimensions (256d): ~180MB with default settings
  • Large dimensions (384d+): ~250MB+ with default settings

Higher M values or efConstruction values will increase memory requirements significantly. Adjust your system's shared memory limits if needed.

upsert()

indexName:

string
Name of the index to upsert vectors into

vectors:

number[][]
Array of embedding vectors

metadata?:

Record<string, any>[]
Metadata for each vector

ids?:

string[]
Optional vector IDs (auto-generated if not provided)

query()

indexName:

string
Name of the index to query

vector:

number[]
Query vector

topK?:

number
= 10
Number of results to return

filter?:

Record<string, any>
Metadata filters

includeVector?:

boolean
= false
Whether to include the vector in the result

minScore?:

number
= 0
Minimum similarity score threshold

options?:

{ ef?: number; probes?: number }
Additional options for HNSW and IVF indexes
object

ef?:

number
HNSW search parameter

probes?:

number
IVF search parameter

listIndexes()

Returns an array of index names as strings.

describeIndex()

indexName:

string
Name of the index to describe

Returns:

interface PGIndexStats {
dimension: number;
count: number;
metric: "cosine" | "euclidean" | "dotproduct";
type: "flat" | "hnsw" | "ivfflat";
config: {
m?: number;
efConstruction?: number;
lists?: number;
probes?: number;
};
}

deleteIndex()

indexName:

string
Name of the index to delete

updateVector()

indexName:

string
Name of the index containing the vector

id:

string
ID of the vector to update

update:

object
Update parameters
object

vector?:

number[]
New vector values

metadata?:

Record<string, any>
New metadata values

Updates an existing vector by ID. At least one of vector or metadata must be provided.

// Update just the vector
await pgVector.updateVector({
indexName: "my_vectors",
id: "vector123",
update: {
vector: [0.1, 0.2, 0.3],
},
});

// Update just the metadata
await pgVector.updateVector({
indexName: "my_vectors",
id: "vector123",
update: {
metadata: { label: "updated" },
},
});

// Update both vector and metadata
await pgVector.updateVector({
indexName: "my_vectors",
id: "vector123",
update: {
vector: [0.1, 0.2, 0.3],
metadata: { label: "updated" },
},
});

deleteVector()

indexName:

string
Name of the index containing the vector

id:

string
ID of the vector to delete

Deletes a single vector by ID from the specified index.

await pgVector.deleteVector({ indexName: "my_vectors", id: "vector123" });

disconnect()

Closes the database connection pool. Should be called when done using the store.

buildIndex()

indexName:

string
Name of the index to define

metric?:

'cosine' | 'euclidean' | 'dotproduct'
= cosine
Distance metric for similarity search

indexConfig:

IndexConfig
Configuration for the index type and parameters

Builds or rebuilds an index with specified metric and configuration. Will drop any existing index before creating the new one.

// Define HNSW index
await pgVector.buildIndex("my_vectors", "cosine", {
type: "hnsw",
hnsw: {
m: 8,
efConstruction: 32,
},
});

// Define IVF index
await pgVector.buildIndex("my_vectors", "cosine", {
type: "ivfflat",
ivf: {
lists: 100,
},
});

// Define flat index
await pgVector.buildIndex("my_vectors", "cosine", {
type: "flat",
});

Response Types

Query results are returned in this format:

interface QueryResult {
id: string;
score: number;
metadata: Record<string, any>;
vector?: number[]; // Only included if includeVector is true
}

Error Handling

The store throws typed errors that can be caught:

try {
await store.query({
indexName: "index_name",
queryVector: queryVector,
});
} catch (error) {
if (error instanceof VectorStoreError) {
console.log(error.code); // 'connection_failed' | 'invalid_dimension' | etc
console.log(error.details); // Additional error context
}
}

Index Configuration Guide

Performance Optimization

IVFFlat Tuning

  • lists parameter: Set to sqrt(n) * 2 where n is the number of vectors
  • More lists = better accuracy but slower build time
  • Fewer lists = faster build but potentially lower accuracy

HNSW Tuning

  • m parameter:
    • 8-16: Moderate accuracy, lower memory
    • 16-32: High accuracy, moderate memory
    • 32-64: Very high accuracy, high memory
  • efConstruction:
    • 32-64: Fast build, good quality
    • 64-128: Slower build, better quality
    • 128-256: Slowest build, best quality

Index Recreation Behavior

The system automatically detects configuration changes and only rebuilds indexes when necessary:

  • Same configuration: Index is kept (no recreation)
  • Changed configuration: Index is dropped and rebuilt
  • This prevents the performance issues from unnecessary index recreations

Best Practices

  • Regularly evaluate your index configuration to ensure optimal performance.
  • Adjust parameters like lists and m based on dataset size and query requirements.
  • Monitor index performance using describeIndex() to track usage
  • Rebuild indexes periodically to maintain efficiency, especially after significant data changes

Direct Pool Access

The PgVector class exposes its underlying PostgreSQL connection pool as a public field:

pgVector.pool; // instance of pg.Pool

This enables advanced usage such as running direct SQL queries, managing transactions, or monitoring pool state. When using the pool directly:

  • You are responsible for releasing clients (client.release()) after use.
  • The pool remains accessible after calling disconnect(), but new queries will fail.
  • Direct access bypasses any validation or transaction logic provided by PgVector methods.

This design supports advanced use cases but requires careful resource management by the user.