# Verso

Verso is an embedded TypeScript vector database for Bun, Node.js, and browser
applications. Its stable API is intentionally small:

```text
VectorDB -> Collection<TMetadata> -> records and search
```

HNSW, storage backends, workers, WAL, and quantization are advanced extension
surfaces. They are not part of the root package contract.

## Install

```bash
bun add verso-db
# or
npm install verso-db
```

## Open, write, search

```typescript
import { VectorDB, type CollectionConfig } from 'verso-db';

type Document = {
  title: string;
  category: 'tech' | 'science';
  score: number;
  tags: string[];
};

const db = await VectorDB.open({ path: './vectors' });
const config: CollectionConfig<Document> = {
  vector: { dimensions: 768, metric: 'cosine' },
  index: { type: 'hnsw', profile: 'balanced' },
};
const documents = await db.createCollection<Document>('documents', config);

await documents.insert([
  {
    id: 'doc-1',
    vector: embedding,
    metadata: {
      title: 'HNSW explained',
      category: 'tech',
      score: 0.94,
      tags: ['vector', 'search'],
    },
  },
]);

const result = await documents.search({
  vector: queryEmbedding,
  limit: 10,
  filter: {
    $and: [
      { category: { $in: ['tech', 'science'] } },
      { score: { $gte: 0.8 } },
    ],
  },
});

for (const match of result.matches) {
  console.log(match.id, match.score, match.metadata?.title);
}

await db.close();
```

`distance` is metric-native and lower is always better. `score` is the stable
application-facing value and higher is always better: cosine uses similarity,
while Euclidean and dot-product scores are the negated metric distance.

## Collection operations

```typescript
await documents.upsert(records);
await documents.insertPacked({ ids, vectors: packedFloat32, metadata });
await documents.import(recordsAsyncIterable, { mode: 'upsert', batchSize: 2_000 });

const record = await documents.get('doc-1', {
  include: { vector: true },
});
const records = await documents.getMany(['doc-1', 'doc-2']);
const page = await documents.list({ limit: 100, cursor: pageCursor });

for await (const record of documents.scan({ filter })) {
  // export or inspect records without a large array allocation
}

await documents.update('doc-1', {
  metadata: { score: 0.97 }, // shallow merge
});
await documents.delete({ ids: ['doc-2'] });
await documents.delete({ filter: { category: 'science' } });
```

`insert` rejects duplicate IDs. `upsert` replaces the supplied vector and
metadata. `update` merges metadata and optionally replaces the vector. Deletes
are tombstones until `compact()` physically rebuilds the index.

## Filters

Filters are strict and support equality, comparisons, membership, existence,
array membership, prefixes, ranges, nested dotted paths, and Boolean
composition:

```typescript
import { where } from 'verso-db';

const filter = where.and(
  where.in('category', ['tech', 'science']),
  where.gte('score', 0.8),
  where.contains('tags', 'vector'),
);
```

Supported operators are `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`,
`$nin`, `$exists`, `$contains`, `$containsAny`, `$containsAll`, `$startsWith`,
`$between`, `$and`, `$or`, and `$not`. Comparisons never coerce strings and
numbers. Missing fields differ from explicit `null`; `$ne` and `$nin` match a
missing field, while positive comparisons do not. Metadata must be plain,
finite, JSON-compatible values.

## Search strategies and diagnostics

The ordinary API is independent of HNSW tuning:

```typescript
const exact = await documents.search({
  vector: queryEmbedding,
  limit: 10,
  strategy: 'exact',
});

const highRecall = await documents.search({
  vector: queryEmbedding,
  limit: 10,
  accuracy: 'high',
  tuning: { efSearch: 256, quantization: 'auto', oversampling: 4 },
  explain: true,
});

const many = await documents.searchMany([
  { vector: queryA, limit: 10 },
  { vector: queryB, limit: 10 },
], { concurrency: 'auto' });
```

Verso intentionally stops at vector retrieval. Full-text indexing, hybrid
fusion, query expansion, and model reranking belong in the application search
layer. Exact-vs-approximate recall comparison is available from the explicit
`verso-db/advanced` subpath for tuning and regression tests.

## Lifecycle, persistence, and operations

`VectorDB.open()` is asynchronous and discovers corruption or unavailable
persistence before returning. Node and Bun use filesystem storage; browsers
use OPFS when available. A requested persistent backend fails closed by
default. Use `{ storage: { type: 'opfs', fallback: 'memory' } }` only when an
explicit in-memory fallback is acceptable.

```typescript
const info = await documents.describe();
const stats = await documents.stats();
const report = await documents.verify();
const compacted = await documents.compact({ onProgress: console.log });

await db.setAlias('documents-current', 'documents');
const current = await db.collection('documents-current');
const snapshot = await db.snapshot();
await db.restore(snapshot, { overwrite: true });
const collectionSnapshot = await documents.exportSnapshot();
await db.importCollectionSnapshot(collectionSnapshot, { name: 'documents-copy' });
await db.backupTo('./backups/documents');
```

Use `durability: 'manual'` for bulk ingestion and call `flush()` explicitly;
the default is immediate persistence. `write()`/`batch()` provide a named
mutation session for application workflows, while `insertMany()` and
`import()`/`export()` support async iterables. `namespace(value)` creates a
scoped handle over an indexed metadata field.

## Package boundaries

The root export contains `VectorDB`, the generic `Collection` type, typed
record/filter/search types, `where`, and structured errors. Advanced APIs are
explicit:

```typescript
import { HNSWIndex, ScalarQuantizer } from 'verso-db/hnsw';
import { MemoryStorage, defineStorageAdapter } from 'verso-db/storage';
import { WorkerPool, WriteAheadLog } from 'verso-db/advanced';
```

The root does not export concrete storage implementations, raw HNSW classes,
WAL/worker protocols, or quantizer internals.

## Development

```bash
bun install
bun test
bun run build
bun run verify:dist
bun run test:browser
```

Performance measurements are workload-specific. Use the bundled benchmark
commands when making recall or latency claims.
