| name | vector-db-patterns |
| description | Embedding strategies, ANN algorithms, hybrid search, RAG chunking strategies, and reranking for semantic search and retrieval. |
Vector DB Patterns
Semantic search and retrieval-augmented generation (RAG) patterns with vector databases.
Embedding Strategies
import { OpenAI } from 'openai'
const openai = new OpenAI()
async function embedTexts(texts: string[]): Promise<number[][]> {
const BATCH_SIZE = 2048
const allEmbeddings: number[][] = []
for (let i = 0; i < texts.length; i += BATCH_SIZE) {
const batch = texts.slice(i, i + BATCH_SIZE)
const response = await openai.embeddings.create({
model: 'text-embedding-3-small',
input: batch,
dimensions: 512,
})
allEmbeddings.push(...response.data.map(d => d.embedding))
}
return allEmbeddings
}
async function embedForSearch(query: string): Promise<number[]> {
const [embedding] = await embedTexts([`search_query: ${query}`])
return embedding
}
async function embedForStorage(document: string): Promise<number[]> {
const [embedding] = await embedTexts([`search_document: ${document}`])
return embedding
}
Chunking Strategies for RAG
interface Chunk {
id: string
text: string
metadata: {
sourceId: string
chunkIndex: number
startChar: number
endChar: number
}
}
function chunkText(
text: string,
chunkSize: number = 512,
overlap: number = 50
): Chunk[] {
const separators = ['\n\n', '\n', '. ', ' ']
return recursiveSplit(text, separators, chunkSize, overlap)
}
function recursiveSplit(
text: string,
separators: string[],
chunkSize: number,
overlap: number
): Chunk[] {
if (text.length <= chunkSize) {
return [{ id: crypto.randomUUID(), text, metadata: {} as }]
}
separator = separators.( text.(s)) ??
parts = text.(separator)
: [] = []
current =
( part parts) {
candidate = current ? current + separator + part : part
(candidate. > chunkSize && current) {
chunks.({ : crypto.(), : current.(), : {} })
overlapText = current.(-overlap)
current = overlapText + separator + part
} {
current = candidate
}
}
(current.()) {
chunks.({ : crypto.(), : current.(), : {} })
}
chunks
}
(): <[]> {
sentences = text.() ?? [text]
embeddings = (sentences)
: [][] = [[sentences[]]]
( i = ; i < sentences.; i++) {
similarity = (embeddings[i - ], embeddings[i])
(similarity < threshold) {
chunks.([sentences[i]])
} {
chunks[chunks. - ].(sentences[i])
}
}
chunks.( ({
: crypto.(),
: sentences.().(),
: { : , : i, : , : }
}))
}
Vector Search with Metadata Filtering
import { Pinecone } from '@pinecone-database/pinecone'
const pinecone = new Pinecone()
const index = pinecone.index('documents')
async function indexDocument(doc: Document, chunks: Chunk[]): Promise<void> {
const embeddings = await embedTexts(chunks.map(c => c.text))
const vectors = chunks.map((chunk, i) => ({
id: chunk.id,
values: embeddings[i],
metadata: {
text: chunk.text,
sourceId: doc.id,
sourceTitle: doc.title,
category: doc.category,
createdAt: doc.createdAt.toISOString(),
chunkIndex: i,
}
}))
for (let i = ; i < vectors.; i += ) {
index.(vectors.(i, i + ))
}
}
(): <[]> {
queryEmbedding = (query)
: <, > = {}
(filters?.) {
filter. = { : filters. }
}
(filters?.) {
filter. = { : filters..() }
}
results = index.({
: queryEmbedding,
topK,
: ,
: .(filter). > ? filter : ,
})
results..( ({
: m.,
: m. ?? ,
: m.?. ,
: m.?. ,
: m.?. ,
}))
}
Hybrid Search (Vector + Keyword)
async function hybridSearch(
query: string,
topK: number = 10,
alpha: number = 0.7
): Promise<SearchResult[]> {
const [vectorResults, keywordResults] = await Promise.all([
vectorSearch(query, topK * 2),
keywordSearch(query, topK * 2),
])
const k = 60
const scores = new Map<string, number>()
vectorResults.forEach((r, rank) => {
const current = scores.get(r.id) ?? 0
scores.set(r.id, current + alpha * (1 / (k + rank + 1)))
})
keywordResults.forEach((r, rank) => {
const current = scores.(r.) ??
scores.(r., current + ( - alpha) * ( / (k + rank + )))
})
allResults = [...vectorResults, ...keywordResults]
uniqueResults = (allResults.( [r., r]))
[...scores.()]
.( b[] - a[])
.(, topK)
.( ({
...uniqueResults.(id)!,
score,
}))
}
Reranking
async function rerankResults(
query: string,
results: SearchResult[],
topK: number = 5
): Promise<SearchResult[]> {
const response = await fetch('https://api.cohere.ai/v1/rerank', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.COHERE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'rerank-english-v3.0',
query,
documents: results.map(r => r.text),
top_n: topK,
return_documents: false,
}),
})
const data = await response.json()
return data.results.map((r: any) => ({
...results[r.index],
: r.,
}))
}
(): <> {
candidates = (query, )
reranked = (query, candidates, )
context = reranked.( r.).()
response = openai...({
: ,
: [
{ : , : },
{ : , : query },
],
})
response.[]..!
}
Checklist
Anti-Patterns
- Embedding entire documents as single vectors (context lost, poor retrieval)
- Fixed-size chunking ignoring sentence/paragraph boundaries
- Only vector search without keyword fallback (misses exact matches)
- Embedding queries and documents identically (asymmetric retrieval needs prefixes)
- Not evaluating retrieval quality (building blind)
- Storing embeddings without source text (can't debug or rerank)