@anvia/pgvector
Postgres pgvector vector store adapter for Anvia.
@anvia/pgvector connects Anvia's vector store interface to PostgreSQL with the pgvector extension. Use it when you already have Postgres and want to add vector search without a separate database.
Install
pnpm add @anvia/pgvectorThe pg and pgvector packages are transitive dependencies. Your Postgres instance must have the vector extension available.
Quick Start
import { PgVectorStore } from "@anvia/pgvector";
import { createFastEmbedEmbeddingModel } from "@anvia/fastembed";
const model = await createFastEmbedEmbeddingModel();
// Connect (creates table and extension if missing)
const store = await PgVectorStore.connect({
connectionString: "postgresql://user:pass@localhost:5432/mydb",
tableName: "documents",
vectorSize: 384, // must match your embedding model's dimensions
});
// Upsert embedded documents
await store.upsertDocuments([
{
id: "doc-1",
document: "Anvia is a TypeScript AI agent framework.",
embeddings: await model.embedTexts(["Anvia is a TypeScript AI agent framework."]),
},
]);
// Search
const index = store.index(model);
const results = await index.search({ query: "What is Anvia?", topK: 5 });
console.log(results[0].document);Connection Options
type PgVectorStoreConnectOptions = {
client?: PgClientLike; // pre-configured pg client (Pool, Client, etc.)
connectionString?: string; // Postgres connection string
tableName: string; // required
vectorSize: number; // required: embedding dimensions
createIfMissing?: boolean; // default: true
distance?: PgVectorDistance; // default: "cosine"
};// With connection string
const store = await PgVectorStore.connect({
connectionString: "postgresql://user:pass@localhost:5432/mydb",
tableName: "documents",
vectorSize: 384,
});
// With existing pg client
import pg from "pg";
const pool = new pg.Pool({ connectionString: "..." });
const store = await PgVectorStore.connect({
client: pool,
tableName: "documents",
vectorSize: 384,
});
// Schema-qualified table name
const store = await PgVectorStore.connect({
connectionString: "...",
tableName: "public.documents",
vectorSize: 384,
});When createIfMissing is true (default), the adapter:
- Runs
CREATE EXTENSION IF NOT EXISTS vector - Creates the table with the correct schema if it does not exist
- Validates that the existing table's vector dimension matches
vectorSize
Table Schema
The auto-created table has this structure:
CREATE TABLE IF NOT EXISTS "documents" (
id text PRIMARY KEY,
document_id text NOT NULL,
document jsonb NOT NULL,
metadata jsonb,
embedding vector(384) NOT NULL
)idis a deterministic hash of the logical document IDdocument_idis the original document IDdocumentis the JSON-serialized document contentmetadatais optional JSONB metadataembeddingis the pgvector column
Upserting Documents
await store.upsertDocuments([
{
id: "doc-1",
document: { title: "Getting Started", content: "..." },
metadata: { category: "guide", version: 2 },
embeddings: await model.embedTexts(["Getting Started content..."]),
},
]);Uses INSERT ... ON CONFLICT ... DO UPDATE so documents are upserted atomically.
Searching
const index = store.index(model);
// Basic search
const results = await index.search({ query: "agent tools", topK: 5 });
// With threshold
const results = await index.search({
query: "agent tools",
topK: 10,
threshold: 0.7,
});
// With metadata filter
const results = await index.search({
query: "agent tools",
topK: 5,
filter: { type: "eq", key: "category", value: "guide" },
});Metadata Filters
// Equality
{ type: "eq", key: "category", value: "guide" }
// Greater than / less than (numeric values only)
{ type: "gt", key: "version", value: 2 }
{ type: "lt", key: "version", value: 5 }
// Logical combinators
{ type: "and", filters: [...] }
{ type: "or", filters: [...] }Filters are translated to Postgres JSONB queries on the metadata column. The gt and lt filters cast the metadata value to numeric, so they require numeric metadata values.
Distance Metrics
| Metric | Value | SQL Operator |
|---|---|---|
| Cosine (default) | "cosine" | <=> |
| L2 (Euclidean) | "l2" | <-> |
| Inner product | "innerProduct" | <#> |
Agent Tool
Expose the vector index as an agent tool:
const searchTool = index.asTool({
name: "search_docs",
description: "Search the documentation.",
});
const agent = new AgentBuilder("support", model)
.tool(searchTool)
.build();Error Handling
connect()throws if Postgres is unreachable or thevectorextension is unavailableconnect()throws if the table exists but the vector dimension does not matchvectorSizeupsertDocuments()throws if a document has zero embeddings- Metadata keys starting with
__anvia_are reserved and rejected
Related
- Retrieval Guide for RAG concepts
- pgvector Reference for full API types
@anvia/fastembedfor local embeddings
