LocalMode
Core

Vector Database

Create, query, and persist vector databases with HNSW indexing.

LocalMode includes a high-performance vector database with HNSW (Hierarchical Navigable Small World) indexing for fast approximate nearest neighbor search.

See it in action

Try Semantic Search and PDF Search for working demos of these APIs.

Creating a Database

import { createVectorDB } from '@localmode/core';

const db = await createVectorDB({
  name: 'my-documents',
  dimensions: 384, // Must match your embedding model
});

VectorDBConfig

Prop

Type

With Custom Storage

import { createVectorDB, MemoryStorage } from '@localmode/core';

// Use memory storage (no persistence)
const db = await createVectorDB({
  name: 'temp-db',
  dimensions: 384,
  storage: new MemoryStorage(),
});

// Or use a third-party adapter
import { DexieStorage } from '@localmode/dexie';

const db = await createVectorDB({
  name: 'dexie-db',
  dimensions: 384,
  storage: new DexieStorage({ name: 'my-app' }),
});

Adding Documents

Single Document

await db.add({
  id: 'doc-1',
  vector: embedding, // Float32Array
  metadata: {
    text: 'Original document text',
    source: 'file.pdf',
    page: 1,
  },
});

Multiple Documents

await db.addMany([
  { id: 'doc-1', vector: embeddings[0], metadata: { text: 'First' } },
  { id: 'doc-2', vector: embeddings[1], metadata: { text: 'Second' } },
  { id: 'doc-3', vector: embeddings[2], metadata: { text: 'Third' } },
]);

AddMany Options

OptionTypeDefaultDescription
onProgress(completed: number, total: number) => void-Callback invoked after each batch is inserted
batchSizenumber100Number of documents per batch
// Track insertion progress
await db.addMany(documents, {
  batchSize: 50,
  onProgress: (completed, total) => {
    console.log(`Inserted ${completed}/${total} documents`);
  },
});

Dimension Mismatch

The vector dimensions must match the dimensions specified when creating the database. Using a different size will throw a DimensionMismatchError.

Searching

const results = await db.search(queryVector, { k: 5 });

results.forEach((result) => {
  console.log(`ID: ${result.id}`);
  console.log(`Score: ${result.score.toFixed(4)}`);
  console.log(`Metadata:`, result.metadata);
});

With Filters

Filter results by metadata:

const results = await db.search(queryVector, {
  k: 10,
  filter: {
    source: { $eq: 'manual.pdf' },
  },
});

Filter Operators

Updating Documents

// Update metadata only (vector unchanged)
await db.update('doc-1', {
  metadata: { ...existingMetadata, status: 'reviewed' },
});

// Update vector and metadata
await db.update('doc-1', {
  vector: newEmbedding,
  metadata: { text: 'Updated text' },
});

Deleting Documents

// Delete single document
await db.delete('doc-1');

// Delete multiple documents
await db.deleteMany(['doc-1', 'doc-2', 'doc-3']);

// Clear all documents
await db.clear();

Delete by Filter

Delete documents matching a metadata filter:

// Delete all documents with a specific documentId
const deletedCount = await db.deleteWhere({
  documentId: 'doc-123',
});

console.log(`Deleted ${deletedCount} documents`);

// Delete documents matching multiple criteria
const count = await db.deleteWhere({
  $and: [
    { source: { $eq: 'old-import.pdf' } },
    { status: { $eq: 'archived' } },
  ],
});

Batch Deletion

Use deleteWhere() when you need to remove multiple documents by metadata (e.g., all chunks from a specific file). It's more efficient than deleting documents one by one.

Getting Documents

// Get by ID
const doc = await db.get('doc-1');
if (doc) {
  console.log(doc.id, doc.vector, doc.metadata);
}

// Check if exists
const exists = await db.has('doc-1');

// Get all IDs
const ids = await db.keys();

// Get count
const count = await db.size();

Persistence

By default, the vector database uses IndexedDB for persistence:

const db = await createVectorDB({
  name: 'persistent-db',
  dimensions: 384,
});

// Add documents
await db.addMany(documents);

// Data persists across page reloads!
// On next load, just create with same name:
const db2 = await createVectorDB({
  name: 'persistent-db', // Same name
  dimensions: 384,
});

// All documents are still there
const count = await db2.size();

Memory-Only Mode

For temporary data or testing:

import { MemoryStorage } from '@localmode/core';

const db = await createVectorDB({
  name: 'temp',
  dimensions: 384,
  storage: new MemoryStorage(),
});

// Data lost on page reload

Web Worker Mode

Offload database operations to a Web Worker for better main thread performance:

import { createVectorDBWithWorker } from '@localmode/core';

const db = await createVectorDBWithWorker({
  name: 'worker-db',
  dimensions: 384,
});

// Same API, but operations run in a worker
const results = await db.search(queryVector, { k: 5 });

Worker Benefits

Worker mode prevents blocking the main thread during: - Large batch insertions - Complex searches

  • Index rebuilding

HNSW Configuration

Tune the HNSW index for your use case:

const db = await createVectorDB({
  name: 'tuned-db',
  dimensions: 384,
  hnswConfig: {
    // More connections = better accuracy, more memory
    m: 32, // Default: 16

    // Higher = better index quality, slower builds
    efConstruction: 400, // Default: 200

    // Higher = better search accuracy, slower searches
    efSearch: 100, // Default: 50
  },
});

Configuration Guidelines

Use CasemefConstructionefSearch
Fast, low memory810030
Balanced (default)1620050
High accuracy32400100
Maximum accuracy48500200

Middleware

Add middleware for logging, encryption, etc.:

import { wrapVectorDB, loggingMiddleware } from '@localmode/core';

const baseDB = await createVectorDB({ name: 'db', dimensions: 384 });

const db = wrapVectorDB(baseDB, {
  beforeSearch: async (vector, options) => {
    console.log('Searching with k =', options.k);
    return { vector, options };
  },
  afterSearch: async (results) => {
    console.log('Found', results.length, 'results');
    return results;
  },
});

Type Safety

Full TypeScript support for metadata:

interface MyMetadata {
  text: string;
  source: string;
  page: number;
  tags: string[];
}

const db = await createVectorDB<MyMetadata>({
  name: 'typed-db',
  dimensions: 384,
});

// Type-safe add
await db.add({
  id: 'doc-1',
  vector: embedding,
  metadata: {
    text: 'Hello',
    source: 'file.pdf',
    page: 1,
    tags: ['intro'],
  },
});

// Type-safe search results
const results = await db.search(queryVector, { k: 5 });
results.forEach((r) => {
  // r.metadata is typed as MyMetadata
  console.log(r.metadata.text);
});

Next Steps

Showcase Apps

AppDescriptionLinks
Semantic SearchFull-text semantic search with VectorDBDemo · Source
Cross-Modal SearchMulti-modal vector storage and retrievalDemo · Source
Product SearchProduct catalog indexing and similarity searchDemo · Source
Smart GalleryImage vector storage for gallery organizationDemo · Source
PDF SearchPDF chunk storage and retrievalDemo · Source
LangChain RAGVectorDB-backed retrieval for RAGDemo · Source
Data MigratorImport/export vectors between storage formatsDemo · Source

On this page