Collection
A Collection stores JSON documents keyed by _id (string). It's the API you'll use most: insert, query, update, delete, aggregate.
Document IDs (_id)
- If you don't provide
_id, LioranDB generates one. _idcannot start with the reserved prefix\u0000__meta__:(used for internal metadata).
Schema validation (optional)
setSchema(schema, version)
- TypeScript
- JavaScript
setSchema(schema: ZodSchema<T>, version: number): void
setSchema(schema, version)
- Validates writes.
- Stores the current schema version in documents as
__v.
- TypeScript
- JavaScript
import { z } from "zod";
users.setSchema(
z.object({
_id: z.string().optional(),
email: z.string().email(),
__v: z.number().optional(),
}),
1
);
import { z } from "zod";
users.setSchema(
z.object({
_id: z.string().optional(),
email: z.string().email(),
__v: z.number().optional(),
}),
1
);
addMigration({ from, to, migrate })
- TypeScript
- JavaScript
addMigration(migration: { from: number; to: number; migrate: (doc: any) => T }): void
addMigration(migration)
- Upgrades documents on read (from older
__vto the current version). - Great for renames/defaults when you don't want to rewrite the whole collection immediately.
- TypeScript
- JavaScript
users.setSchema(UserV2, 2);
users.addMigration({
from: 1,
to: 2,
migrate: (doc) => ({ ...doc, plan: doc.tier ?? "free" }),
});
users.setSchema(UserV2, 2);
users.addMigration({
from: 1,
to: 2,
migrate: (doc) => ({ ...doc, plan: doc.tier ?? "free" }),
});
CRUD (every common function with a tiny example)
All writes are queued per collection to keep ordering consistent.
insertOne(doc)
- TypeScript
- JavaScript
const inserted = await users.insertOne({ email: "a@b.com", plan: "free" });
console.log(inserted);
const inserted = await users.insertOne({ email: "a@b.com", plan: "free" });
console.log(inserted);
Sandbox output (example)
{ _id: "...", email: "a@b.com", plan: "free", __v: 1 }
insertMany(docs, options?)
- TypeScript
- JavaScript
await users.insertMany([{ email: "a@b.com" }, { email: "c@d.com" }], { chunkSize: 500 });
await users.insertMany([{ email: "a@b.com" }, { email: "c@d.com" }], { chunkSize: 500 });
Sandbox output (example)
(no output)
find(query?, options?)
- TypeScript
- JavaScript
const list = await users.find(
{ plan: { $in: ["free", "pro"] } },
{ limit: 2, offset: 0, projection: ["email", "plan"] }
);
console.log(list);
const list = await users.find(
{ plan: { $in: ["free", "pro"] } },
{ limit: 2, offset: 0, projection: ["email", "plan"] }
);
console.log(list);
Sandbox output (example)
[ { email: "a@b.com", plan: "free" }, { email: "c@d.com", plan: "pro" } ]
findOne(query?, options?)
- TypeScript
- JavaScript
const one = await users.findOne({ email: "a@b.com" });
console.log(one?.email);
const one = await users.findOne({ email: "a@b.com" });
console.log(one?.email);
Sandbox output (example)
a@b.com
updateOne(filter, update, options?)
- TypeScript
- JavaScript
const updated = await users.updateOne(
{ email: "a@b.com" },
{ $set: { plan: "pro" }, $inc: { "stats.logins": 1 } }
);
console.log(updated);
const updated = await users.updateOne(
{ email: "a@b.com" },
{ $set: { plan: "pro" }, $inc: { "stats.logins": 1 } }
);
console.log(updated);
Sandbox output (example)
{ _id: "...", email: "a@b.com", plan: "pro", stats: { logins: 1 }, __v: 1 }
Upsert
- TypeScript
- JavaScript
const doc = await users.updateOne(
{ email: "new@x.com" },
{ $set: { email: "new@x.com", plan: "free" } },
{ upsert: true }
);
console.log(doc);
const doc = await users.updateOne(
{ email: "new@x.com" },
{ $set: { email: "new@x.com", plan: "free" } },
{ upsert: true }
);
console.log(doc);
Sandbox output (example)
{ _id: "...", email: "new@x.com", plan: "free", __v: 1 }
updateMany(filter, update)
- TypeScript
- JavaScript
const changed = await users.updateMany({ plan: "free" }, { $set: { plan: "pro" } });
console.log(changed.length);
const changed = await users.updateMany({ plan: "free" }, { $set: { plan: "pro" } });
console.log(changed.length);
Sandbox output (example)
2
deleteOne(filter)
- TypeScript
- JavaScript
console.log(await users.deleteOne({ email: "a@b.com" }));
console.log(await users.deleteOne({ email: "a@b.com" }));
Sandbox output (example)
true
deleteMany(filter)
- TypeScript
- JavaScript
console.log(await users.deleteMany({ plan: "free" }));
console.log(await users.deleteMany({ plan: "free" }));
Sandbox output (example)
3
count() / countDocuments(filter?)
- TypeScript
- JavaScript
console.log(await users.count());
console.log(await users.countDocuments({ plan: "pro" }));
console.log(await users.count());
console.log(await users.countDocuments({ plan: "pro" }));
Sandbox output (example)
10
4
Indexes (collection-level)
You can create indexes directly from a Collection as well:
createIndex(field, options?)
- TypeScript
- JavaScript
createIndex(field: string, options?: { unique?: boolean }): Promise<void>
createIndex(field, options)
Example: unique index (email)
- TypeScript
- JavaScript
await users.createIndex("email", { unique: true });
await users.insertOne({ email: "a@b.com", plan: "free" });
await users.createIndex("email", { unique: true });
await users.insertOne({ email: "a@b.com", plan: "free" });
Sandbox output (example)
(no output)
explain(query?, options?)
- TypeScript
- JavaScript
explain(query?: any, options?: FindOptions): Promise<any>
explain(query, options)
Example: check whether an index is used
- TypeScript
- JavaScript
await users.createIndex("plan");
const plan = await users.explain({ plan: "pro" }, { limit: 10 });
console.log(plan.indexUsed);
await users.createIndex("plan");
const plan = await users.explain({ plan: "pro" }, { limit: 10 });
console.log(plan.indexUsed);
Sandbox output (example)
plan
Streaming inserts
For large datasets, streamed inserts avoid building a huge array in memory:
insertManyStream(docs, options?)
- TypeScript
- JavaScript
insertManyStream(docs: Iterable<any> | AsyncIterable<any>, options?: { chunkSize?: number }): Promise<number>
insertManyStream(docs, options)
Example: insert 100k docs from a generator
- TypeScript
- JavaScript
function* generateDocs(count: number) {
for (let i = 0; i < count; i++) yield { n: i };
}
const inserted = await users.insertManyStream(generateDocs(100_000), { chunkSize: 1000 });
console.log(inserted);
function* generateDocs(count) {
for (let i = 0; i < count; i++) yield { n: i };
}
const inserted = await users.insertManyStream(generateDocs(100000), { chunkSize: 1000 });
console.log(inserted);
Sandbox output (example)
100000
Aggregation
aggregate(pipeline)
Supported stages: $match, $project, $skip, $limit, $group.
- TypeScript
- JavaScript
const out = await users.aggregate([{ $group: { _id: "plan", count: { $sum: 1 } } }]);
console.log(out);
const out = await users.aggregate([{ $group: { _id: "plan", count: { $sum: 1 } } }]);
console.log(out);
Sandbox output (example)
[ { _id: "free", count: 7 }, { _id: "pro", count: 3 } ]
Maintenance
compact()
Compaction rebuilds the collection to reduce fragmentation and then rebuilds indexes.
- TypeScript
- JavaScript
await users.compact();
await users.compact();
Sandbox output (example)
(no output)
End-to-end example (CRUD)
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const items = db.collection<{ _id?: string; sku: string; qty: number }>("items");
await items.insertMany([{ sku: "A", qty: 10 }, { sku: "B", qty: 2 }]);
await items.updateOne({ sku: "B" }, { $inc: { qty: 1 } });
console.log(await items.find({ qty: { $gte: 3 } }));
await manager.close();
Sandbox output (example)
[ { _id: "...", sku: "A", qty: 10, __v: 1 }, { _id: "...", sku: "B", qty: 3, __v: 1 } ]
import { LioranManager } from "@liorandb/core";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const items = db.collection("items");
await items.insertMany([{ sku: "A", qty: 10 }, { sku: "B", qty: 2 }]);
await items.updateOne({ sku: "B" }, { $inc: { qty: 1 } });
console.log(await items.find({ qty: { $gte: 3 } }));
await manager.close();
Sandbox output (example)
[ { _id: "...", sku: "A", qty: 10, __v: 1 }, { _id: "...", sku: "B", qty: 3, __v: 1 } ]