feat(db): add vocabulary, term, and authority tables
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
-- Controlled vocabularies (term sources) and their terms.
|
||||||
|
CREATE TABLE vocabulary (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
key TEXT NOT NULL UNIQUE -- e.g. 'material', 'object_name'
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE term (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
vocabulary_id UUID NOT NULL REFERENCES vocabulary (id) ON DELETE CASCADE,
|
||||||
|
external_uri TEXT -- e.g. Getty AAT / KulturNav / Wikidata URI
|
||||||
|
);
|
||||||
|
CREATE INDEX term_vocabulary_idx ON term (vocabulary_id);
|
||||||
|
|
||||||
|
CREATE TABLE term_label (
|
||||||
|
term_id UUID NOT NULL REFERENCES term (id) ON DELETE CASCADE,
|
||||||
|
lang TEXT NOT NULL, -- BCP-47, e.g. 'sv', 'en'
|
||||||
|
label TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (term_id, lang)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Authority records: person / organisation / place. Store once, link many.
|
||||||
|
CREATE TABLE authority (
|
||||||
|
id UUID PRIMARY KEY,
|
||||||
|
kind TEXT NOT NULL CHECK (kind IN ('person', 'organisation', 'place')),
|
||||||
|
external_uri TEXT
|
||||||
|
);
|
||||||
|
CREATE INDEX authority_kind_idx ON authority (kind);
|
||||||
|
|
||||||
|
CREATE TABLE authority_label (
|
||||||
|
authority_id UUID NOT NULL REFERENCES authority (id) ON DELETE CASCADE,
|
||||||
|
lang TEXT NOT NULL,
|
||||||
|
label TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (authority_id, lang)
|
||||||
|
);
|
||||||
@@ -18,3 +18,26 @@ async fn migrate_is_idempotent_and_creates_audit_log(pool: PgPool) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(regclass.as_deref(), Some("audit_log"));
|
assert_eq!(regclass.as_deref(), Some("audit_log"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[sqlx::test]
|
||||||
|
async fn migrate_creates_vocabulary_and_authority_tables(pool: PgPool) {
|
||||||
|
let db = Db::from_pool(pool);
|
||||||
|
for table in [
|
||||||
|
"vocabulary",
|
||||||
|
"term",
|
||||||
|
"term_label",
|
||||||
|
"authority",
|
||||||
|
"authority_label",
|
||||||
|
] {
|
||||||
|
let regclass: Option<String> =
|
||||||
|
sqlx::query_scalar(&format!("SELECT to_regclass('public.{table}')::text"))
|
||||||
|
.fetch_one(db.pool())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
regclass.as_deref(),
|
||||||
|
Some(table),
|
||||||
|
"table {table} should exist"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user