Vector Databases
Index and query high-dimensional embeddings using Pinecone, PostgreSQL pgvector, HNSW indexes, and metadata filtering.
TL;DR
- Initialize managed vector indexes using
@pinecone-database/pineconeclient SDKs. - Store high-dimensional vector embeddings alongside relational records using
pgvector. - Accelerate approximate nearest neighbor queries utilizing
HNSWindex configurations.
Managed Indexing with Pinecone
Pinecone Client InitializationConnect to managed serverless index with Pinecone TypeScript SDK.
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone({
apiKey: process.env.PINECONE_API_KEY!,
});
const index = pc.index('kb-docs');Upserting Vectors with MetadataInsert or update vector records with attached metadata payloads.
await index.upsert([
{
id: 'doc-101',
values: [0.012, 0.431, -0.219],
metadata: {
tenantId: 'team-4',
category: 'legal',
text: 'Clause 5 terms...',
},
},
]);Filtered Vector Similarity QueryQuery top-K nearest neighbors scoped by metadata filter conditions.
const results = await index.query({
vector: queryVector,
topK: 5,
includeMetadata: true,
filter: {
tenantId: { $eq: 'team-4' },
category: { $in: ['legal', 'compliance'] },
},
});PostgreSQL with pgvector
SQL Table & HNSW Index SchemaEnable pgvector extension and create indexed vector table.
-- Enable extension in PostgreSQL database
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
metadata JSONB DEFAULT '{}'::jsonb,
embedding vector(1536)
);
-- Build HNSW index using cosine distance
CREATE INDEX doc_hnsw_idx ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);Drizzle ORM Vector QueryQuery nearest neighbors using cosine distance operator in SQL.
import { sql } from 'drizzle-orm';
async function searchDocs(
db: any, queryVec: number[]
) {
const vecStr = `[${queryVec.join(',')}]`;
return await db.execute(sql`
SELECT id, content,
1 - (embedding <=> ${vecStr}::vector) AS score
FROM documents
WHERE metadata->>'tenant' = 'org-12'
ORDER BY embedding <=> ${vecStr}::vector
LIMIT 5;
`);
}Vector Dimension Match CheckEnforce exact vector dimension parity before database inserts.
function validateVector(
v: number[], targetDim = 1536
) {
if (v.length !== targetDim) {
const err = 'Vector dimension mismatch';
throw new Error(err);
}
}Index Performance & Scaling
HNSW vs IVFFlat SelectionTrade-offs between indexing build time, memory, and search speed.
// HNSW: Fast queries, high memory, no training step.
// Best for production real-time search (< 5ms).
// IVFFlat: Low memory, faster build, requires train.
// Best for batch systems with tight RAM budgets.Dynamic ef_search TuningIncrease recall accuracy at query time for critical searches.
async function queryWithHighRecall(client: any) {
// Default ef_search is 40. Bump to 100 for accuracy
await client.query('SET hnsw.ef_search = 100;');
return await runVectorQuery(client);
}Namespaces for Multi-TenancyIsolate tenant vector records completely using index namespaces.
const ns = 'tenant_enterprise_99';
const tenantIndex = index.namespace(ns);
await tenantIndex.upsert(records);
const matches = await tenantIndex.query({
vector: q, topK: 3,
});Metadata Filtering & Tenancy
Pre-Filtering vs Post-FilteringFilter candidates before distance calculation to prevent recall collapse.
// Pre-filtering restricts the search graph to matches.
// Post-filtering searches K items, then filters,
// which can yield zero results if K has few matches.Composite Metadata QueryFilter by customer tenant ID and document update timestamp.
const filter = {
tenant: { $eq: 'customer_7' },
updatedAt: { $gte: 1704067200 },
};
const res = await index.query({
vector: q, topK: 5, filter,
});Soft Deletion TombstonesFilter out deleted vectors without paying index compaction costs.
const activeFilter = { isDeleted: { $ne: true } };
const active = await index.query({
vector: q, topK: 5, filter: activeFilter
});Tips
- Create
HNSWindexes over IVFFlat in pgvector for dramatically higher queries-per-second and low latency under scale. - Attach tenant and role identifiers as vector
metadatato enforce data isolation at retrieval time.
Warnings
- Never perform slow, unindexed vector table scans on production tables exceeding ten thousand records in
PostgreSQL. - Avoid deeply nested
metadataobjects in Pinecone that exceed provider payload indexing limits and inflate retrieval latencies.
In Practice
Connects to a Pinecone index, upserts an embedded document with metadata, and queries matches.
- Instantiate Pinecone client with environment configuration.
- Select target vector index reference handle.
- Upsert document vector with metadata payload fields.
- Execute top-K semantic query filtered by tenant attribute.
import { Pinecone } from '@pinecone-database/pinecone';
const pc = new Pinecone();
const idx = pc.index('knowledge-base');
await idx.upsert([{
id: 'doc_42',
values: new Array(1536).fill(0.01),
metadata: { org: 'acme', title: 'Onboarding' },
}]);
const q = new Array(1536).fill(0.01);
const matches = await idx.query({
vector: q, topK: 1,
includeMetadata: true,
filter: { org: { $eq: 'acme' } },
});
console.log(matches.matches[0]?.metadata?.title);FAQ
Choose pgvector if you already use PostgreSQL and want ACID transactions joining vectors with relational data. Choose Pinecone if you need serverless, zero-ops vector infrastructure with automated scaling.
HNSW builds a multi-layered graph for fast approximate nearest neighbor search with sub-5ms queries and no separate index training step, whereas IVFFlat clusters vectors into lists and requires re-training.
Metadata filters constrain the candidate set of vectors by attributes like tenant ID, author, or timestamp, ensuring search results only include authorized and relevant documents.