adding new schema for words, senses and translations incl. relations

This commit is contained in:
lila 2026-07-23 16:49:16 +02:00
parent 0770ff9ec6
commit de3ebf66a9

View file

@ -21,6 +21,7 @@ import {
CEFR_LEVELS,
DIFFICULTY_LEVELS,
LOBBY_STATUSES,
NOUN_GENDERS,
} from "@lila/shared";
// ── Vocabulary ────────────────────────────────────────────────────────────────
@ -112,6 +113,95 @@ export const entry_translations = pgTable(
],
);
export const words = pgTable(
"words",
{
id: uuid().primaryKey().defaultRandom(),
headword: text().notNull(),
language_code: varchar({ length: 10 }).notNull(),
pos: varchar({ length: 20 }).notNull(),
created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
},
(table) => [
unique("unique_word_per_language_and_pos").on(
table.headword,
table.language_code,
table.pos,
),
check(
"words_language_code_check",
sql`${table.language_code} IN (${sql.raw(SUPPORTED_LANGUAGE_CODES.map((code) => `'${code}'`).join(", "))})`,
),
check(
"words_pos_check",
sql`${table.pos} IN (${sql.raw(SUPPORTED_POS.map((pos) => `'${pos}'`).join(", "))})`,
),
index("idx_language_code_pos").on(table.language_code, table.pos),
],
);
export const senses = pgTable(
"senses",
{
id: uuid().primaryKey().defaultRandom(),
word_id: uuid()
.notNull()
.references(() => words.id, { onDelete: "cascade" }),
sense_index: smallint().default(0).notNull(),
difficulty: varchar({ length: 20 }).notNull(),
definitions: text().array().notNull().default([]),
examples: text().array().notNull().default([]),
created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
},
(table) => [
unique("unique_sense_per_word").on(table.word_id, table.sense_index),
check(
"senses_difficulty_check",
sql`${table.difficulty} IN (${sql.raw(DIFFICULTY_LEVELS.map((lvl) => `'${lvl}'`).join(", "))})`,
),
index("idx_word_sense_difficulty").on(table.word_id, table.difficulty),
],
);
export const translations = pgTable(
"translations",
{
id: uuid().primaryKey().defaultRandom(),
sense_id: uuid()
.notNull()
.references(() => senses.id, { onDelete: "cascade" }),
target_language_code: varchar({ length: 10 }).notNull(),
translation: text().notNull(),
gender: varchar({ length: 20 }),
difficulty: varchar({ length: 20 }).notNull(),
created_at: timestamp({ withTimezone: true }).defaultNow().notNull(),
},
(table) => [
unique("unique_translation_per_sense").on(
table.sense_id,
table.target_language_code,
table.translation,
),
check(
"translations_language_code_check",
sql`${table.target_language_code} IN (${sql.raw(SUPPORTED_LANGUAGE_CODES.map((code) => `'${code}'`).join(", "))})`,
),
check(
"translations_difficulty_check",
sql`${table.difficulty} IN (${sql.raw(DIFFICULTY_LEVELS.map((lvl) => `'${lvl}'`).join(", "))})`,
),
check(
"translations_gender_check",
sql`${table.gender} IS NULL OR ${table.gender} IN (${sql.raw(NOUN_GENDERS.map((gender) => `'${gender}'`).join(", "))})`,
),
index("idx_translations_sense_language_difficulty").on(
table.sense_id,
table.target_language_code,
table.difficulty,
),
],
);
// ── Auth (managed by Better Auth) ─────────────────────────────────────────────
export const user = pgTable("user", {
@ -243,6 +333,22 @@ export const entryTranslationRelations = relations(
}),
);
export const wordRelations = relations(words, ({ many }) => ({
senses: many(senses),
}));
export const senseRelations = relations(senses, ({ one, many }) => ({
word: one(words, { fields: [senses.word_id], references: [words.id] }),
translations: many(translations),
}));
export const translationRelations = relations(translations, ({ one }) => ({
sense: one(senses, {
fields: [translations.sense_id],
references: [senses.id],
}),
}));
export const userRelations = relations(user, ({ many }) => ({
sessions: many(session),
accounts: many(account),