001_creation_knowledge_schema.sql 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. -- Creation Knowledge formal cloud schema.
  2. -- Applied to database: creation_knowledge_prod
  3. -- Schema: creation_knowledge
  4. CREATE EXTENSION IF NOT EXISTS pgcrypto;
  5. CREATE SCHEMA IF NOT EXISTS creation_knowledge;
  6. CREATE TABLE IF NOT EXISTS creation_knowledge.schema_migrations (
  7. version text PRIMARY KEY,
  8. description text NOT NULL,
  9. applied_at timestamptz NOT NULL DEFAULT now()
  10. );
  11. CREATE OR REPLACE FUNCTION creation_knowledge.touch_updated_at()
  12. RETURNS trigger
  13. LANGUAGE plpgsql
  14. AS $$
  15. BEGIN
  16. NEW.updated_at = now();
  17. RETURN NEW;
  18. END;
  19. $$;
  20. CREATE TABLE IF NOT EXISTS creation_knowledge.query_batches (
  21. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  22. name text NOT NULL,
  23. source_type text NOT NULL DEFAULT 'manual',
  24. generation_method text,
  25. target_platforms text[] NOT NULL DEFAULT ARRAY[]::text[],
  26. status text NOT NULL DEFAULT 'draft',
  27. metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  28. error_message text,
  29. created_at timestamptz NOT NULL DEFAULT now(),
  30. updated_at timestamptz NOT NULL DEFAULT now()
  31. );
  32. CREATE TABLE IF NOT EXISTS creation_knowledge.queries (
  33. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  34. batch_id uuid REFERENCES creation_knowledge.query_batches(id) ON DELETE CASCADE,
  35. query_text text NOT NULL,
  36. axes jsonb NOT NULL DEFAULT '{}'::jsonb,
  37. keep boolean,
  38. filter_reason text,
  39. status text NOT NULL DEFAULT 'draft',
  40. sort_order integer NOT NULL DEFAULT 0,
  41. metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  42. error_message text,
  43. created_at timestamptz NOT NULL DEFAULT now(),
  44. updated_at timestamptz NOT NULL DEFAULT now()
  45. );
  46. CREATE TABLE IF NOT EXISTS creation_knowledge.acquisition_runs (
  47. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  48. batch_id uuid REFERENCES creation_knowledge.query_batches(id),
  49. run_key text UNIQUE,
  50. status text NOT NULL DEFAULT 'pending',
  51. note text,
  52. metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  53. error_message text,
  54. started_at timestamptz,
  55. finished_at timestamptz,
  56. created_at timestamptz NOT NULL DEFAULT now(),
  57. updated_at timestamptz NOT NULL DEFAULT now()
  58. );
  59. CREATE TABLE IF NOT EXISTS creation_knowledge.acquisition_jobs (
  60. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  61. run_id uuid NOT NULL REFERENCES creation_knowledge.acquisition_runs(id) ON DELETE CASCADE,
  62. query_id uuid REFERENCES creation_knowledge.queries(id),
  63. platform text NOT NULL,
  64. status text NOT NULL DEFAULT 'pending',
  65. search_limit integer,
  66. display_limit integer,
  67. attempt_count integer NOT NULL DEFAULT 0,
  68. metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  69. error_message text,
  70. started_at timestamptz,
  71. finished_at timestamptz,
  72. created_at timestamptz NOT NULL DEFAULT now(),
  73. updated_at timestamptz NOT NULL DEFAULT now(),
  74. UNIQUE (run_id, query_id, platform)
  75. );
  76. CREATE TABLE IF NOT EXISTS creation_knowledge.candidate_items (
  77. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  78. job_id uuid REFERENCES creation_knowledge.acquisition_jobs(id) ON DELETE SET NULL,
  79. query_id uuid REFERENCES creation_knowledge.queries(id) ON DELETE SET NULL,
  80. platform text NOT NULL,
  81. platform_item_id text,
  82. canonical_url text,
  83. content_type text,
  84. title text,
  85. author_name text,
  86. published_at timestamptz,
  87. raw_summary text,
  88. status text NOT NULL DEFAULT 'candidate',
  89. source_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  90. metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  91. error_message text,
  92. created_at timestamptz NOT NULL DEFAULT now(),
  93. updated_at timestamptz NOT NULL DEFAULT now()
  94. );
  95. CREATE TABLE IF NOT EXISTS creation_knowledge.media_assets (
  96. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  97. item_id uuid NOT NULL REFERENCES creation_knowledge.candidate_items(id) ON DELETE CASCADE,
  98. media_type text NOT NULL,
  99. source_url text,
  100. oss_url text,
  101. cdn_url text,
  102. position integer NOT NULL DEFAULT 0,
  103. status text NOT NULL DEFAULT 'pending',
  104. source_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  105. metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  106. error_message text,
  107. created_at timestamptz NOT NULL DEFAULT now(),
  108. updated_at timestamptz NOT NULL DEFAULT now()
  109. );
  110. CREATE TABLE IF NOT EXISTS creation_knowledge.item_classifications (
  111. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  112. item_id uuid NOT NULL REFERENCES creation_knowledge.candidate_items(id) ON DELETE CASCADE,
  113. is_creation_knowledge boolean,
  114. label text,
  115. confidence numeric(5,4),
  116. reason text,
  117. model_name text,
  118. prompt_version text,
  119. result_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  120. status text NOT NULL DEFAULT 'pending',
  121. error_message text,
  122. created_at timestamptz NOT NULL DEFAULT now(),
  123. updated_at timestamptz NOT NULL DEFAULT now()
  124. );
  125. CREATE TABLE IF NOT EXISTS creation_knowledge.decode_jobs (
  126. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  127. item_id uuid NOT NULL REFERENCES creation_knowledge.candidate_items(id) ON DELETE CASCADE,
  128. status text NOT NULL DEFAULT 'pending',
  129. attempt_count integer NOT NULL DEFAULT 0,
  130. metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  131. error_message text,
  132. started_at timestamptz,
  133. finished_at timestamptz,
  134. created_at timestamptz NOT NULL DEFAULT now(),
  135. updated_at timestamptz NOT NULL DEFAULT now()
  136. );
  137. CREATE TABLE IF NOT EXISTS creation_knowledge.decode_results (
  138. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  139. decode_job_id uuid REFERENCES creation_knowledge.decode_jobs(id) ON DELETE SET NULL,
  140. item_id uuid NOT NULL REFERENCES creation_knowledge.candidate_items(id) ON DELETE CASCADE,
  141. read_result jsonb NOT NULL DEFAULT '{}'::jsonb,
  142. gate_result jsonb NOT NULL DEFAULT '{}'::jsonb,
  143. framing_result jsonb NOT NULL DEFAULT '{}'::jsonb,
  144. status text NOT NULL DEFAULT 'draft',
  145. error_message text,
  146. created_at timestamptz NOT NULL DEFAULT now(),
  147. updated_at timestamptz NOT NULL DEFAULT now()
  148. );
  149. CREATE TABLE IF NOT EXISTS creation_knowledge.knowledge_particles (
  150. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  151. decode_result_id uuid REFERENCES creation_knowledge.decode_results(id) ON DELETE CASCADE,
  152. item_id uuid REFERENCES creation_knowledge.candidate_items(id) ON DELETE CASCADE,
  153. parent_particle_id uuid REFERENCES creation_knowledge.knowledge_particles(id) ON DELETE SET NULL,
  154. particle_type text NOT NULL CHECK (particle_type IN ('what', 'how', 'why')),
  155. title text NOT NULL,
  156. business_stage text,
  157. creation_stage text,
  158. content jsonb NOT NULL DEFAULT '{}'::jsonb,
  159. sort_order integer NOT NULL DEFAULT 0,
  160. status text NOT NULL DEFAULT 'draft',
  161. metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
  162. error_message text,
  163. created_at timestamptz NOT NULL DEFAULT now(),
  164. updated_at timestamptz NOT NULL DEFAULT now()
  165. );
  166. CREATE TABLE IF NOT EXISTS creation_knowledge.scope_results (
  167. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  168. particle_id uuid REFERENCES creation_knowledge.knowledge_particles(id) ON DELETE CASCADE,
  169. item_id uuid REFERENCES creation_knowledge.candidate_items(id) ON DELETE CASCADE,
  170. scope_type text NOT NULL,
  171. scope_value text NOT NULL,
  172. is_reused boolean,
  173. matched_scope_id text,
  174. confidence numeric(5,4),
  175. evidence jsonb NOT NULL DEFAULT '{}'::jsonb,
  176. status text NOT NULL DEFAULT 'draft',
  177. error_message text,
  178. created_at timestamptz NOT NULL DEFAULT now(),
  179. updated_at timestamptz NOT NULL DEFAULT now()
  180. );
  181. CREATE TABLE IF NOT EXISTS creation_knowledge.payload_drafts (
  182. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  183. particle_id uuid REFERENCES creation_knowledge.knowledge_particles(id) ON DELETE CASCADE,
  184. item_id uuid REFERENCES creation_knowledge.candidate_items(id) ON DELETE CASCADE,
  185. payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  186. review_status text NOT NULL DEFAULT 'pending',
  187. ingest_ready boolean NOT NULL DEFAULT false,
  188. status text NOT NULL DEFAULT 'draft',
  189. error_message text,
  190. created_at timestamptz NOT NULL DEFAULT now(),
  191. updated_at timestamptz NOT NULL DEFAULT now()
  192. );
  193. CREATE TABLE IF NOT EXISTS creation_knowledge.ingest_records (
  194. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  195. payload_draft_id uuid REFERENCES creation_knowledge.payload_drafts(id) ON DELETE SET NULL,
  196. target_system text NOT NULL,
  197. target_id text,
  198. status text NOT NULL DEFAULT 'pending',
  199. attempt_count integer NOT NULL DEFAULT 0,
  200. response_payload jsonb NOT NULL DEFAULT '{}'::jsonb,
  201. error_message text,
  202. created_at timestamptz NOT NULL DEFAULT now(),
  203. updated_at timestamptz NOT NULL DEFAULT now()
  204. );
  205. CREATE TABLE IF NOT EXISTS creation_knowledge.contract_snapshots (
  206. id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  207. contract_name text NOT NULL,
  208. contract_type text NOT NULL,
  209. version_label text,
  210. content_hash text,
  211. source_path text,
  212. snapshot jsonb NOT NULL DEFAULT '{}'::jsonb,
  213. created_at timestamptz NOT NULL DEFAULT now()
  214. );
  215. CREATE INDEX IF NOT EXISTS idx_queries_batch ON creation_knowledge.queries(batch_id);
  216. CREATE INDEX IF NOT EXISTS idx_acquisition_runs_batch ON creation_knowledge.acquisition_runs(batch_id);
  217. CREATE INDEX IF NOT EXISTS idx_acquisition_jobs_run ON creation_knowledge.acquisition_jobs(run_id);
  218. CREATE INDEX IF NOT EXISTS idx_candidate_items_job ON creation_knowledge.candidate_items(job_id);
  219. CREATE INDEX IF NOT EXISTS idx_candidate_items_platform_item ON creation_knowledge.candidate_items(platform, platform_item_id);
  220. CREATE INDEX IF NOT EXISTS idx_media_assets_item ON creation_knowledge.media_assets(item_id);
  221. CREATE INDEX IF NOT EXISTS idx_item_classifications_item ON creation_knowledge.item_classifications(item_id);
  222. CREATE INDEX IF NOT EXISTS idx_decode_jobs_item ON creation_knowledge.decode_jobs(item_id);
  223. CREATE INDEX IF NOT EXISTS idx_decode_results_item ON creation_knowledge.decode_results(item_id);
  224. CREATE INDEX IF NOT EXISTS idx_knowledge_particles_item ON creation_knowledge.knowledge_particles(item_id);
  225. CREATE INDEX IF NOT EXISTS idx_scope_results_particle ON creation_knowledge.scope_results(particle_id);
  226. CREATE INDEX IF NOT EXISTS idx_payload_drafts_particle ON creation_knowledge.payload_drafts(particle_id);
  227. CREATE INDEX IF NOT EXISTS idx_ingest_records_payload ON creation_knowledge.ingest_records(payload_draft_id);
  228. CREATE INDEX IF NOT EXISTS idx_contract_snapshots_name ON creation_knowledge.contract_snapshots(contract_name, contract_type);
  229. DO $$
  230. DECLARE
  231. table_name text;
  232. BEGIN
  233. FOREACH table_name IN ARRAY ARRAY[
  234. 'query_batches',
  235. 'queries',
  236. 'acquisition_runs',
  237. 'acquisition_jobs',
  238. 'candidate_items',
  239. 'media_assets',
  240. 'item_classifications',
  241. 'decode_jobs',
  242. 'decode_results',
  243. 'knowledge_particles',
  244. 'scope_results',
  245. 'payload_drafts',
  246. 'ingest_records'
  247. ]
  248. LOOP
  249. EXECUTE format('DROP TRIGGER IF EXISTS trg_%I_touch_updated_at ON creation_knowledge.%I', table_name, table_name);
  250. EXECUTE format(
  251. 'CREATE TRIGGER trg_%I_touch_updated_at BEFORE UPDATE ON creation_knowledge.%I FOR EACH ROW EXECUTE FUNCTION creation_knowledge.touch_updated_at()',
  252. table_name,
  253. table_name
  254. );
  255. END LOOP;
  256. END;
  257. $$;
  258. DO $$
  259. BEGIN
  260. IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'ck_app') THEN
  261. GRANT CONNECT ON DATABASE creation_knowledge_prod TO ck_app;
  262. GRANT USAGE ON SCHEMA creation_knowledge TO ck_app;
  263. GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA creation_knowledge TO ck_app;
  264. GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA creation_knowledge TO ck_app;
  265. ALTER DEFAULT PRIVILEGES IN SCHEMA creation_knowledge
  266. GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ck_app;
  267. ALTER DEFAULT PRIVILEGES IN SCHEMA creation_knowledge
  268. GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO ck_app;
  269. ALTER ROLE ck_app IN DATABASE creation_knowledge_prod
  270. SET search_path = creation_knowledge, public;
  271. END IF;
  272. END;
  273. $$;
  274. INSERT INTO creation_knowledge.schema_migrations(version, description)
  275. VALUES ('001_creation_knowledge_schema', 'formal creation knowledge cloud schema')
  276. ON CONFLICT (version) DO UPDATE
  277. SET description = EXCLUDED.description,
  278. applied_at = now();