How relationships (hasMany, hasOne, references) work
masterRelationships are defined in a second .map() call chained after table definitions. They determine how data is fetched and how ownership/deletion behaves.
Relationship Types
hasMany(targetTable).by('foreignKeyColumn'): One-to-many. The parent owns the children. Deleting the parent triggers a cascade delete of the children. Returns an array.hasOne(targetTable).by('foreignKeyColumn'): One-to-one. A special case ofhasMany. The parent owns the child (cascade delete). Returns a single object ornull.references(targetTable).by('foreignKeyColumn'): Many-to-one. The current table holds the foreign key. The target is independent (no cascade delete). Returns a single object ornull.
Ownership Rules
- Owned (
hasMany/hasOne): Deleting the parent deletes the children. Updating the parent can manage child lifecycle. - Independent (
references): Deleting the referencing row does NOT delete the referenced row. You can set the reference tonullto detach it.
// Example: Author and Book (one-to-many)
import orange from 'orange-orm';
const map = orange.map(x => ({
author: x.table('author').map(({ column }) => ({
id: column('id').numeric().primary().notNullExceptInsert(),
name: column('name').string().notNull(),
})),
book: x.table('book').map(({ column }) => ({
id: column('id').numeric().primary().notNullExceptInsert(),
authorId: column('authorId').numeric().notNull(),
title: column('title').string().notNull(),
year: column('year').numeric(),
}))
})).map(x => ({
author: x.author.map(({ hasMany }) => ({
books: hasMany(x.book).by('authorId')
})),
book: x.book.map(({ references }) => ({
author: references(x.author).by('authorId')
}))
}));