LioranDB
LioranDB represents a single database directory under the manager root.
It's where you:
- open collections (
db.collection(...)) - manage indexes (
db.createIndex(...),db.explain(...)) - run transactions (
db.transaction(...)) - compact storage (
db.compactCollection(...),db.compactAll()) - rotate encryption keys (
db.rotateEncryptionKey(...)) - coordinate DB-level migrations (
db.migrate(...),db.applyMigrations(...))
collection(name, schema?, schemaVersion?)
- TypeScript
- JavaScript
collection<T = any>(
name: string,
schema?: ZodSchema<T>,
schemaVersion?: number
): Collection<T>
collection(name, schema, schemaVersion)
name: collection directory name.schema(optional): a Zod schema used to validate writes and migrated reads.schemaVersion(optional): stored in documents as__v(defaults to1).- Returns a cached
Collectioninstance pername.
Example: open a collection
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
await users.insertOne({ email: "a@b.com" });
await manager.close();
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
await users.insertOne({ email: "a@b.com" });
await manager.close();
Sandbox output (example)
(no output)
Index API
createIndex(collection, field, options?)
- TypeScript
- JavaScript
createIndex(
collection: string,
field: string,
options?: { unique?: boolean }
): Promise<void>
createIndex(collection, field, options)
- Builds an index for existing documents, then keeps it updated on writes.
unique: trueenforces uniqueness for that field.
Example: unique index (email)
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
await db.createIndex("users", "email", { unique: true });
await users.insertOne({ email: "a@b.com" });
await manager.close();
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
await db.createIndex("users", "email", { unique: true });
await users.insertOne({ email: "a@b.com" });
await manager.close();
Sandbox output (example)
(no output)
explain(collection, query?, options?)
- TypeScript
- JavaScript
explain(collection: string, query?: any, options?: any): Promise<{
indexUsed?: string;
indexType?: "btree";
scannedDocuments: number;
returnedDocuments: number;
executionTimeMs: number;
usedFullScan: boolean;
candidateDocuments: number;
}>
explain(collection, query, options)
- Returns planner + execution stats for a query.
Example: see whether an index is used
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
await db.createIndex("users", "age");
await users.insertMany([{ age: 10 }, { age: 20 }, { age: 30 }]);
const plan = await db.explain("users", { age: { $gte: 18 } });
console.log({ indexUsed: plan.indexUsed, scanned: plan.scannedDocuments });
await manager.close();
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
await db.createIndex("users", "age");
await users.insertMany([{ age: 10 }, { age: 20 }, { age: 30 }]);
const plan = await db.explain("users", { age: { $gte: 18 } });
console.log({ indexUsed: plan.indexUsed, scanned: plan.scannedDocuments });
await manager.close();
Sandbox output (example)
{ indexUsed: "age", scanned: 2 }
Transactions
transaction(fn)
- TypeScript
- JavaScript
transaction<T>(fn: (tx: DBTransactionContext) => Promise<T>): Promise<T>
transaction(fn)
- Runs multiple operations as a single atomic commit (WAL-backed).
- Not allowed when the manager is in readonly mode.
Example: transfer between accounts (single commit)
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("bank");
await db.transaction(async (tx) => {
const accounts = tx.collection("accounts");
accounts.updateOne({ id: "a" }, { $inc: { balance: -50 } }, { upsert: true });
accounts.updateOne({ id: "b" }, { $inc: { balance: 50 } }, { upsert: true });
});
await manager.close();
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("bank");
await db.transaction(async (tx) => {
const accounts = tx.collection("accounts");
accounts.updateOne({ id: "a" }, { $inc: { balance: -50 } }, { upsert: true });
accounts.updateOne({ id: "b" }, { $inc: { balance: 50 } }, { upsert: true });
});
await manager.close();
Sandbox output (example)
(no output)
Migrations (database-level)
getSchemaVersion() / setSchemaVersion(v)
getSchemaVersion(): stringsetSchemaVersion(v: string): void(writable mode only)
migrate(from, to, fn)
migrate(
from: string,
to: string,
fn: (db: LioranDB) => Promise<void>
): void
- Registers a migration step that runs once when applying migrations.
- After
fncompletes, the DB schema version is set toto.
applyMigrations(targetVersion)
applyMigrations(targetVersion: string): Promise<void>
- Applies registered migrations up to the latest registered version.
Example: rename a collection (migration step)
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
db.migrate("v1", "v2", async (db) => {
// example "schema change": make sure a collection exists, or move data, etc.
await db.collection("users").insertOne({ migratedAt: Date.now() });
});
await db.applyMigrations("v2");
console.log(db.getSchemaVersion());
await manager.close();
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
db.migrate("v1", "v2", async (db) => {
// example "schema change": make sure a collection exists, or move data, etc.
await db.collection("users").insertOne({ migratedAt: Date.now() });
});
await db.applyMigrations("v2");
console.log(db.getSchemaVersion());
await manager.close();
Sandbox output (example)
v2
Maintenance
compactCollection(name: string): Promise<void>compactAll(): Promise<void>rotateEncryptionKey(newKey: string | Buffer): Promise<void>close(): Promise<void>
Example: compact a single collection
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
await db.compactCollection("users");
await manager.close();
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
await db.compactCollection("users");
await manager.close();
Sandbox output (example)
(no output)
Example: schema + collection
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
import { z } from "zod";
const manager = new LioranManager({ rootPath: "./data" });
const db = await manager.db("app");
const User = z.object({
_id: z.string().optional(),
email: z.string().email(),
age: z.number().int().nonnegative(),
__v: z.number().optional(),
});
const users = db.collection("users", User, 1);
await users.insertOne({ email: "u@example.com", age: 18 });
await manager.close();
Sandbox output (example)
(no output)
import { LioranManager } from "@liorandb/core";
import { z } from "zod";
const manager = new LioranManager({ rootPath: "./data" });
const db = await manager.db("app");
const User = z.object({
_id: z.string().optional(),
email: z.string().email(),
age: z.number().int().nonnegative(),
__v: z.number().optional(),
});
const users = db.collection("users", User, 1);
await users.insertOne({ email: "u@example.com", age: 18 });
await manager.close();
Sandbox output (example)
(no output)