Node.js API Reference
Complete reference for the vectlite npm package.
const vectlite = require('vectlite')
// or
import * as vectlite from 'vectlite'
Module Functions
open
function open(path: string, options?: OpenOptions): Database
Open or create a vectlite database.
| Parameter | Type | Description |
|---|---|---|
path | string | Path to the .vdb file. Created if it does not exist. |
options | OpenOptions | Optional configuration. |
OpenOptions
| Property | Type | Default | Description |
|---|---|---|---|
dimension | number | -- | Vector dimension. Required when creating a new database. |
metric | string | 'cosine' | Distance metric used at creation time. One of 'cosine', 'euclidean' (alias 'l2'), 'dotproduct' (aliases 'dot', 'ip', 'inner_product', 'dot_product'), or 'manhattan' (alias 'l1'). Persisted in the database file. Ignored when opening an existing database. Scores are always oriented so higher is better. |
readOnly | boolean | false | Open in read-only mode. |
lockTimeout | number | -- | Maximum seconds to wait when acquiring the advisory file lock. Omit to fail immediately on contention. |
Returns: Database
openStore
function openStore(root: string): Store
Open or create a collection store directory.
| Parameter | Type | Description |
|---|---|---|
root | string | Path to the store directory. |
Returns: Store
restore
function restore(source: string, dest: string): Database
Restore a backup to a new database path.
| Parameter | Type | Description |
|---|---|---|
source | string | Path to the backup directory. |
dest | string | Path for the restored .vdb file. |
Returns: Database
sparseTerms
function sparseTerms(text: string): SparseVector
Tokenize and weight text into a sparse term vector for BM25 search.
| Parameter | Type | Description |
|---|---|---|
text | string | Input text. |
Returns: SparseVector (Record<string, number>)
upsertText
function upsertText(
db: Database,
id: string,
text: string,
embed: (text: string) => number[] | Promise<number[]>,
metadata?: Metadata,
options?: { namespace?: string }
): void | Promise<void>
High-level helper that generates a dense embedding and sparse terms from text, then upserts the record. If embed returns a Promise, the function is async.
| Parameter | Type | Description |
|---|---|---|
db | Database | Target database. |
id | string | Record identifier. |
text | string | Text to embed and index. |
embed | (text: string) => number[] | Promise<number[]> | Embedding function. |
metadata | Metadata | Optional metadata object. |
options.namespace | string | Optional namespace. |
searchText
function searchText(
db: Database,
text: string,
embed: (text: string) => number[] | Promise<number[]>,
options?: SearchOptions
): SearchResult[] | Promise<SearchResult[]>
High-level hybrid search. Generates a dense embedding and sparse terms from text, then runs a fused search. Async if embed returns a Promise.
| Parameter | Type | Description |
|---|---|---|
db | Database | Target database. |
text | string | Natural-language query. |
embed | (text: string) => number[] | Promise<number[]> | Embedding function. |
options | SearchOptions | Search configuration (see SearchOptions). |
Returns: SearchResult[]
searchTextWithStats
function searchTextWithStats(
db: Database,
text: string,
embed: (text: string) => number[] | Promise<number[]>,
options?: SearchOptions
): SearchResponse | Promise<SearchResponse>
Same as searchText but returns a SearchResponse with results and query statistics.
configureOpenTelemetry
function configureOpenTelemetry(
config?: boolean | {
tracer?: unknown
tracerName?: string
enabled?: boolean
}
): unknown
Enable, disable, or configure optional OpenTelemetry tracing for search operations. When active, every searchText() and searchTextWithStats() call is wrapped in a vectlite.search span carrying db.system, db.operation.name, and search-specific attributes (vectlite.search.k, .namespace, .has_dense, .has_sparse, .fusion, .used_ann, .result_count, .total_us).
@opentelemetry/api is loaded lazily and is not a runtime dependency. If a search throws, the span records the exception and sets an error status before re-throwing.
| Form | Behaviour |
|---|---|
configureOpenTelemetry() | Auto-detect: resolves a tracer from @opentelemetry/api if installed. |
configureOpenTelemetry({ tracer: myTracer }) | Use a user-supplied tracer instance. |
configureOpenTelemetry({ tracerName: 'my-app' }) | Override the tracer name (default 'vectlite'). |
configureOpenTelemetry({ enabled: false }) or configureOpenTelemetry(false) | Disable tracing. |
Returns: the active tracer (or null when disabled).
Database
Returned by open() and restore().
Properties
| Property | Type | Description |
|---|---|---|
path | string | Absolute path to the .vdb file. |
walPath | string | Path to the write-ahead log file. |
dimension | number | Vector dimension. |
metric | string | Distance metric: 'cosine', 'euclidean', 'dotproduct', or 'manhattan'. |
readOnly | boolean | Whether opened in read-only mode. |
quantizationMethod | string | null | Active quantization method ('scalar', 'binary', 'product') or null. |
quantizationMethod is a property. Use db.isQuantized() as a method to check whether quantization is enabled.
count
count(options?: { namespace?: string; filter?: Filter }): number
Return the number of records, optionally scoped by namespace and/or filter.
namespaces
namespaces(): string[]
Return all namespace names.
transaction
transaction(): Transaction
Begin a new transaction.
Returns: Transaction
insert
insert(
id: string,
vector: number[],
metadata?: Metadata,
options?: WriteOptions
): void
Insert a new record. Throws VectLiteError if the ID already exists.
upsert
upsert(
id: string,
vector: number[],
metadata?: Metadata,
options?: WriteOptions
): void
Insert or replace a record.
WriteOptions
| Property | Type | Description |
|---|---|---|
namespace | string | Target namespace. |
sparse | SparseVector | Sparse term vector for BM25. |
vectors | NamedVectors | Additional named vectors. |
ttl | number | Time-to-live in seconds. Expired records are filtered from reads and garbage-collected on compact(). |
insertMany
insertMany(records: Record[], options?: { namespace?: string }): number
Batch insert. Returns the number of records inserted.
upsertMany
upsertMany(records: Record[], options?: { namespace?: string }): number
Batch upsert. Returns the number of records upserted.
bulkIngest
bulkIngest(
records: Record[],
options?: BulkIngestOptions
): number
Ingest a batch of records efficiently.
BulkIngestOptions
| Property | Type | Default | Description |
|---|---|---|---|
namespace | string | -- | Target namespace. |
batchSize | number | 10000 | Internal WAL batch size. |
Returns: number -- total records ingested.
get
get(id: string, options?: { namespace?: string }): Record | null
Retrieve a record by ID. Returns null if not found.
delete
delete(id: string, options?: { namespace?: string }): boolean
Delete a record. Returns true if the record existed.
deleteMany
deleteMany(ids: string[], options?: { namespace?: string }): number
Delete multiple records. Returns the count of records deleted.
deleteByFilter
deleteByFilter(filter: Filter, options?: { namespace?: string }): number
Delete every record that matches filter in a single pass. Returns the number of records deleted.
const deleted = db.deleteByFilter({ stale: true }, { namespace: 'docs' })
updateMetadata
updateMetadata(
id: string,
metadata: Metadata,
options?: { namespace?: string }
): boolean
Merge a metadata patch into an existing record without re-writing the vector. Keys present in metadata overwrite existing keys; other keys are left untouched. Skips ANN, sparse, quantized, and multi-vector index rebuilds when a WAL batch contains only metadata updates.
Returns true if the record was found and updated.
db.updateMetadata('doc1', { status: 'reviewed', score: 0.95 })
list
list(options?: {
namespace?: string
filter?: Filter
limit?: number // 0 = no limit
offset?: number
}): Record[]
List records without issuing a vector query.
listCursor
listCursor(options?: {
namespace?: string
filter?: Filter
limit?: number // default: 100
cursor?: string | null
}): { records: Record[]; cursor: string | null }
Cursor-based pagination for efficient iteration over large collections. cursor is null when the iterator is exhausted.
let cursor = null
do {
const page = db.listCursor({ limit: 100, cursor })
for (const record of page.records) process(record)
cursor = page.cursor
} while (cursor)
setTtl
setTtl(
id: string,
ttlSecs: number,
options?: { namespace?: string }
): boolean
Set a time-to-live (in seconds from now) on an existing record. Expired records are transparently filtered from get(), list(), count(), and search(), and are permanently removed on compact(). The expiresAt field on Record exposes the absolute expiry timestamp (epoch seconds).
clearTtl
clearTtl(id: string, options?: { namespace?: string }): boolean
Remove the expiry from a record so it lives indefinitely.
createIndex
createIndex(field: string, indexType: 'keyword' | 'numeric'): void
Create a payload index on a metadata field to accelerate filtered queries 10-100x on large collections. Indexes are automatically used by search(), count(), and list() to narrow candidates before full filter evaluation. AND filters intersect index results; OR filters union when all sub-filters are indexed. Index definitions persist across close/reopen in a .vdb.pidx sidecar file.
db.createIndex('source', 'keyword') // string equality, $in
db.createIndex('score', 'numeric') // range queries: $gt, $gte, $lt, $lte
dropIndex
dropIndex(field: string): boolean
Remove a payload index. Returns true if the index existed.
listIndexes
listIndexes(): Array<[string, string]>
Return all active payload indexes as [field, indexType] tuples.
enableQuantization
enableQuantization(
method: 'scalar' | 'binary' | 'product' | 'pq',
options?: {
rescoreMultiplier?: number // default: 4 — fetches k × rescoreMultiplier candidates
numSubVectors?: number // PQ only; omit for a dimension-aware default
numCentroids?: number // PQ only, default: 256
}
): void
Enable vector quantization to reduce memory and accelerate search. All methods use a 2-stage pipeline: fast quantized candidate selection followed by exact float32 rescoring. Parameters persist in a .vdb.quant sidecar file and reload automatically on open. Indexes rebuild automatically on inserts, upserts, and bulk ingestion. Method names are parsed case-insensitively.
| Method | Compression | Notes |
|---|---|---|
scalar | ~4x (int8) | Minimal recall loss. |
binary | ~32x | Best for normalized embeddings (Hamming distance filtering). |
product / pq | configurable | k-means PQ for very large datasets. |
db.enableQuantization('scalar')
db.enableQuantization('binary', { rescoreMultiplier: 10 })
db.enableQuantization('pq') // dimension-aware default for numSubVectors
db.enableQuantization('product', { numSubVectors: 16, numCentroids: 256 })
disableQuantization
disableQuantization(): void
Disable quantization and remove the persisted .vdb.quant sidecar.
isQuantized
isQuantized(): boolean
Whether vector quantization is currently enabled. Method, not a property since 0.9.1.
validNumSubVectors
validNumSubVectors(): number[]
Return the list of valid numSubVectors values for this database's dimension (i.e. the divisors that allow an even split). Useful before calling enableQuantization('pq', { numSubVectors: ... }).
upsertMultiVectors
upsertMultiVectors(
id: string,
vector: number[],
multiVectors: Record<string, number[][]>,
metadata?: Metadata,
options?: { namespace?: string; sparse?: SparseVector }
): void
Insert or replace a record carrying per-token (ColBERT, ColPali) embeddings for late-interaction search. vector is the usual dense embedding; multiVectors maps space names to a list of token vectors.
db.upsertMultiVectors(
'doc1',
denseVector,
{ colbert: [tokenVec1, tokenVec2 /* ... */] },
{ source: 'paper' }
)
searchMultiVector
searchMultiVector(
space: string,
queryTokens: number[][],
options?: {
k?: number
namespace?: string
filter?: Filter
}
): SearchResult[]
MaxSim late-interaction search: for each query token, take the max cosine similarity against all document tokens, then sum across query tokens.
enableMultiVectorQuantization
enableMultiVectorQuantization(space: string): void
Enable ColBERTv2-style 2-bit quantization (~16x compression) for a multi-vector space. Parameters persist in a .vdb.mvquant.<space> sidecar file.
disableMultiVectorQuantization
disableMultiVectorQuantization(space: string): void
Disable 2-bit quantization for a multi-vector space.
isMultiVectorQuantized
isMultiVectorQuantized(space: string): boolean
Whether a multi-vector space currently has 2-bit quantization enabled.
close
close(): void
Flush pending state, release the file lock, and invalidate the handle. Subsequent operations that return data throw VectLiteError.
Async methods
Non-blocking versions of heavy operations, scheduled on the libuv threadpool via napi::Task (no tokio dependency). Use them in hot HTTP paths to avoid stalling the event loop.
db.searchAsync(vector: number[] | null, options?: SearchOptions): Promise<SearchResult[]>
db.searchWithStatsAsync(vector: number[] | null, options?: SearchOptions): Promise<SearchResponse>
db.bulkIngestAsync(records: Record[], options?: BulkIngestOptions): Promise<number>
db.flushAsync(): Promise<void>
db.compactAsync(): Promise<void>
const results = await db.searchAsync(queryVector, { k: 10, filter: { source: 'blog' } })
flush
flush(): void
Alias for compact() in the current package.
compact
compact(): void
Merge WAL into the main .vdb file and rebuild ANN indexes as needed.
snapshot
snapshot(dest: string): void
Create a self-contained database copy at dest.
backup
backup(dest: string): void
Full backup of .vdb and ANN sidecar files to dest directory.
search
search(
vector: number[] | null,
options?: SearchOptions
): SearchResult[]
// Shorthand: second arg is k (since 0.9.2)
search(vector: number[] | null, k: number): SearchResult[]
// Single-object form (since 0.9.1)
search(options: SearchOptions & { query: number[] | null }): SearchResult[]
Run a search query. Pass null as the vector for sparse-only or multi-vector search.
// Positional form
db.search(queryVector, { k: 10, filter: { source: 'blog' } })
// Shorthand — second arg is k (since 0.9.2)
db.search(queryVector, 10)
// Single-object form (since 0.9.1)
db.search({ query: queryVector, k: 10, filter: { source: 'blog' } })
All three forms are also supported by searchWithStats, searchAsync, and searchWithStatsAsync.
Search now rejects query vectors whose length does not match the database dimension, returning DimensionMismatch. Previously, undersized queries were silently truncated via Matryoshka logic. To opt into prefix search, pass truncateDim in the options. An all-zeros vector also raises an error for cosine / dot-product metrics.
Returns: SearchResult[]
searchWithStats
searchWithStats(
vector: number[] | null,
options?: SearchOptions
): SearchResponse
Same as search() but returns a SearchResponse.
Store
Returned by openStore(). Manages a directory of collections.
Properties
| Property | Type | Description |
|---|---|---|
root | string | Absolute path to the store directory. |
collections
collections(): string[]
List all collection names.
createCollection
createCollection(name: string, dimension: number): Database
Create a new collection. Throws if it already exists.
openOrCreateCollection
openOrCreateCollection(name: string, dimension: number): Database
Open or create a collection.
openCollection
openCollection(name: string): Database
Open an existing collection. Throws if it does not exist.
dropCollection
dropCollection(name: string): boolean
Delete a collection and its data from disk. Returns true if it existed.
close
close(): void
Symmetry with Database.close(). The store holds no open file handles, so this is currently a no-op, but using it future-proofs your code across binding versions. Available since 0.9.1.
Transaction
Returned by Database.transaction(). Node.js does not have a with statement, so you must call commit() or rollback() manually.
const tx = db.transaction()
try {
tx.upsert('doc1', emb1, { source: 'a' })
tx.delete('old_doc')
tx.commit()
} catch (err) {
tx.rollback()
throw err
}
insert
insert(
id: string,
vector: number[],
metadata?: Metadata,
options?: WriteOptions
): void
upsert
upsert(
id: string,
vector: number[],
metadata?: Metadata,
options?: WriteOptions
): void
insertMany
insertMany(records: Record[], options?: { namespace?: string }): number
upsertMany
upsertMany(records: Record[], options?: { namespace?: string }): number
delete
delete(id: string, options?: { namespace?: string }): boolean
commit
commit(): void
Commit all queued operations atomically.
rollback
rollback(): void
Discard all queued operations.
count
count(): number
Number of queued operations in the transaction.
Interfaces
OpenOptions
interface OpenOptions {
dimension?: number
readOnly?: boolean
}
WriteOptions
interface WriteOptions {
namespace?: string
sparse?: SparseVector
vectors?: NamedVectors
}
SearchOptions
interface SearchOptions {
k?: number // default: 10
filter?: Filter
namespace?: string
allNamespaces?: boolean // default: false
sparse?: SparseVector
denseWeight?: number // default: 1.0
sparseWeight?: number // default: 1.0
fetchK?: number // default: 0
mmrLambda?: number
vectorName?: string
fusion?: 'linear' | 'rrf' // default: 'linear'
rrfK?: number // default: 60
queryVectors?: NamedVectors
vectorWeights?: Record<string, number>
explain?: boolean // default: false
truncateDim?: number // Matryoshka prefix search
}
truncateDim enables Matryoshka prefix search: query and database vectors are truncated to the first truncateDim dimensions before scoring. Required when the query length is shorter than the database dimension; otherwise the engine throws DimensionMismatch.
BulkIngestOptions
interface BulkIngestOptions {
namespace?: string
batchSize?: number // default: 10000
}
Record
interface Record {
namespace: string
id: string
vector: number[]
vectors: NamedVectors
multiVectors?: Record<string, number[][]>
sparse: SparseVector
metadata: Metadata
expiresAt?: number | null // epoch seconds (TTL)
}
expiresAt is the absolute expiry timestamp in epoch seconds when a TTL is set (otherwise null).
SearchResult
interface SearchResult {
namespace: string
id: string
score: number
dense_score: number
sparse_score: number
vector_name: string | null
matched_terms: string[]
dense_rank: number | null
sparse_rank: number | null
metadata: Metadata
bm25_term_scores: Record<string, number>
explain?: ExplainDetails
}
ExplainDetails
interface ExplainDetails {
fusion?: string
dense_score?: number
sparse_score?: number
matched_terms?: string[]
vector_name?: string | null
dense_rank?: number | null
sparse_rank?: number | null
bm25_term_scores?: Record<string, number>
}
SearchStats
interface SearchStats {
used_ann: boolean
ann_candidate_count: number
exact_fallback: boolean
considered_count: number
fetch_k: number
mmr_applied: boolean
sparse_candidate_count: number
ann_loaded_from_disk: boolean
wal_entries_replayed: number
fusion: string
rerank_applied: boolean
rerank_count: number
timings: SearchTimings
}
SearchTimings
interface SearchTimings {
dense_us: number
sparse_us: number
fusion_us: number
total_us: number
}
SearchResponse
interface SearchResponse {
results: SearchResult[]
stats: SearchStats
}
MetadataValue
type MetadataValue = string | number | boolean | null | MetadataValue[] | { [key: string]: MetadataValue }
Metadata
type Metadata = Record<string, MetadataValue>
SparseVector
type SparseVector = Record<string, number>
NamedVectors
type NamedVectors = Record<string, number[]>
Filter
type Filter = Record<string, any>
MongoDB-style filter expression. See Metadata Filters for the full query syntax.
Error
VectLiteError
class VectLiteError extends Error {
constructor(message: string)
}
Thrown for all vectlite errors including:
- Write operations on a read-only database
- Duplicate ID on
insert() - Dimension mismatch
- Corrupt database file
- File lock contention
- I/O errors