diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..70a48350 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,5 @@ +# Bolt's Journal + +## 2025-05-14 - [Database Indexing for Performance] +**Learning:** Found that the core tables (`chats`, `messages`, `calendar_notes`) were missing indexes on frequently queried and sorted columns (foreign keys and `created_at`). In Drizzle ORM, foreign key constraints do not automatically create indexes in PostgreSQL. +**Action:** Always check for missing indexes on foreign keys and columns used in `WHERE` and `ORDER BY` clauses to ensure query performance scales with data growth. diff --git a/drizzle/migrations/0001_add_calendar_notes.sql b/drizzle/migrations/0001_add_calendar_notes.sql deleted file mode 100644 index 4efbac84..00000000 --- a/drizzle/migrations/0001_add_calendar_notes.sql +++ /dev/null @@ -1,42 +0,0 @@ -CREATE TABLE IF NOT EXISTS "calendar_notes" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "user_id" uuid NOT NULL, - "chat_id" uuid, - "date" timestamp with time zone NOT NULL, - "content" text NOT NULL, - "location_tags" jsonb, - "user_tags" text[], - "map_feature_id" text, - "created_at" timestamp with time zone DEFAULT now() NOT NULL, - "updated_at" timestamp with time zone DEFAULT now() NOT NULL -); ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "chats"("id") ON DELETE cascade ON UPDATE no action; -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -ALTER TABLE "calendar_notes" ENABLE ROW LEVEL SECURITY; ---> statement-breakpoint -CREATE POLICY "user_select_own_notes" ON "calendar_notes" - FOR SELECT - USING (auth.uid() = user_id); ---> statement-breakpoint -CREATE POLICY "user_insert_own_notes" ON "calendar_notes" - FOR INSERT - WITH CHECK (auth.uid() = user_id); ---> statement-breakpoint -CREATE POLICY "user_update_own_notes" ON "calendar_notes" - FOR UPDATE - USING (auth.uid() = user_id); ---> statement-breakpoint -CREATE POLICY "user_delete_own_notes" ON "calendar_notes" - FOR DELETE - USING (auth.uid() = user_id); diff --git a/drizzle/migrations/0001_sync_and_add_indexes.sql b/drizzle/migrations/0001_sync_and_add_indexes.sql new file mode 100644 index 00000000..d7aa09fc --- /dev/null +++ b/drizzle/migrations/0001_sync_and_add_indexes.sql @@ -0,0 +1,80 @@ +CREATE TABLE "calendar_notes" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "chat_id" uuid, + "date" timestamp with time zone NOT NULL, + "content" text NOT NULL, + "location_tags" jsonb, + "user_tags" text[], + "map_feature_id" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "chat_participants" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "chat_id" uuid NOT NULL, + "user_id" uuid NOT NULL, + "role" text DEFAULT 'collaborator' NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT "chat_participants_chat_user_unique" UNIQUE("chat_id","user_id") +); +--> statement-breakpoint +CREATE TABLE "locations" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "chat_id" uuid, + "geojson" jsonb NOT NULL, + "geometry" geometry(GEOMETRY, 4326), + "name" text, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "system_prompts" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "prompt" text NOT NULL, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "visualizations" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" uuid NOT NULL, + "chat_id" uuid, + "type" text DEFAULT 'map_layer' NOT NULL, + "data" jsonb NOT NULL, + "geometry" geometry(GEOMETRY, 4326), + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "chats" ALTER COLUMN "title" SET DATA TYPE text;--> statement-breakpoint +ALTER TABLE "chats" ALTER COLUMN "title" SET DEFAULT 'Untitled Chat';--> statement-breakpoint +ALTER TABLE "chats" ALTER COLUMN "visibility" SET DATA TYPE text;--> statement-breakpoint +ALTER TABLE "chats" ALTER COLUMN "visibility" SET DEFAULT 'private';--> statement-breakpoint +ALTER TABLE "messages" ALTER COLUMN "role" SET DATA TYPE text;--> statement-breakpoint +ALTER TABLE "chats" ADD COLUMN "path" text;--> statement-breakpoint +ALTER TABLE "chats" ADD COLUMN "share_path" text;--> statement-breakpoint +ALTER TABLE "chats" ADD COLUMN "shareable_link_id" uuid DEFAULT gen_random_uuid();--> statement-breakpoint +ALTER TABLE "chats" ADD COLUMN "updated_at" timestamp with time zone DEFAULT now() NOT NULL;--> statement-breakpoint +ALTER TABLE "messages" ADD COLUMN "embedding" vector(1536);--> statement-breakpoint +ALTER TABLE "messages" ADD COLUMN "location_id" uuid;--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "email" text;--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "role" text DEFAULT 'viewer';--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "selected_model" text;--> statement-breakpoint +ALTER TABLE "users" ADD COLUMN "system_prompt" text;--> statement-breakpoint +ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "chat_participants" ADD CONSTRAINT "chat_participants_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "chat_participants" ADD CONSTRAINT "chat_participants_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "locations" ADD CONSTRAINT "locations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "locations" ADD CONSTRAINT "locations_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "system_prompts" ADD CONSTRAINT "system_prompts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "visualizations" ADD CONSTRAINT "visualizations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "visualizations" ADD CONSTRAINT "visualizations_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "calendar_notes_user_id_date_idx" ON "calendar_notes" USING btree ("user_id","date");--> statement-breakpoint +ALTER TABLE "messages" ADD CONSTRAINT "messages_location_id_locations_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."locations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "chats_user_id_created_at_idx" ON "chats" USING btree ("user_id","created_at");--> statement-breakpoint +CREATE INDEX "messages_chat_id_created_at_idx" ON "messages" USING btree ("chat_id","created_at");--> statement-breakpoint +ALTER TABLE "chats" ADD CONSTRAINT "chats_shareable_link_id_unique" UNIQUE("shareable_link_id");--> statement-breakpoint +ALTER TABLE "users" ADD CONSTRAINT "users_email_unique" UNIQUE("email"); \ No newline at end of file diff --git a/drizzle/migrations/meta/0000_snapshot.json b/drizzle/migrations/meta/0000_snapshot.json index eb62145d..8f31b8c5 100644 --- a/drizzle/migrations/meta/0000_snapshot.json +++ b/drizzle/migrations/meta/0000_snapshot.json @@ -1,10 +1,8 @@ { - "id": "0d46923a-5423-4b73-91cb-5f46741e7ff9", - "prevId": "00000000-0000-0000-0000-000000000000", - "version": "5", - "dialect": "pg", + "version": "7", + "dialect": "postgresql", "tables": { - "chats": { + "public.chats": { "name": "chats", "schema": "", "columns": { @@ -48,21 +46,24 @@ "chats_user_id_users_id_fk": { "name": "chats_user_id_users_id_fk", "tableFrom": "chats", - "tableTo": "users", "columnsFrom": [ "user_id" ], + "tableTo": "users", "columnsTo": [ "id" ], - "onDelete": "cascade", - "onUpdate": "no action" + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": {}, + "policies": {}, + "isRLSEnabled": false, + "checkConstraints": {} }, - "messages": { + "public.messages": { "name": "messages", "schema": "", "columns": { @@ -110,34 +111,37 @@ "messages_chat_id_chats_id_fk": { "name": "messages_chat_id_chats_id_fk", "tableFrom": "messages", - "tableTo": "chats", "columnsFrom": [ "chat_id" ], + "tableTo": "chats", "columnsTo": [ "id" ], - "onDelete": "cascade", - "onUpdate": "no action" + "onUpdate": "no action", + "onDelete": "cascade" }, "messages_user_id_users_id_fk": { "name": "messages_user_id_users_id_fk", "tableFrom": "messages", - "tableTo": "users", "columnsFrom": [ "user_id" ], + "tableTo": "users", "columnsTo": [ "id" ], - "onDelete": "cascade", - "onUpdate": "no action" + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": {}, + "policies": {}, + "isRLSEnabled": false, + "checkConstraints": {} }, - "users": { + "public.users": { "name": "users", "schema": "", "columns": { @@ -152,14 +156,23 @@ "indexes": {}, "foreignKeys": {}, "compositePrimaryKeys": {}, - "uniqueConstraints": {} + "uniqueConstraints": {}, + "policies": {}, + "isRLSEnabled": false, + "checkConstraints": {} } }, "enums": {}, "schemas": {}, "_meta": { - "columns": {}, "schemas": {}, - "tables": {} - } + "tables": {}, + "columns": {} + }, + "id": "0d46923a-5423-4b73-91cb-5f46741e7ff9", + "prevId": "00000000-0000-0000-0000-000000000000", + "sequences": {}, + "policies": {}, + "views": {}, + "roles": {} } \ No newline at end of file diff --git a/drizzle/migrations/meta/0001_snapshot.json b/drizzle/migrations/meta/0001_snapshot.json new file mode 100644 index 00000000..d5bcfb49 --- /dev/null +++ b/drizzle/migrations/meta/0001_snapshot.json @@ -0,0 +1,750 @@ +{ + "id": "138b23d3-555f-4a4f-82c1-fd5634addcd3", + "prevId": "0d46923a-5423-4b73-91cb-5f46741e7ff9", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.calendar_notes": { + "name": "calendar_notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "date": { + "name": "date", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "location_tags": { + "name": "location_tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "user_tags": { + "name": "user_tags", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "map_feature_id": { + "name": "map_feature_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "calendar_notes_user_id_date_idx": { + "name": "calendar_notes_user_id_date_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "calendar_notes_user_id_users_id_fk": { + "name": "calendar_notes_user_id_users_id_fk", + "tableFrom": "calendar_notes", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "calendar_notes_chat_id_chats_id_fk": { + "name": "calendar_notes_chat_id_chats_id_fk", + "tableFrom": "calendar_notes", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chat_participants": { + "name": "chat_participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'collaborator'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "chat_participants_chat_id_chats_id_fk": { + "name": "chat_participants_chat_id_chats_id_fk", + "tableFrom": "chat_participants", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_participants_user_id_users_id_fk": { + "name": "chat_participants_user_id_users_id_fk", + "tableFrom": "chat_participants", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chat_participants_chat_user_unique": { + "name": "chat_participants_chat_user_unique", + "nullsNotDistinct": false, + "columns": [ + "chat_id", + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.chats": { + "name": "chats", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Untitled Chat'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'private'" + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "share_path": { + "name": "share_path", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "shareable_link_id": { + "name": "shareable_link_id", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "gen_random_uuid()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "chats_user_id_created_at_idx": { + "name": "chats_user_id_created_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "chats_user_id_users_id_fk": { + "name": "chats_user_id_users_id_fk", + "tableFrom": "chats", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "chats_shareable_link_id_unique": { + "name": "chats_shareable_link_id_unique", + "nullsNotDistinct": false, + "columns": [ + "shareable_link_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.locations": { + "name": "locations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "geojson": { + "name": "geojson", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "geometry": { + "name": "geometry", + "type": "geometry(GEOMETRY, 4326)", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "locations_user_id_users_id_fk": { + "name": "locations_user_id_users_id_fk", + "tableFrom": "locations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "locations_chat_id_chats_id_fk": { + "name": "locations_chat_id_chats_id_fk", + "tableFrom": "locations", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "location_id": { + "name": "location_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "messages_chat_id_created_at_idx": { + "name": "messages_chat_id_created_at_idx", + "columns": [ + { + "expression": "chat_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "messages_chat_id_chats_id_fk": { + "name": "messages_chat_id_chats_id_fk", + "tableFrom": "messages", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_user_id_users_id_fk": { + "name": "messages_user_id_users_id_fk", + "tableFrom": "messages", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_location_id_locations_id_fk": { + "name": "messages_location_id_locations_id_fk", + "tableFrom": "messages", + "tableTo": "locations", + "columnsFrom": [ + "location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_prompts": { + "name": "system_prompts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "prompt": { + "name": "prompt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "system_prompts_user_id_users_id_fk": { + "name": "system_prompts_user_id_users_id_fk", + "tableFrom": "system_prompts", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'viewer'" + }, + "selected_model": { + "name": "selected_model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "system_prompt": { + "name": "system_prompt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.visualizations": { + "name": "visualizations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "chat_id": { + "name": "chat_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'map_layer'" + }, + "data": { + "name": "data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "geometry": { + "name": "geometry", + "type": "geometry(GEOMETRY, 4326)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "visualizations_user_id_users_id_fk": { + "name": "visualizations_user_id_users_id_fk", + "tableFrom": "visualizations", + "tableTo": "users", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "visualizations_chat_id_chats_id_fk": { + "name": "visualizations_chat_id_chats_id_fk", + "tableFrom": "visualizations", + "tableTo": "chats", + "columnsFrom": [ + "chat_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "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/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json index 34cd1203..8dac5eb2 100644 --- a/drizzle/migrations/meta/_journal.json +++ b/drizzle/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1750358514791, "tag": "0000_sweet_metal_master", "breakpoints": true + }, + { + "idx": 1, + "version": "7", + "when": 1771148375605, + "tag": "0001_sync_and_add_indexes", + "breakpoints": true } ] } \ No newline at end of file diff --git a/lib/db/schema.ts b/lib/db/schema.ts index e8e6b943..128015c4 100644 --- a/lib/db/schema.ts +++ b/lib/db/schema.ts @@ -1,4 +1,4 @@ -import { pgTable, text, timestamp, uuid, jsonb, customType, unique } from 'drizzle-orm/pg-core'; +import { pgTable, text, timestamp, uuid, jsonb, customType, unique, index } from 'drizzle-orm/pg-core'; import { relations } from 'drizzle-orm'; // Custom type for PostGIS geometry @@ -37,6 +37,10 @@ export const chats = pgTable('chats', { shareableLinkId: uuid('shareable_link_id').defaultRandom().unique(), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(), +}, (table) => { + return { + userIdCreatedAtIdx: index('chats_user_id_created_at_idx').on(table.userId, table.createdAt), + } }); export const locations = pgTable('locations', { @@ -58,6 +62,10 @@ export const messages = pgTable('messages', { embedding: vector('embedding'), locationId: uuid('location_id').references(() => locations.id, { onDelete: 'set null' }), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), +}, (table) => { + return { + chatIdCreatedAtIdx: index('messages_chat_id_created_at_idx').on(table.chatId, table.createdAt), + } }); export const chatParticipants = pgTable('chat_participants', { @@ -100,6 +108,10 @@ export const calendarNotes = pgTable('calendar_notes', { mapFeatureId: text('map_feature_id'), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(), +}, (table) => { + return { + userIdDateIdx: index('calendar_notes_user_id_date_idx').on(table.userId, table.date), + } }); // Relations