Presets
A preset is a named refinement you call on the table.
Where this fits
The table is declared. A preset names a where you would otherwise repeat, such as notes.pending().
A preset receives a builder and returns that builder. where is the only method. It adds a predicate. It cannot remove or replace one. The caller's where stays first. Every added predicate joins it with AND. The tenant predicate and an active set are not in the builder. The planner puts them around the preset.
Return the builder you were given. Anything else is OKM1121: Preset pending on notes must return the builder it was given.
inspect() records one line per call, such as pending filters status, from table notes or trait flagged. A preset that adds no where records filters nothing.
where
field
A fixed value. notes.pending() keeps status equal to "pending".
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
const notes = table(
"notes",
{ id: t.identity(), title: t.text(), status: t.text() },
{ presets: { pending: (q) => q.where({ status: "pending" }) } },
);
const app = schema({ tables: [notes] });
export async function pending(url: string) {
await using db = connect(url, { schema: app });
return db.notes.pending().find({ limit: 20 });
}argument
The arguments after the builder are the method's arguments.
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
const notes = table(
"notes",
{ id: t.identity(), title: t.text(), ownerId: t.uuid() },
{
presets: {
ownedBy: (q, owner: string) => q.where({ ownerId: owner }),
},
},
);
const app = schema({ tables: [notes] });
export async function mine(url: string, owner: string) {
await using db = connect(url, { schema: app });
return db.notes.ownedBy(owner).find({ limit: 20 });
}list
A rest argument passes a list. inList comes from okmodel/pg.
import { connect } from "okmodel/pg/postgresjs";
import { inList, schema, table, t } from "okmodel/pg";
const notes = table(
"notes",
{ id: t.identity(), status: t.text() },
{
presets: {
inState: (q, ...states: readonly string[]) => q.where({ status: inList(states) }),
},
},
);
const app = schema({ tables: [notes] });
export async function openOrPending(url: string) {
await using db = connect(url, { schema: app });
return db.notes.inState("open", "pending").find({ limit: 20 });
}bound
gte is a predicate, the same as a fixed value.
import { connect } from "okmodel/pg/postgresjs";
import { gte, schema, table, t } from "okmodel/pg";
const notes = table(
"notes",
{ id: t.identity(), priority: t.integer() },
{ presets: { urgent: (q) => q.where({ priority: gte(3) }) } },
);
const app = schema({ tables: [notes] });
export async function urgent(url: string) {
await using db = connect(url, { schema: app });
return db.notes.urgent().find({ limit: 20 });
}or
or takes an array of filters. or(a, b) is OKM1121: Pass an array: or([a, b]).
import { connect } from "okmodel/pg/postgresjs";
import { or, schema, table, t } from "okmodel/pg";
const notes = table(
"notes",
{ id: t.identity(), status: t.text(), ownerId: t.uuid() },
{
presets: {
mineOrOpen: (q, owner: string) =>
q.where(or([{ ownerId: owner }, { status: "open" }])),
},
},
);
const app = schema({ tables: [notes] });
export async function mineOrOpen(url: string, owner: string) {
await using db = connect(url, { schema: app });
return db.notes.mineOrOpen(owner).find({ limit: 20 });
}several
Each where adds. The first predicate stays.
import { connect } from "okmodel/pg/postgresjs";
import { gte, schema, table, t } from "okmodel/pg";
const notes = table(
"notes",
{ id: t.identity(), status: t.text(), priority: t.integer() },
{
presets: {
stale: (q) => q.where({ status: "pending" }).where({ priority: gte(2) }),
},
},
);
const app = schema({ tables: [notes] });
export async function stale(url: string) {
await using db = connect(url, { schema: app });
return db.notes.stale().find({ limit: 20 });
}Calls
A preset filters find, one, count, exists, page, and aggregate. It also filters update, delete, archive, and restore. Insert does not use it.
A call does not replace the handle it started from. const pending = db.notes.pending() still means pending after pending.ownedBy(owner).
Update and delete still need their own where. A preset does not stand in for one. notes.pending().delete({}) is OKM1102: delete on notes needs a where. Pass a filter, or .all(reason). .all("clear the backlog") matches every pending row.
simple
One preset, then find.
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
const notes = table(
"notes",
{ id: t.identity(), title: t.text(), status: t.text() },
{ presets: { pending: (q) => q.where({ status: "pending" }) } },
);
const app = schema({ tables: [notes] });
export async function pending(url: string) {
await using db = connect(url, { schema: app });
return db.notes.pending().find({ limit: 20 });
}complex
The chain is pending, then ownedBy, then urgent. The caller's where stays first. Update uses the same pending filter beside its own where.
import { connect } from "okmodel/pg/postgresjs";
import { gte, schema, table, t } from "okmodel/pg";
const notes = table(
"notes",
{
id: t.identity(),
title: t.text(),
status: t.text(),
ownerId: t.uuid(),
priority: t.integer(),
},
{
presets: {
pending: (q) => q.where({ status: "pending" }),
ownedBy: (q, owner: string) => q.where({ ownerId: owner }),
urgent: (q) => q.where({ priority: gte(3) }),
},
},
);
const app = schema({ tables: [notes] });
export async function review(url: string, owner: string, id: string) {
await using db = connect(url, { schema: app });
const rows = await db.notes
.pending()
.ownedBy(owner)
.urgent()
.find({ where: { title: "ship" }, limit: 20 });
await db.notes.pending().update({ where: { id }, set: { title: "Done" } });
return rows;
}Names
A reserved name is a type error. On a trait, trait() throws OKM1040 at the call. On a table, connect throws it before the first query: Preset find on table notes is a client method or a reserved name. Reserved: find, one, page, aggregate, count, exists, insert, update, delete, descendants, ancestors, sync, archive, restore, withArchived, onlyArchived, then, lock, watch, subscribe, stream, inspect, explain, with, for, as.
Traits
A trait can add presets. They see the trait's columns. The table's own names are already in the map, so a trait that repeats one is OKM1040: Preset pending on notes is defined by table notes and by trait flagged. Two traits that share a name are Preset pending on notes is defined by trait flagged and by trait other. Schema traits apply first. See traits.
import { connect } from "okmodel/pg/postgresjs";
import { schema, table, t } from "okmodel/pg";
import { trait } from "okmodel/traits";
const flagged = trait("flagged", {
fields: { flagged: t.boolean().default(false) },
presets: { flagged: (q) => q.where({ flagged: true }) },
});
const notes = table(
"notes",
{ id: t.identity(), title: t.text() },
{ traits: [flagged] },
);
const app = schema({ tables: [notes] });
export async function flaggedNotes(url: string) {
await using db = connect(url, { schema: app });
return db.notes.flagged().find({ limit: 20 });
}