Skip to content
All essays
AIMarch 20, 202535 min

Vector Databases for AI Applications

Master vector databases for AI applications. Learn about embeddings, similarity search, and building semantic search systems in 2025.

Ü
Ümit Uz
Mobile & Full Stack Developer

Vector databases have emerged as the backbone of modern AI applications, enabling semantic search, recommendation systems, and retrieval-augmented generation (RAG). As we progress through 2025, understanding vector databases is essential for building intelligent, context-aware applications. This comprehensive guide explores everything from embeddings to production deployments.

Understanding Vector Databases

What Are Vector Databases?

Vector databases are specialized databases designed to store, index, and query high-dimensional vectors efficiently. Unlike traditional databases that excel at exact matches, vector databases excel at similarity search, finding the "nearest" vectors to a query.

typescript
// Traditional database vs Vector database
// Traditional: WHERE name = "John" → Exact match
// Vector: Find similar to [0.1, 0.2, 0.3, ...] → Similarity match

Key Concepts

Embeddings: Numerical representations of data (text, images, audio) in high-dimensional space Vectors: Arrays of numbers representing embeddings Similarity Metrics: Mathematical functions to measure vector closeness Indexing: Data structures for fast similarity search ANN (Approximate Nearest Neighbor): Fast, approximate similarity search

Embeddings: The Foundation

What Are Embeddings?

Embeddings transform complex data into dense vectors where similar items are close together in vector space.

typescript
// Example: Word embeddings (simplified)
const king = [0.9, 0.2, 0.1, 0.8];
const queen = [0.9, 0.2, 0.1, 0.7];
const man = [0.8, 0.3, 0.2, 0.6];
const woman = [0.8, 0.3, 0.2, 0.5];

// king - man + woman ≈ queen (vector arithmetic!)

Creating Embeddings

Text Embeddings with OpenAI

typescript
import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY
});

async function createEmbedding(text: string): Promise<number[]> {
  const response = await openai.embeddings.create({
    model: 'text-embedding-3-small', // or 'text-embedding-3-large'
    input: text,
    dimensions: 1536 // Embedding dimensions
  });

  return response.data[0].embedding;
}

// Batch embeddings
async function createBatchEmbeddings(texts: string[]): Promise<number[][]> {
  const response = await openai.embeddings.create({
    model: 'text-embedding-3-small',
    input: texts
  });

  return response.data.map(item => item.embedding);
}

// Usage examples
const embedding1 = await createEmbedding('Machine learning is awesome');
const embedding2 = await createEmbedding('AI is transforming the world');
const embedding3 = await createEmbedging('The quick brown fox');

console.log('Embedding dimensions:', embedding1.length); // 1536

Embeddings with Hugging Face Transformers.js

typescript
// Run embeddings entirely in the browser
import { pipeline, env } from '@xenova/transformers';

// Skip local model checks
env.allowLocalModels = false;
env.allowRemoteModels = true;

class EmbeddingService {
  private extractor: any;

  async init() {
    console.log('Loading embedding model...');
    this.extractor = await pipeline(
      'feature-extraction',
      'Xenova/all-MiniLM-L6-v2'
    );
    console.log('Model loaded!');
  }

  async embed(text: string): Promise<number[]> {
    const output = await this.extractor(text, {
      pooling: 'mean',
      normalize: true
    });

    return Array.from(output.data);
  }

  async embedBatch(texts: string[]): Promise<number[][]> {
    return Promise.all(texts.map(text => this.embed(text)));
  }
}

// Usage
const embedder = new EmbeddingService();
await embedder.init();

const embedding = await embedder.embed('Hello, world!');
console.log('Embedding shape:', embedding.length); // 384 dimensions

Image Embeddings

typescript
import * as tf from '@tensorflow/tfjs';
import * as mobilenet from '@tensorflow-models/mobilenet';

class ImageEmbedder {
  private model: mobilenet.MobileNet | null = null;

  async load() {
    this.model = await mobilenet.load();
  }

  async embed(imageElement: HTMLImageElement): Promise<number[]> {
    if (!this.model) throw new Error('Model not loaded');

    // Get activation from a specific layer (penultimate layer)
    const activation = this.model.infer(imageElement, true);
    return Array.from(activation.dataSync());
  }

  async embedBatch(images: HTMLImageElement[]): Promise<number[][]> {
    return Promise.all(images.map(img => this.embed(img)));
  }
}

// Usage
const imageEmbedder = new ImageEmbedder();
await imageEmbedder.load();

const imageEmbedding = await imageEmbedder.embed(document.querySelector('img')!);

Similarity Metrics

Cosine Similarity

Most common for text embeddings, measures angle between vectors:

typescript
function cosineSimilarity(a: number[], b: number[]): number {
  if (a.length !== b.length) {
    throw new Error('Vectors must have same length');
  }

  let dotProduct = 0;
  let magnitudeA = 0;
  let magnitudeB = 0;

  for (let i = 0; i < a.length; i++) {
    dotProduct += a[i] * b[i];
    magnitudeA += a[i] * a[i];
    magnitudeB += b[i] * b[i];
  }

  magnitudeA = Math.sqrt(magnitudeA);
  magnitudeB = Math.sqrt(magnitudeB);

  if (magnitudeA === 0 || magnitudeB === 0) {
    return 0;
  }

  return dotProduct / (magnitudeA * magnitudeB);
}

// Usage
const vec1 = [1, 2, 3];
const vec2 = [2, 4, 6];
const similarity = cosineSimilarity(vec1, vec2);
console.log('Cosine similarity:', similarity); // 1.0 (perfectly similar)

Euclidean Distance

typescript
function euclideanDistance(a: number[], b: number[]): number {
  if (a.length !== b.length) {
    throw new Error('Vectors must have same length');
  }

  let sum = 0;
  for (let i = 0; i < a.length; i++) {
    const diff = a[i] - b[i];
    sum += diff * diff;
  }

  return Math.sqrt(sum);
}

// Usage
const distance = euclideanDistance(vec1, vec2);
console.log('Euclidean distance:', distance);

Dot Product

typescript
function dotProduct(a: number[], b: number[]): number {
  if (a.length !== b.length) {
    throw new Error('Vectors must have same length');
  }

  let product = 0;
  for (let i = 0; i < a.length; i++) {
    product += a[i] * b[i];
  }

  return product;
}

Building a Vector Database

In-Memory Vector Store

typescript
interface VectorDocument {
  id: string;
  vector: number[];
  metadata: Record<string, any>;
  createdAt: Date;
}

class VectorStore {
  private documents: Map<string, VectorDocument>;
  private dimension: number;

  constructor(dimension: number) {
    this.documents = new Map();
    this.dimension = dimension;
  }

  add(id: string, vector: number[], metadata: Record<string, any> = {}): void {
    if (vector.length !== this.dimension) {
      throw new Error(`Vector dimension mismatch. Expected ${this.dimension}, got ${vector.length}`);
    }

    this.documents.set(id, {
      id,
      vector,
      metadata,
      createdAt: new Date()
    });
  }

  addBatch(items: Array<{
    id: string;
    vector: number[];
    metadata?: Record<string, any>;
  }>): void {
    items.forEach(item => {
      this.add(item.id, item.vector, item.metadata);
    });
  }

  get(id: string): VectorDocument | undefined {
    return this.documents.get(id);
  }

  delete(id: string): boolean {
    return this.documents.delete(id);
  }

  search(
    queryVector: number[],
    topK: number = 10,
    similarityThreshold: number = 0.7
  ): Array<{
    document: VectorDocument;
    similarity: number;
  }> {
    if (queryVector.length !== this.dimension) {
      throw new Error('Query vector dimension mismatch');
    }

    const results = Array.from(this.documents.values())
      .map(doc => ({
        document: doc,
        similarity: cosineSimilarity(queryVector, doc.vector)
      }))
      .filter(result => result.similarity >= similarityThreshold)
      .sort((a, b) => b.similarity - a.similarity)
      .slice(0, topK);

    return results;
  }

  // Hybrid search: combine vector similarity with metadata filters
  hybridSearch(
    queryVector: number[],
    filters: Record<string, any>,
    topK: number = 10
  ): Array<{
    document: VectorDocument;
    similarity: number;
  }> {
    const results = Array.from(this.documents.values())
      .filter(doc => {
        // Apply metadata filters
        return Object.entries(filters).every(([key, value]) => {
          return doc.metadata[key] === value;
        });
      })
      .map(doc => ({
        document: doc,
        similarity: cosineSimilarity(queryVector, doc.vector)
      }))
      .sort((a, b) => b.similarity - a.similarity)
      .slice(0, topK);

    return results;
  }

  size(): number {
    return this.documents.size;
  }

  clear(): void {
    this.documents.clear();
  }
}

// Usage example
async function vectorStoreExample() {
  const store = new VectorStore(1536); // OpenAI embedding dimension

  // Add documents
  const embeddingService = new EmbeddingService();
  await embeddingService.init();

  const docs = [
    { id: 'doc1', text: 'Machine learning is a subset of AI', category: 'ai' },
    { id: 'doc2', text: 'React is a JavaScript library for building UIs', category: 'web' },
    { id: 'doc3', text: 'Python is widely used in data science', category: 'data' },
    { id: 'doc4', text: 'TensorFlow is an ML framework', category: 'ai' },
    { id: 'doc5', text: 'Node.js enables JavaScript on the server', category: 'web' }
  ];

  // Create embeddings and add to store
  for (const doc of docs) {
    const embedding = await embeddingService.embed(doc.text);
    store.add(doc.id, embedding, { category: doc.category, text: doc.text });
  }

  // Search
  const query = 'artificial intelligence and machine learning';
  const queryEmbedding = await embeddingService.embed(query);

  const results = store.search(queryEmbedding, 3, 0.5);

  console.log('Search results:');
  results.forEach((result, i) => {
    console.log(`${i + 1}. [${result.similarity.toFixed(3)}] ${result.document.metadata.text}`);
  });

  // Hybrid search
  const webResults = store.hybridSearch(queryEmbedding, { category: 'web' }, 2);
  console.log('\nWeb category results:', webResults);
}

Indexed Vector Store (HNSW)

For production applications, use approximate nearest neighbor (ANN) algorithms like HNSW:

typescript
// Simplified HNSW (Hierarchical Navigable Small World) implementation
class HNSWIndex {
  private layers: Map<number, Map<string, number[]>> = new Map();
  private maxConnections: number = 16;
  private maxLayers: number = 16;
  private entryPoint: string | null = null;

  constructor(private dimension: number) {}

  insert(id: string, vector: number[]): void {
    if (vector.length !== this.dimension) {
      throw new Error('Dimension mismatch');
    }

    // Determine number of layers for this node
    const numLayers = Math.floor(-Math.log(Math.random()) * this.maxLayers) + 1;

    for (let layer = 0; layer < numLayers; layer++) {
      if (!this.layers.has(layer)) {
        this.layers.set(layer, new Map());
      }

      this.layers.get(layer)!.set(id, vector);
    }

    // Set entry point if first node
    if (!this.entryPoint) {
      this.entryPoint = id;
    }
  }

  search(query: number[], topK: number): Array<{
    id: string;
    similarity: number;
  }> {
    if (!this.entryPoint) {
      return [];
    }

    // Start from top layer and work down
    let currentPoint = this.entryPoint;
    const visited = new Set<string>();

    for (let layer = this.maxLayers - 1; layer >= 0; layer--) {
      const layerNodes = this.layers.get(layer);
      if (!layerNodes) continue;

      // Greedy search at this layer
      let improved = true;
      while (improved) {
        improved = false;

        for (const [id, vector] of layerNodes.entries()) {
          if (visited.has(id)) continue;

          const similarity = cosineSimilarity(query, vector);
          const currentSimilarity = cosineSimilarity(
            query,
            layerNodes.get(currentPoint)!
          );

          if (similarity > currentSimilarity) {
            currentPoint = id;
            improved = true;
            visited.add(id);
          }
        }
      }
    }

    // Collect results from bottom layer
    const bottomLayer = this.layers.get(0);
    if (!bottomLayer) return [];

    const results = Array.from(bottomLayer.entries())
      .map(([id, vector]) => ({
        id,
        similarity: cosineSimilarity(query, vector)
      }))
      .sort((a, b) => b.similarity - a.similarity)
      .slice(0, topK);

    return results;
  }
}

// Usage
const index = new HNSWIndex(1536);
index.insert('doc1', embedding1);
index.insert('doc2', embedding2);

const results = index.search(queryEmbedding, 5);

Production Vector Databases

Pinecone Integration

typescript
import { Pinecone, PineconeRecord } from '@pinecone-database/pinecone';

class PineconeVectorStore {
  private pinecone: Pinecone;
  private index: any;

  constructor(apiKey: string, indexName: string) {
    this.pinecone = new Pinecone({ apiKey });
    this.index = this.pinecone.index(indexName);
  }

  async upsert(
    vectors: Array<{
      id: string;
      values: number[];
      metadata?: Record<string, any>;
    }>
  ): Promise<void> {
    await this.index.upsert(vectors);
  }

  async query(
    queryVector: number[],
    topK: number,
    filter?: Record<string, any>
  ): Promise<Array<{
    id: string;
    score: number;
    metadata: Record<string, any>;
  }>> {
    const response = await this.index.query({
      vector: queryVector,
      topK,
      filter,
      includeMetadata: true
    });

    return response.matches.map((match: any) => ({
      id: match.id,
      score: match.score,
      metadata: match.metadata
    }));
  }

  async delete(ids: string[]): Promise<void> {
    await this.index.deleteMany(ids);
  }
}

// Usage
const pineconeStore = new PineconeVectorStore(
  process.env.PINECONE_API_KEY!,
  'my-index'
);

// Insert documents
await pineconeStore.upsert([
  {
    id: 'doc1',
    values: embedding1,
    metadata: { category: 'ai', title: 'Introduction to ML' }
  },
  {
    id: 'doc2',
    values: embedding2,
    metadata: { category: 'web', title: 'React Basics' }
  }
]);

// Query
const results = await pineconeStore.query(
  queryEmbedding,
  5,
  { category: 'ai' } // Metadata filter
);

Weaviate Integration

typescript
import weaviate from 'weaviate-ts-client';

class WeaviateVectorStore {
  private client: any;

  constructor(url: string) {
    this.client = weaviate.client({
      scheme: 'http',
      host: url
    });
  }

  async createClass(className: string, dimension: number): Promise<void> {
    const classObj = {
      class: className,
      vectorizer: 'none', // We'll provide our own vectors
      properties: [
        { name: 'text', dataType: ['text'] },
        { name: 'category', dataType: ['string'] }
      ]
    };

    await this.client.schema.classCreator().withClass(classObj).do();
  }

  async add(
    className: string,
    dataObjects: Array<{
      id: string;
      vector: number[];
      properties: Record<string, any>;
    }>
  ): Promise<void> {
    const batcher = this.client.batch.objectsBatcher();
    const classNameWithCap = className.charAt(0).toUpperCase() + className.slice(1);

    dataObjects.forEach(obj => {
      batcher.withObject({
        class: classNameWithCap,
        id: obj.id,
        vector: obj.vector,
        properties: obj.properties
      });
    });

    await batcher.do();
  }

  async search(
    className: string,
    queryVector: number[],
    limit: number,
    where?: Record<string, any>
  ): Promise<any[]> {
    const classNameWithCap = className.charAt(0).toUpperCase() + className.slice(1);

    let builder = this.client.graphql
      .get()
      .withClassName(classNameWithCap)
      .withNearVector({ vector: queryVector })
      .withLimit(limit);

    if (where) {
      builder = builder.withWhere({
        operator: 'And',
        operands: Object.entries(where).map(([key, value]) => ({
          path: [key],
          operator: 'Equal',
          valueText: value
        }))
      });
    }

    const response = await builder.do();

    return response.data.Get[classNameWithCap];
  }
}

// Usage
const weaviateStore = new WeaviateVectorStore('localhost:8080');

await weaviateStore.createClass('Document', 1536);

await weaviateStore.add('Document', [
  {
    id: 'doc1',
    vector: embedding1,
    properties: { text: 'Machine learning...', category: 'ai' }
  }
]);

const results = await weaviateStore.search(
  'Document',
  queryEmbedding,
  5,
  { category: 'ai' }
);

Semantic Search Application

Complete Semantic Search System

typescript
class SemanticSearchEngine {
  private vectorStore: VectorStore;
  private embedder: EmbeddingService;
  private reranker: Reranker | null = null;

  constructor(dimension: number) {
    this.vectorStore = new VectorStore(dimension);
    this.embedder = new EmbeddingService();
  }

  async init(): Promise<void> {
    await this.embedder.init();
    // Optional: Initialize reranker for better results
    this.reranker = new Reranker();
  }

  async indexDocuments(
    documents: Array<{
      id: string;
      text: string;
      metadata?: Record<string, any>;
    }>
  ): Promise<void> {
    const batchSize = 10;
    for (let i = 0; i < documents.length; i += batchSize) {
      const batch = documents.slice(i, i + batchSize);
      const embeddings = await this.embedder.embedBatch(
        batch.map(doc => doc.text)
      );

      batch.forEach((doc, index) => {
        this.vectorStore.add(doc.id, embeddings[index], {
          ...doc.metadata,
          text: doc.text
        });
      });
    }
  }

  async search(
    query: string,
    options: {
      topK?: number;
      threshold?: number;
      filters?: Record<string, any>;
      rerank?: boolean;
    } = {}
  ): Promise<Array<{
    id: string;
    text: string;
    score: number;
    metadata: Record<string, any>;
  }>> {
    const {
      topK = 10,
      threshold = 0.7,
      filters,
      rerank = true
    } = options;

    // Embed query
    const queryEmbedding = await this.embedder.embed(query);

    // Initial retrieval
    let results = filters
      ? this.vectorStore.hybridSearch(queryEmbedding, filters, topK * 2)
      : this.vectorStore.search(queryEmbedding, topK * 2, threshold);

    // Rerank if enabled
    if (rerank && this.reranker) {
      results = await this.reranker.rerank(query, results, topK);
    } else {
      results = results.slice(0, topK);
    }

    return results.map(result => ({
      id: result.document.id,
      text: result.document.metadata.text,
      score: result.similarity,
      metadata: result.document.metadata
    }));
  }

  // Semantic recommendations
  async recommend(
    itemId: string,
    topK: number = 5
  ): Promise<Array<{
    id: string;
    score: number;
    metadata: Record<string, any>;
  }>> {
    const item = this.vectorStore.get(itemId);
    if (!item) {
      throw new Error(`Item ${itemId} not found`);
    }

    const results = this.vectorStore.search(item.vector, topK + 1, 0.5);
    // Exclude the item itself
    return results.filter(r => r.document.id !== itemId).slice(0, topK);
  }
}

// Reranker using cross-encoder
class Reranker {
  async rerank(
    query: string,
    candidates: Array<{
      document: VectorDocument;
      similarity: number;
    }>,
    topK: number
  ): Promise<Array<{
    document: VectorDocument;
    similarity: number;
  }>> {
    // Simple keyword-based reranking (can be replaced with ML model)
    const queryWords = new Set(query.toLowerCase().split(/\s+/));

    const reranked = candidates.map(candidate => {
      const text = candidate.document.metadata.text.toLowerCase();
      let keywordScore = 0;

      queryWords.forEach(word => {
        if (text.includes(word)) {
          keywordScore += 0.1;
        }
      });

      // Combine vector similarity with keyword score
      return {
        ...candidate,
        similarity: candidate.similarity + keywordScore
      };
    });

    return reranked
      .sort((a, b) => b.similarity - a.similarity)
      .slice(0, topK);
  }
}

// React component for semantic search
import { useState, useEffect } from 'react';

export function SemanticSearchComponent() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState<any[]>([]);
  const [loading, setLoading] = useState(false);
  const searchEngineRef = useRef<SemanticSearchEngine | null>(null);

  useEffect(() => {
    // Initialize search engine
    const initSearch = async () => {
      const engine = new SemanticSearchEngine(1536);
      await engine.init();

      // Index sample documents
      const docs = [
        {
          id: '1',
          text: 'Machine learning is a subset of artificial intelligence',
          metadata: { category: 'ai', author: 'John' }
        },
        {
          id: '2',
          text: 'React is a JavaScript library for building user interfaces',
          metadata: { category: 'web', author: 'Jane' }
        },
        {
          id: '3',
          text: 'Python is widely used in data science and machine learning',
          metadata: { category: 'data', author: 'Bob' }
        },
        {
          id: '4',
          text: 'TensorFlow is an open-source machine learning framework',
          metadata: { category: 'ai', author: 'Alice' }
        },
        {
          id: '5',
          text: 'Node.js enables JavaScript to run on the server side',
          metadata: { category: 'web', author: 'Charlie' }
        }
      ];

      await engine.indexDocuments(docs);
      searchEngineRef.current = engine;
    };

    initSearch();
  }, []);

  const handleSearch = async () => {
    if (!query || !searchEngineRef.current) return;

    setLoading(true);
    try {
      const searchResults = await searchEngineRef.current.search(query, {
        topK: 5,
        threshold: 0.5,
        rerank: true
      });
      setResults(searchResults);
    } catch (error) {
      console.error('Search error:', error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="max-w-2xl mx-auto p-6">
      <h1 className="text-3xl font-bold mb-6">Semantic Search</h1>

      <div className="flex gap-2 mb-6">
        <input
          type="text"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
          placeholder="Search..."
          className="flex-1 px-4 py-2 border rounded"
        />
        <button
          onClick={handleSearch}
          disabled={loading}
          className="px-6 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
        >
          {loading ? 'Searching...' : 'Search'}
        </button>
      </div>

      {results.length > 0 && (
        <div className="space-y-4">
          <h2 className="text-xl font-semibold">Results</h2>
          {results.map((result, i) => (
            <div key={result.id} className="p-4 bg-gray-50 rounded">
              <div className="flex justify-between items-start mb-2">
                <h3 className="font-semibold">{result.text}</h3>
                <span className="text-sm text-gray-500">
                  {(result.score * 100).toFixed(1)}%
                </span>
              </div>
              <div className="text-sm text-gray-600">
                Category: {result.metadata.category} | Author: {result.metadata.author}
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

RAG (Retrieval-Augmented Generation)

Building a RAG System

typescript
class RAGSystem {
  private vectorStore: VectorStore;
  private embedder: EmbeddingService;
  private llm: any; // Language model interface

  constructor() {
    this.vectorStore = new VectorStore(1536);
    this.embedder = new EmbeddingService();
  }

  async init(): Promise<void> {
    await this.embedder.init();
  }

  async indexKnowledgeBase(
    documents: Array<{
      id: string;
      content: string;
      metadata?: Record<string, any>;
    }>
  ): Promise<void> {
    // Chunk documents for better retrieval
    const chunks = this.chunkDocuments(documents, 500, 50);

    for (const chunk of chunks) {
      const embedding = await this.embedder.embed(chunk.content);
      this.vectorStore.add(chunk.id, embedding, {
        content: chunk.content,
        ...chunk.metadata
      });
    }
  }

  private chunkDocuments(
    documents: Array<{ id: string; content: string; metadata?: Record<string, any> }>,
    chunkSize: number,
    overlap: number
  ): Array<{ id: string; content: string; metadata?: Record<string, any> }> {
    const chunks: Array<{ id: string; content: string; metadata?: Record<string, any> }> = [];

    documents.forEach(doc => {
      const words = doc.content.split(/\s+/);
      for (let i = 0; i < words.length; i += (chunkSize - overlap)) {
        const chunkWords = words.slice(i, i + chunkSize);
        const chunkContent = chunkWords.join(' ');

        chunks.push({
          id: `${doc.id}_chunk_${chunks.length}`,
          content: chunkContent,
          metadata: {
            ...doc.metadata,
            sourceId: doc.id
          }
        });
      }
    });

    return chunks;
  }

  async query(
    question: string,
    contextCount: number = 3
  ): Promise<{
    answer: string;
    sources: Array<{ content: string; score: number }>;
  }> {
    // Retrieve relevant context
    const questionEmbedding = await this.embedder.embed(question);
    const relevantDocs = this.vectorStore.search(
      questionEmbedding,
      contextCount,
      0.6
    );

    const context = relevantDocs
      .map(doc => doc.document.metadata.content)
      .join('\n\n');

    // Generate answer using LLM with context
    const prompt = `Based on the following context, answer the question:

Context:
${context}

Question: ${question}

Answer:`;

    const answer = await this.llm.generate(prompt);

    return {
      answer,
      sources: relevantDocs.map(doc => ({
        content: doc.document.metadata.content,
        score: doc.similarity
      }))
    };
  }
}

// Usage
const rag = new RAGSystem();
await rag.init();

// Index knowledge base
await rag.indexKnowledgeBase([
  {
    id: 'doc1',
    content: 'TensorFlow is an open-source machine learning framework created by Google...',
    metadata: { source: 'tensorflow-docs', category: 'ml' }
  },
  {
    id: 'doc2',
    content: 'React is a JavaScript library for building user interfaces...',
    metadata: { source: 'react-docs', category: 'web' }
  }
]);

// Query
const response = await rag.query('What is TensorFlow?');
console.log('Answer:', response.answer);
console.log('Sources:', response.sources);

Performance Optimization

Caching Strategy

typescript
class CachedVectorStore {
  private vectorStore: VectorStore;
  private cache: Map<string, { results: any[]; timestamp: number }>;
  private cacheTimeout: number;

  constructor(vectorStore: VectorStore, cacheTimeout: number = 300000) {
    this.vectorStore = vectorStore;
    this.cache = new Map();
    this.cacheTimeout = cacheTimeout; // 5 minutes default
  }

  async search(
    query: string,
    queryVector: number[],
    options: any = {}
  ): Promise<any[]> {
    // Check cache
    const cacheKey = JSON.stringify({ query, options });
    const cached = this.cache.get(cacheKey);

    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      return cached.results;
    }

    // Perform search
    const results = this.vectorStore.search(queryVector, options.topK || 10, options.threshold || 0.7);

    // Cache results
    this.cache.set(cacheKey, {
      results,
      timestamp: Date.now()
    });

    return results;
  }

  clearCache(): void {
    this.cache.clear();
  }
}

Batch Processing

typescript
async function batchEmbed(
  texts: string[],
  batchSize: number = 100
): Promise<number[][]> {
  const embeddings: number[][] = [];

  for (let i = 0; i < texts.length; i += batchSize) {
    const batch = texts.slice(i, i + batchSize);
    const batchEmbeddings = await createBatchEmbeddings(batch);
    embeddings.push(...batchEmbeddings);
  }

  return embeddings;
}

Best Practices

1. Dimension Selection

typescript
// Trade-off between performance and accuracy
// Smaller dimensions (384): Faster, less accurate
// Medium dimensions (768, 1024): Balanced
// Large dimensions (1536, 3072): Slower, more accurate

const dimensionGuide = {
  fast: 384,   // For real-time applications
  balanced: 768, // For general use
  accurate: 1536 // For highest accuracy
};

2. Metadata Design

typescript
// Good metadata structure
const goodMetadata = {
  title: 'Document Title',
  category: 'ai',
  author: 'John Doe',
  createdAt: '2025-01-01',
  tags: ['ml', 'deep-learning'],
  source: 'research-paper'
};

// Avoid storing large text in metadata
const badMetadata = {
  title: 'Document Title',
  fullContent: '...very long text...', // Don't do this!
  // Store in separate document store instead
};

3. Monitoring and Analytics

typescript
class VectorStoreMetrics {
  private searchLatency: number[] = [];
  private queryCounts: Map<string, number> = new Map();

  recordSearch(latency: number, query: string): void {
    this.searchLatency.push(latency);

    const count = this.queryCounts.get(query) || 0;
    this.queryCounts.set(query, count + 1);
  }

  getAverageLatency(): number {
    const sum = this.searchLatency.reduce((a, b) => a + b, 0);
    return sum / this.searchLatency.length;
  }

  getTopQueries(limit: number = 10): Array<{ query: string; count: number }> {
    return Array.from(this.queryCounts.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, limit)
      .map(([query, count]) => ({ query, count }));
  }
}

Conclusion

Vector databases are fundamental to modern AI applications, enabling semantic search, recommendations, and RAG systems. By understanding embeddings, similarity metrics, and implementation patterns, you can build intelligent, context-aware applications that understand meaning beyond keyword matching.

Key takeaways:

  1. 1Choose the right embedding model for your use case
  2. 2Implement proper similarity metrics (cosine for text)
  3. 3Use ANN algorithms for production-scale applications
  4. 4Combine vector search with metadata filters for hybrid search
  5. 5Implement caching and batch processing for performance
  6. 6Monitor latency and query patterns for optimization

The field continues to evolve rapidly, with new techniques and technologies emerging regularly. Stay current with the latest developments to build cutting-edge AI applications!

Related essays

Next essay
NFT Marketplace Development: Building with ERC-721 and ERC-1155