From 0908e5179170517a943c90775e7f9cf04f429779 Mon Sep 17 00:00:00 2001 From: Pacharapol Withayasakpunt Date: Fri, 15 Oct 2021 12:43:17 +0700 Subject: [PATCH] update db schema --- initdb.d/01-function.sql | 21 +++++++++++ initdb.d/20-user.sql | 10 ++--- initdb.d/22-entry.sql | 79 ++++++++++++++++++++++++++++++++++++++++ initdb.d/23-library.sql | 23 ++---------- 4 files changed, 109 insertions(+), 24 deletions(-) create mode 100644 initdb.d/22-entry.sql diff --git a/initdb.d/01-function.sql b/initdb.d/01-function.sql index e49f9ca..2f967d3 100644 --- a/initdb.d/01-function.sql +++ b/initdb.d/01-function.sql @@ -1,3 +1,24 @@ +CREATE OR REPLACE FUNCTION "f_updatedAt"() +RETURNS TRIGGER AS $$ +BEGIN + NEW."updatedAt" = now(); + RETURN NEW; +END; +$$ language 'plpgsql'; + CREATE FUNCTION array_distinct(anyarray) RETURNS anyarray AS $f$ SELECT array_agg(DISTINCT x) FROM unnest($1) t(x); $f$ LANGUAGE SQL IMMUTABLE; + +CREATE OR REPLACE FUNCTION normalize_reading (TEXT[]) RETURNS TEXT[] AS +$func$ +DECLARE + s TEXT; + new_arr TEXT[] := '{}'; +BEGIN + FOREACH s IN ARRAY $1||'{}'::text[] LOOP + new_arr := new_arr||ARRAY[split_part(s,'.',1), replace(s,'.','')]; + END LOOP; + RETURN new_arr; +END; +$func$ LANGUAGE plpgsql IMMUTABLE; diff --git a/initdb.d/20-user.sql b/initdb.d/20-user.sql index 932dbca..c12a8a5 100644 --- a/initdb.d/20-user.sql +++ b/initdb.d/20-user.sql @@ -1,11 +1,10 @@ CREATE TABLE "user" ( - "id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), + "id" UUID NOT NULL PRIMARY KEY, "createdAt" TIMESTAMPTZ DEFAULT now(), "updatedAt" TIMESTAMPTZ DEFAULT now(), "identifier" TEXT UNIQUE NOT NULL, - "wk_hash" TEXT NOT NULL, "level.min" INT DEFAULT 1, - "level.max" INT DEFAULT 3, + "level.max" INT NOT NULL, "level.vocabulary.showing" TEXT[], "quiz.settings" JSONB ); @@ -17,6 +16,7 @@ CREATE TRIGGER "t_user_updatedAt" CREATE INDEX "idx_user_updatedAt" ON "user" ("updatedAt"); CREATE INDEX "idx_user_identifier" ON "user" ("identifier"); -CREATE INDEX "idx_user_wk_hash" ON "user" ("wk_hash"); -INSERT INTO "user" ("id", "identifier") VALUES (uuid_nil(), ''); +INSERT INTO "user" ("id", "identifier") VALUES +(uuid_nil(), '', 1), +('00000000-0000-0000-0000-000000000001', 'wanikani', 1); diff --git a/initdb.d/22-entry.sql b/initdb.d/22-entry.sql new file mode 100644 index 0000000..b7b8cb8 --- /dev/null +++ b/initdb.d/22-entry.sql @@ -0,0 +1,79 @@ +CREATE TABLE "entry" ( + "id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), + "createdAt" TIMESTAMPTZ DEFAULT now(), + "updatedAt" TIMESTAMPTZ DEFAULT now(), + "userId" UUID NOT NULL REFERENCES "user"("id") ON DELETE CASCADE, + "type" TEXT NOT NULL, + "entry" TEXT[] NOT NULL CHECK ("entry"[1] IS NOT NULL), + -- "reading" -- backref "entry.reading" + -- "translation" -- backref "entry.translation" + "description" TEXT NOT NULL DEFAULT '', + "tag" TEXT[] NOT NULL DEFAULT '{}', + "level" FLOAT NOT NULL, + "level.kanji" INT NOT NULL, + "frequency" FLOAT +); + +CREATE TRIGGER "t_entry_updatedAt" + BEFORE UPDATE ON "entry" + FOR EACH ROW + EXECUTE PROCEDURE "f_updatedAt"(); + +CREATE UNIQUE INDEX idx_entry_u ON "entry" (("entry"[1]), "type", "userId"); + +CREATE INDEX "idx_entry_updatedAt" ON "entry" ("updatedAt"); +CREATE INDEX "idx_entry_userId" ON "entry" ("userId"); +CREATE INDEX "idx_entry_type" ON "entry" ("type"); +CREATE INDEX "idx_entry_level" ON "entry" ("level"); +CREATE INDEX "idx_entry_level.kanji" ON "entry" ("level.kanji"); +CREATE INDEX "idx_entry_frequency" ON "entry" ("frequency"); + +CREATE INDEX "idx_entry_description" ON "entry" + USING pgroonga("description") + WITH (plugins='token_filters/stem', token_filters='TokenFilterStem'); + +CREATE INDEX "idx_entry_entry" ON "entry" + USING pgroonga("entry") + WITH ( + tokenizer='TokenMecab', + normalizer='NormalizerNFKC100("unify_kana", true)' + ); +CREATE INDEX "idx_entry_entry_gin" ON "entry" USING GIN("entry"); +CREATE INDEX "idx_entry_tag" ON "entry" USING pgroonga ("tag"); + +CREATE TABLE "entry.reading" ( + "id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), + "createdAt" TIMESTAMPTZ DEFAULT now(), + "updatedAt" TIMESTAMPTZ DEFAULT now(), + "userId" UUID REFERENCES "user"("id") ON DELETE CASCADE, + "entryId" UUID REFERENCES "entry"("id") ON DELETE CASCADE + "type" TEXT NOT NULL, + "reading" TEXT[] NOT NULL DEFAULT '{}' CHECK ("reading"[1] IS NOT NULL) +); + +CREATE INDEX "idx_entry.reading_updatedAt" ON "entry.reading" ("updatedAt"); +CREATE INDEX "idx_entry.reading_userId" ON "entry.reading" ("userId") +CREATE INDEX "idx_entry.reading_type" ON "entry.reading" ("type"); +CREATE INDEX "idx_entry.reading_reading" ON "entry.reading" + USING pgroonga (normalize_reading("reading")) + WITH ( + tokenizer='TokenMecab', + normalizer='NormalizerNFKC100("unify_kana", true)' + ); + +CREATE TABLE "entry.translation" ( + "id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), + "createdAt" TIMESTAMPTZ DEFAULT now(), + "updatedAt" TIMESTAMPTZ DEFAULT now(), + "userId" UUID REFERENCES "user"("id") ON DELETE CASCADE, + "entryId" UUID REFERENCES "entry"("id") ON DELETE CASCADE + "language" TEXT NOT NULL, + "translation" TEXT[] NOT NULL DEFAULT '{}' CHECK ("translation"[1] IS NOT NULL) +); + +CREATE INDEX "idx_entry.translation_updatedAt" ON "entry.translation" ("updatedAt"); +CREATE INDEX "idx_entry.translation_userId" ON "entry.translation" ("userId") +CREATE INDEX "idx_entry.translation_language" ON "entry.trnalsation" ("language"); +CREATE INDEX "idx_entry.translation_translation" ON "entry.translation" + USING pgroonga("translation") + WITH (plugins='token_filters/stem', token_filters='TokenFilterStem'); diff --git a/initdb.d/23-library.sql b/initdb.d/23-library.sql index 855740f..95b2885 100644 --- a/initdb.d/23-library.sql +++ b/initdb.d/23-library.sql @@ -1,30 +1,15 @@ -CREATE OR REPLACE FUNCTION unwrap_entries (JSONB) RETURNS TEXT[] AS -$func$ -DECLARE - it JSONB; - "out" TEXT[] := '{}'::text[]; -BEGIN - FOR it IN (SELECT * FROM jsonb_array_elements($1)) - LOOP - "out" := "out"||(it ->> 'entry'); - END LOOP; - - RETURN "out"; -END; -$func$ LANGUAGE plpgsql IMMUTABLE; - CREATE TABLE "library" ( "id" UUID NOT NULL PRIMARY KEY DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMPTZ DEFAULT now(), "updatedAt" TIMESTAMPTZ DEFAULT now(), - "userId" UUID NOT NULL DEFAULT uuid_nil() REFERENCES "user"("id") ON DELETE CASCADE, + "userId" UUID NOT NULL REFERENCES "user"("id") ON DELETE CASCADE, "isShared" BOOLEAN, "type" TEXT NOT NULL, - "entries" JSONB NOT NULL CHECK ("entries" -> 0 -> 'entry' IS NOT NULL), - "entry" TEXT[] GENERATED ALWAYS AS (unwrap_entries("entries")) STORED, + "entry" TEXT[] NOT NULL CHECK ("entry"[1] IS NOT NULL), + "entry.meta" JSONB DEFAULT '{}', "title" TEXT NOT NULL, "description" TEXT NOT NULL DEFAULT '', - "tag" TEXT[] NOT NULL DEFAULT '{}'::TEXT[] + "tag" TEXT[] NOT NULL DEFAULT '{}' ); CREATE TRIGGER "t_library_updatedAt"