diff --git a/src/db/migrations/0000_rainy_metal_master.sql b/src/db/migrations/0000_rainy_metal_master.sql new file mode 100644 index 0000000..a71f5ad --- /dev/null +++ b/src/db/migrations/0000_rainy_metal_master.sql @@ -0,0 +1,100 @@ +CREATE TABLE "activities" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "person_id" uuid, + "definition_id" uuid, + "activity_date" timestamp with time zone DEFAULT now(), + "notes" text, + "metadata" jsonb DEFAULT '{}'::jsonb, + "updated_at" timestamp with time zone DEFAULT now(), + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "activity_definitions" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text NOT NULL, + "group" text, + "updated_at" timestamp with time zone DEFAULT now(), + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "activity_reports" ( + "activity_id" uuid, + "report_id" uuid, + CONSTRAINT "activity_reports_activity_id_report_id_pk" PRIMARY KEY("activity_id","report_id") +); +--> statement-breakpoint +CREATE TABLE "data_keys" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text NOT NULL, + "group" text, + "updated_at" timestamp with time zone DEFAULT now(), + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "person" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text NOT NULL, + "primary_email" text, + "primary_phone" text, + "bio" text, + "data" jsonb DEFAULT '{}'::jsonb, + "bio_embedding" vector(1536), + "updated_at" timestamp with time zone DEFAULT now(), + "created_at" timestamp with time zone DEFAULT now(), + CONSTRAINT "person_primary_email_unique" UNIQUE("primary_email") +); +--> statement-breakpoint +CREATE TABLE "report_embeddings" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "report_id" uuid, + "chunk_index" integer NOT NULL, + "content_chunk" text NOT NULL, + "embedding" vector(1536), + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE "report_links" ( + "parent_report_id" uuid, + "child_report_id" uuid, + CONSTRAINT "report_links_parent_report_id_child_report_id_pk" PRIMARY KEY("parent_report_id","child_report_id"), + CONSTRAINT "no_self_link" CHECK ("report_links"."parent_report_id" != "report_links"."child_report_id") +); +--> statement-breakpoint +CREATE TABLE "report_people" ( + "report_id" uuid, + "person_id" uuid, + CONSTRAINT "report_people_report_id_person_id_pk" PRIMARY KEY("report_id","person_id") +); +--> statement-breakpoint +CREATE TABLE "reports" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "name" text NOT NULL, + "subtitle" text, + "group" text, + "content" text, + "tags" text[], + "updated_at" timestamp with time zone DEFAULT now(), + "created_at" timestamp with time zone DEFAULT now() +); +--> statement-breakpoint +ALTER TABLE "activities" ADD CONSTRAINT "activities_person_id_person_id_fk" FOREIGN KEY ("person_id") REFERENCES "public"."person"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "activities" ADD CONSTRAINT "activities_definition_id_activity_definitions_id_fk" FOREIGN KEY ("definition_id") REFERENCES "public"."activity_definitions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "activity_reports" ADD CONSTRAINT "activity_reports_activity_id_activities_id_fk" FOREIGN KEY ("activity_id") REFERENCES "public"."activities"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "activity_reports" ADD CONSTRAINT "activity_reports_report_id_reports_id_fk" FOREIGN KEY ("report_id") REFERENCES "public"."reports"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "report_embeddings" ADD CONSTRAINT "report_embeddings_report_id_reports_id_fk" FOREIGN KEY ("report_id") REFERENCES "public"."reports"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "report_links" ADD CONSTRAINT "report_links_parent_report_id_reports_id_fk" FOREIGN KEY ("parent_report_id") REFERENCES "public"."reports"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "report_links" ADD CONSTRAINT "report_links_child_report_id_reports_id_fk" FOREIGN KEY ("child_report_id") REFERENCES "public"."reports"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "report_people" ADD CONSTRAINT "report_people_report_id_reports_id_fk" FOREIGN KEY ("report_id") REFERENCES "public"."reports"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "report_people" ADD CONSTRAINT "report_people_person_id_person_id_fk" FOREIGN KEY ("person_id") REFERENCES "public"."person"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "idx_activities_person_id" ON "activities" USING btree ("person_id");--> statement-breakpoint +CREATE INDEX "idx_activities_date" ON "activities" USING btree ("activity_date");--> statement-breakpoint +CREATE INDEX "idx_activity_reports_report" ON "activity_reports" USING btree ("report_id");--> statement-breakpoint +CREATE INDEX "person_bio_embedding_index" ON "person" USING hnsw ("bio_embedding" vector_cosine_ops);--> statement-breakpoint +CREATE INDEX "idx_person_name" ON "person" USING btree ("name");--> statement-breakpoint +CREATE INDEX "idx_person_data" ON "person" USING gin ("data");--> statement-breakpoint +CREATE INDEX "report_embeddings_embedding_index" ON "report_embeddings" USING hnsw ("embedding" vector_cosine_ops);--> statement-breakpoint +CREATE INDEX "idx_report_embeddings_report_id" ON "report_embeddings" USING btree ("report_id");--> statement-breakpoint +CREATE INDEX "idx_report_links_child" ON "report_links" USING btree ("child_report_id");--> statement-breakpoint +CREATE INDEX "idx_report_people_person" ON "report_people" USING btree ("person_id");--> statement-breakpoint +CREATE INDEX "idx_reports_group" ON "reports" USING btree ("group");--> statement-breakpoint +CREATE INDEX "idx_reports_tags" ON "reports" USING gin ("tags"); \ No newline at end of file diff --git a/src/db/migrations/meta/0000_snapshot.json b/src/db/migrations/meta/0000_snapshot.json new file mode 100644 index 0000000..ed35c5e --- /dev/null +++ b/src/db/migrations/meta/0000_snapshot.json @@ -0,0 +1,786 @@ +{ + "id": "2dcced0c-b85a-4f8a-97e1-cd6ca45be599", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.activities": { + "name": "activities", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "person_id": { + "name": "person_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "definition_id": { + "name": "definition_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "activity_date": { + "name": "activity_date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_activities_person_id": { + "name": "idx_activities_person_id", + "columns": [ + { + "expression": "person_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_activities_date": { + "name": "idx_activities_date", + "columns": [ + { + "expression": "activity_date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "activities_person_id_person_id_fk": { + "name": "activities_person_id_person_id_fk", + "tableFrom": "activities", + "tableTo": "person", + "columnsFrom": [ + "person_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "activities_definition_id_activity_definitions_id_fk": { + "name": "activities_definition_id_activity_definitions_id_fk", + "tableFrom": "activities", + "tableTo": "activity_definitions", + "columnsFrom": [ + "definition_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.activity_definitions": { + "name": "activity_definitions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group": { + "name": "group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.activity_reports": { + "name": "activity_reports", + "schema": "", + "columns": { + "activity_id": { + "name": "activity_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "report_id": { + "name": "report_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_activity_reports_report": { + "name": "idx_activity_reports_report", + "columns": [ + { + "expression": "report_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "activity_reports_activity_id_activities_id_fk": { + "name": "activity_reports_activity_id_activities_id_fk", + "tableFrom": "activity_reports", + "tableTo": "activities", + "columnsFrom": [ + "activity_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "activity_reports_report_id_reports_id_fk": { + "name": "activity_reports_report_id_reports_id_fk", + "tableFrom": "activity_reports", + "tableTo": "reports", + "columnsFrom": [ + "report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "activity_reports_activity_id_report_id_pk": { + "name": "activity_reports_activity_id_report_id_pk", + "columns": [ + "activity_id", + "report_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.data_keys": { + "name": "data_keys", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "group": { + "name": "group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.person": { + "name": "person", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "primary_email": { + "name": "primary_email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "primary_phone": { + "name": "primary_phone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bio": { + "name": "bio", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'::jsonb" + }, + "bio_embedding": { + "name": "bio_embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "person_bio_embedding_index": { + "name": "person_bio_embedding_index", + "columns": [ + { + "expression": "bio_embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + }, + "idx_person_name": { + "name": "idx_person_name", + "columns": [ + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_person_data": { + "name": "idx_person_data", + "columns": [ + { + "expression": "data", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "person_primary_email_unique": { + "name": "person_primary_email_unique", + "nullsNotDistinct": false, + "columns": [ + "primary_email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.report_embeddings": { + "name": "report_embeddings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "report_id": { + "name": "report_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "chunk_index": { + "name": "chunk_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "content_chunk": { + "name": "content_chunk", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "report_embeddings_embedding_index": { + "name": "report_embeddings_embedding_index", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + }, + "idx_report_embeddings_report_id": { + "name": "idx_report_embeddings_report_id", + "columns": [ + { + "expression": "report_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_embeddings_report_id_reports_id_fk": { + "name": "report_embeddings_report_id_reports_id_fk", + "tableFrom": "report_embeddings", + "tableTo": "reports", + "columnsFrom": [ + "report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.report_links": { + "name": "report_links", + "schema": "", + "columns": { + "parent_report_id": { + "name": "parent_report_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "child_report_id": { + "name": "child_report_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_report_links_child": { + "name": "idx_report_links_child", + "columns": [ + { + "expression": "child_report_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_links_parent_report_id_reports_id_fk": { + "name": "report_links_parent_report_id_reports_id_fk", + "tableFrom": "report_links", + "tableTo": "reports", + "columnsFrom": [ + "parent_report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "report_links_child_report_id_reports_id_fk": { + "name": "report_links_child_report_id_reports_id_fk", + "tableFrom": "report_links", + "tableTo": "reports", + "columnsFrom": [ + "child_report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "report_links_parent_report_id_child_report_id_pk": { + "name": "report_links_parent_report_id_child_report_id_pk", + "columns": [ + "parent_report_id", + "child_report_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": { + "no_self_link": { + "name": "no_self_link", + "value": "\"report_links\".\"parent_report_id\" != \"report_links\".\"child_report_id\"" + } + }, + "isRLSEnabled": false + }, + "public.report_people": { + "name": "report_people", + "schema": "", + "columns": { + "report_id": { + "name": "report_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "person_id": { + "name": "person_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "idx_report_people_person": { + "name": "idx_report_people_person", + "columns": [ + { + "expression": "person_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "report_people_report_id_reports_id_fk": { + "name": "report_people_report_id_reports_id_fk", + "tableFrom": "report_people", + "tableTo": "reports", + "columnsFrom": [ + "report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "report_people_person_id_person_id_fk": { + "name": "report_people_person_id_person_id_fk", + "tableFrom": "report_people", + "tableTo": "person", + "columnsFrom": [ + "person_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "report_people_report_id_person_id_pk": { + "name": "report_people_report_id_person_id_pk", + "columns": [ + "report_id", + "person_id" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reports": { + "name": "reports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group": { + "name": "group", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": { + "idx_reports_group": { + "name": "idx_reports_group", + "columns": [ + { + "expression": "group", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "idx_reports_tags": { + "name": "idx_reports_tags", + "columns": [ + { + "expression": "tags", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "gin", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/src/db/migrations/meta/_journal.json b/src/db/migrations/meta/_journal.json new file mode 100644 index 0000000..cd302a2 --- /dev/null +++ b/src/db/migrations/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1773439398675, + "tag": "0000_rainy_metal_master", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/src/db/schema.ts b/src/db/schema.ts new file mode 100644 index 0000000..7605b31 --- /dev/null +++ b/src/db/schema.ts @@ -0,0 +1,161 @@ +import { sql } from "drizzle-orm"; +import { + check, + index, + integer, + jsonb, + pgTable, + primaryKey, + text, + timestamp, + uuid, + vector, +} from "drizzle-orm/pg-core"; + +export const dataKeys = pgTable("data_keys", { + id: uuid("id").defaultRandom().primaryKey(), + name: text("name").notNull(), + group: text("group"), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), +}); + +export const activityDefinitions = pgTable("activity_definitions", { + id: uuid("id").defaultRandom().primaryKey(), + name: text("name").notNull(), + group: text("group"), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), +}); + +export const person = pgTable( + "person", + { + id: uuid("id").defaultRandom().primaryKey(), + name: text("name").notNull(), + primaryEmail: text("primary_email").unique(), + primaryPhone: text("primary_phone"), + bio: text("bio"), + data: jsonb("data").default(sql`'{}'::jsonb`), + bioEmbedding: vector("bio_embedding", { dimensions: 1536 }), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + }, + (table) => [ + index().using("hnsw", table.bioEmbedding.op("vector_cosine_ops")), + index("idx_person_name").on(table.name), + index("idx_person_data").using("gin", table.data), + ], +); + +export const reports = pgTable( + "reports", + { + id: uuid("id").defaultRandom().primaryKey(), + name: text("name").notNull(), + subtitle: text("subtitle"), + group: text("group"), + content: text("content"), + tags: text("tags").array(), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + }, + (table) => [ + index("idx_reports_group").on(table.group), + index("idx_reports_tags").using("gin", table.tags), + ], +); + +export const activities = pgTable( + "activities", + { + id: uuid("id").defaultRandom().primaryKey(), + personId: uuid("person_id").references(() => person.id, { + onDelete: "cascade", + }), + definitionId: uuid("definition_id").references( + () => activityDefinitions.id, + ), + activityDate: timestamp("activity_date", { + withTimezone: true, + }).defaultNow(), + notes: text("notes"), + metadata: jsonb("metadata").default(sql`'{}'::jsonb`), + updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + }, + (table) => [ + index("idx_activities_person_id").on(table.personId), + index("idx_activities_date").on(table.activityDate), + ], +); + +export const reportEmbeddings = pgTable( + "report_embeddings", + { + id: uuid("id").defaultRandom().primaryKey(), + reportId: uuid("report_id").references(() => reports.id, { + onDelete: "cascade", + }), + chunkIndex: integer("chunk_index").notNull(), + contentChunk: text("content_chunk").notNull(), + embedding: vector("embedding", { dimensions: 1536 }), + createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(), + }, + (table) => [ + index().using("hnsw", table.embedding.op("vector_cosine_ops")), + index("idx_report_embeddings_report_id").on(table.reportId), + ], +); + +export const reportPeople = pgTable( + "report_people", + { + reportId: uuid("report_id").references(() => reports.id, { + onDelete: "cascade", + }), + personId: uuid("person_id").references(() => person.id, { + onDelete: "cascade", + }), + }, + (table) => [ + primaryKey({ columns: [table.reportId, table.personId] }), + index("idx_report_people_person").on(table.personId), + ], +); + +export const reportLinks = pgTable( + "report_links", + { + parentReportId: uuid("parent_report_id").references(() => reports.id, { + onDelete: "cascade", + }), + childReportId: uuid("child_report_id").references(() => reports.id, { + onDelete: "cascade", + }), + }, + (table) => [ + primaryKey({ columns: [table.parentReportId, table.childReportId] }), + check( + "no_self_link", + sql`${table.parentReportId} != ${table.childReportId}`, + ), + index("idx_report_links_child").on(table.childReportId), + ], +); + +export const activityReports = pgTable( + "activity_reports", + { + activityId: uuid("activity_id").references(() => activities.id, { + onDelete: "cascade", + }), + reportId: uuid("report_id").references(() => reports.id, { + onDelete: "cascade", + }), + }, + (table) => [ + primaryKey({ columns: [table.activityId, table.reportId] }), + index("idx_activity_reports_report").on(table.reportId), + ], +);