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
| Option | Type | Default | Description |
|---|---|---|---|
onProgress | (completed: number, total: number) => void | - | Callback invoked after each batch is inserted |
batchSize | number | 100 | Number 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
Basic Search
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 reloadWeb 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 Case | m | efConstruction | efSearch |
|---|---|---|---|
| Fast, low memory | 8 | 100 | 30 |
| Balanced (default) | 16 | 200 | 50 |
| High accuracy | 32 | 400 | 100 |
| Maximum accuracy | 48 | 500 | 200 |
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
RAG
Build retrieval-augmented generation pipelines.
Storage
Learn about storage options and persistence.
Showcase Apps
| App | Description | Links |
|---|---|---|
| Semantic Search | Full-text semantic search with VectorDB | Demo · Source |
| Cross-Modal Search | Multi-modal vector storage and retrieval | Demo · Source |
| Product Search | Product catalog indexing and similarity search | Demo · Source |
| Smart Gallery | Image vector storage for gallery organization | Demo · Source |
| PDF Search | PDF chunk storage and retrieval | Demo · Source |
| LangChain RAG | VectorDB-backed retrieval for RAG | Demo · Source |
| Data Migrator | Import/export vectors between storage formats | Demo · Source |