From b16b5db3f7e88056c161cdd0fb1d97bba6afdf61 Mon Sep 17 00:00:00 2001 From: lila Date: Sun, 5 Apr 2026 01:21:32 +0200 Subject: [PATCH] updating data models --- packages/db/src/db/schema.ts | 69 +++++++++++++++++++------------- packages/shared/src/constants.ts | 4 +- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/packages/db/src/db/schema.ts b/packages/db/src/db/schema.ts index d78e20a..cbc2e34 100644 --- a/packages/db/src/db/schema.ts +++ b/packages/db/src/db/schema.ts @@ -13,14 +13,20 @@ import { import { sql } from "drizzle-orm"; -import { SUPPORTED_POS, SUPPORTED_LANGUAGE_CODES } from "@glossa/shared"; +import { + SUPPORTED_POS, + SUPPORTED_LANGUAGE_CODES, + CEFR_LEVELS, +} from "@glossa/shared"; export const terms = pgTable( "terms", { id: uuid().primaryKey().defaultRandom(), - synset_id: text().unique().notNull(), + source: varchar({ length: 50 }), // 'omw', 'wiktionary', null for manual + source_id: text(), // synset_id value for omw, wiktionary QID, etc. pos: varchar({ length: 20 }).notNull(), + cefr_level: varchar({ length: 2 }), created_at: timestamp({ withTimezone: true }).defaultNow().notNull(), }, (table) => [ @@ -28,31 +34,12 @@ export const terms = pgTable( "pos_check", sql`${table.pos} IN (${sql.raw(SUPPORTED_POS.map((p) => `'${p}'`).join(", "))})`, ), - index("idx_terms_pos").on(table.pos), - ], -); -// sql.raw() is safe here: SUPPORTED_POS is a compile-time constant from @glossa/shared, -// not user input. If you're refactoring this, make sure the values still come from that -// constant and never from a runtime/user-controlled source. - -export const translations = pgTable( - "translations", - { - id: uuid().primaryKey().defaultRandom(), - term_id: uuid() - .notNull() - .references(() => terms.id, { onDelete: "cascade" }), - language_code: varchar({ length: 10 }).notNull(), - text: text().notNull(), - created_at: timestamp({ withTimezone: true }).defaultNow().notNull(), - }, - (table) => [ - unique("unique_translations").on( - table.term_id, - table.language_code, - table.text, + check( + "cefr_check", + sql`${table.cefr_level} IN (${sql.raw(CEFR_LEVELS.map((p) => `'${p}'`).join(", "))})`, ), - index("idx_translations_lang").on(table.language_code, table.term_id), + unique("unique_source_id").on(table.source, table.source_id), + index("idx_terms_source_pos").on(table.source, table.pos), ], ); @@ -73,7 +60,35 @@ export const term_glosses = pgTable( table.language_code, table.text, ), - index("idx_term_glosses_term").on(table.term_id), + check( + "language_code_check", + sql`${table.language_code} IN (${sql.raw(SUPPORTED_LANGUAGE_CODES.map((l) => `'${l}'`).join(", "))})`, + ), + ], +); + +export const translations = pgTable( + "translations", + { + id: uuid().primaryKey().defaultRandom(), + term_id: uuid() + .notNull() + .references(() => terms.id, { onDelete: "cascade" }), + language_code: varchar({ length: 10 }).notNull(), + text: text().notNull(), + created_at: timestamp({ withTimezone: true }).defaultNow().notNull(), + }, + (table) => [ + unique("unique_translations").on( + table.term_id, + table.language_code, + table.text, + ), + check( + "language_code_check", + sql`${table.language_code} IN (${sql.raw(SUPPORTED_LANGUAGE_CODES.map((l) => `'${l}'`).join(", "))})`, + ), + index("idx_translations_lang").on(table.language_code, table.term_id), ], ); diff --git a/packages/shared/src/constants.ts b/packages/shared/src/constants.ts index 26b5467..416aff4 100644 --- a/packages/shared/src/constants.ts +++ b/packages/shared/src/constants.ts @@ -1,5 +1,7 @@ export const SUPPORTED_LANGUAGE_CODES = ["en", "it"] as const; -export const SUPPORTED_POS = ["noun"] as const; +export const SUPPORTED_POS = ["noun", "verb"] as const; export const GAME_ROUNDS = ["3", "10"] as const; + +export const CEFR_LEVELS = ["A1", "A2", "B1", "B2", "C1", "C2"] as const;