Mini Object-relational Mapper IILoading saved progress…

Mini Object-relational Mapper II

Build the query layer of a small in-memory ORM — the kind of thing libraries like Prisma put between your code and your data. createORM takes a few named models, each seeded with plain objects, and hands back a delegate per model. A delegate can create rows (each gets an auto-incrementing id) and, the part that matters here, findMany({ where, orderBy }) to query them. The focus of this question is rich filtering — operators like gt, in, and contains, not just equality — and multi-field sorting where one key breaks ties left by another.

Signature

// createORM(schema): one delegate per model name in `schema`.
//   schema: { [model: string]: object[] }   — seed rows per model (ids assigned)
function createORM(schema): Record<string, ModelDelegate>;

interface ModelDelegate {
  create(data: object): object;           // append a row; returns it WITH its new id
  findById(id: number): object | null;    // the row with that id, or null
  findMany(args?: {
    where?:   Record<string, Condition>;  // AND-combined field conditions
    orderBy?: OrderBy | OrderBy[];         // single key, or keys tried in order
  }): object[];
}

// A Condition is either a bare value (means `equals`) or an operator object.
type Condition = unknown | {
  equals?: unknown; not?: unknown;
  gt?: number; gte?: number; lt?: number; lte?: number;
  in?: unknown[]; contains?: string;       // `contains` is substring-on-strings
};

type OrderBy = { [field: string]: 'asc' | 'desc' };

Examples

const db = createORM({
  user: [
    { name: 'Ada', last: 'Byron', age: 36 },
    { name: 'Grace', last: 'Hopper', age: 85 },
    { name: 'Linus', last: 'Byron', age: 21 },
  ],
});

// Operator filter: keep rows where age is strictly greater than 30.
db.user.findMany({ where: { age: { gt: 30 } } });
// → [ { id: 1, name: 'Ada', ... }, { id: 2, name: 'Grace', ... } ]
// Multi-field sort: by last name ascending, ties broken by age descending.
db.user.findMany({ orderBy: [{ last: 'asc' }, { age: 'desc' }] });
// → Byron/Ada(36), Byron/Linus(21), Hopper/Grace(85)
//   The two Byrons tie on `last`, so the age-desc key decides their order.
// where + orderBy combine: filter first, then sort the survivors.
db.user.findMany({ where: { last: { in: ['Byron'] } }, orderBy: { age: 'asc' } });
// → Byron/Linus(21), Byron/Ada(36)

Notes

  • A bare value means equality. { name: 'Ada' } is shorthand for { name: { equals: 'Ada' } }. An operator object like { age: { gt: 18 } } applies its comparisons instead.
  • Operators to support: equals, not, gt, gte, lt, lte, in (membership in an array), and contains (substring match on a string field). gte/lte include the boundary; gt/lt exclude it.
  • Everything is AND. Multiple fields in where must all match; multiple operators on one field ({ age: { gte: 21, lt: 41 } }) must all hold. There is no OR in this question.
  • orderBy can be one key or many. A single { field: 'asc' } sorts by that field. An array tries each key in order — a later key only matters when every earlier key ties.
  • Don't mutate the stored rows. findMany returns a fresh array; sorting a result must not reorder the model's underlying data. Insertion order is the baseline.
  • Out of scope: relation includes and field selection — that is the sibling mini-orm-iii. Here you only filter and sort one model's own rows.
Loading editor…