Migrations
Migrations exist at two levels:
- Collection document migrations (per-document
__v), and - Database schema migrations (database
schemaVersionstring)
1) Collection document migrations (__v)
Use collection.setSchema(schema, version) to set the "current" document version, and collection.addMigration(...) to upgrade older docs when they are read.
Good for:
- renaming fields
- changing shapes
- adding defaults
Example: rename tier -> plan (on read)
- TypeScript
- JavaScript
import { LioranManager } from "@liorandb/core";
import { z } from "zod";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
// v1 schema
const UserV1 = z.object({
_id: z.string().optional(),
email: z.string().email(),
tier: z.enum(["free", "pro"]),
__v: z.number().optional(),
});
users.setSchema(UserV1, 1);
// simulate an old stored doc (v1)
await users.insertOne({ email: "x@y.com", tier: "pro", __v: 1 });
// v2 schema
const UserV2 = z.object({
_id: z.string().optional(),
email: z.string().email(),
plan: z.enum(["free", "pro"]),
__v: z.number().optional(),
});
users.setSchema(UserV2, 2);
users.addMigration({
from: 1,
to: 2,
migrate: (doc) => ({ ...doc, plan: doc.tier ?? "free" }),
});
const migrated = await users.findOne({ email: "x@y.com" });
console.log(migrated);
await manager.close();
import { LioranManager } from "@liorandb/core";
import { z } from "zod";
const manager = new LioranManager({ rootPath: "./.liorandb" });
const db = await manager.db("app");
const users = db.collection("users");
// v1 schema
const UserV1 = z.object({
_id: z.string().optional(),
email: z.string().email(),
tier: z.enum(["free", "pro"]),
__v: z.number().optional(),
});
users.setSchema(UserV1, 1);
// simulate an old stored doc (v1)
await users.insertOne({ email: "x@y.com", tier: "pro", __v: 1 });
// v2 schema
const UserV2 = z.object({
_id: z.string().optional(),
email: z.string().email(),
plan: z.enum(["free", "pro"]),
__v: z.number().optional(),
});
users.setSchema(UserV2, 2);
users.addMigration({
from: 1,
to: 2,
migrate: (doc) => ({ ...doc, plan: doc.tier ?? "free" }),
});
const migrated = await users.findOne({ email: "x@y.com" });
console.log(migrated);
await manager.close();
Sandbox output (example)
{ _id: "...", email: "x@y.com", plan: "pro", __v: 2, tier: "pro" }
2) Database schema migrations (schemaVersion)
The database stores a schemaVersion string in its metadata file. You can register "upgrade steps" that run once and then bump the schema version.
db.getSchemaVersion() / db.setSchemaVersion(v)
- TypeScript
- JavaScript
getSchemaVersion(): string
setSchemaVersion(v: string): void
getSchemaVersion()
setSchemaVersion(v)
Writable mode only for setSchemaVersion.
db.migrate(from, to, fn)
- TypeScript
- JavaScript
migrate(from: string, to: string, fn: (db: LioranDB) => Promise<void>): void
migrate(from, to, fn)
Registers a step; after fn finishes, the DB schema version is set to to.
db.applyMigrations(targetVersion)
- TypeScript
- JavaScript
applyMigrations(targetVersion: string): Promise<void>
applyMigrations(targetVersion)
Applies registered migrations in order until there are no more pending steps.
Example: add an index in a migration
- 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) => {
db.collection("users");
await db.createIndex("users", "email", { unique: true });
});
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) => {
db.collection("users");
await db.createIndex("users", "email", { unique: true });
});
await db.applyMigrations("v2");
console.log(db.getSchemaVersion());
await manager.close();
Sandbox output (example)
v2
What gets created on disk?
Database migrations create two coordination files inside the database folder:
__migration.lock(temporary lock while a migration runs)__migration_history.json(history of applied migrations)