From 65f5603c23ca1690f198066afb160619bcb42e0b Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Wed, 1 Apr 2026 22:25:52 +0000 Subject: [PATCH] feat(24-00): DB migrations and Drizzle schema updates for search/history/branching - Add 0050_add_branch_columns.sql: parent_conversation_id + branch_from_message_id on chat_conversations - Add 0051_add_message_search_vector.sql: content_search tsvector + GIN index on chat_messages - Add 0052_create_chat_message_bookmarks.sql: new bookmarks table with company/message/conversation FK - Update chat_conversations.ts: parentConversationId + branchFromMessageId columns + parentIdx - Update chat_messages.ts: add comment for generated tsvector column - Create chat_message_bookmarks.ts: Drizzle schema with indexes - Update schema/index.ts: export chatMessageBookmarks - Update _journal.json: entries for idx 50, 51, 52 --- .../migrations/0050_add_branch_columns.sql | 4 ++++ .../0051_add_message_search_vector.sql | 5 +++++ .../0052_create_chat_message_bookmarks.sql | 9 ++++++++ packages/db/src/migrations/meta/_journal.json | 21 +++++++++++++++++++ packages/db/src/schema/chat_conversations.ts | 5 ++++- .../db/src/schema/chat_message_bookmarks.ts | 19 +++++++++++++++++ packages/db/src/schema/chat_messages.ts | 1 + packages/db/src/schema/index.ts | 1 + 8 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 packages/db/src/migrations/0050_add_branch_columns.sql create mode 100644 packages/db/src/migrations/0051_add_message_search_vector.sql create mode 100644 packages/db/src/migrations/0052_create_chat_message_bookmarks.sql create mode 100644 packages/db/src/schema/chat_message_bookmarks.ts diff --git a/packages/db/src/migrations/0050_add_branch_columns.sql b/packages/db/src/migrations/0050_add_branch_columns.sql new file mode 100644 index 00000000..c4674b22 --- /dev/null +++ b/packages/db/src/migrations/0050_add_branch_columns.sql @@ -0,0 +1,4 @@ +ALTER TABLE "chat_conversations" + ADD COLUMN "parent_conversation_id" uuid REFERENCES "chat_conversations"("id") ON DELETE SET NULL, + ADD COLUMN "branch_from_message_id" uuid; +CREATE INDEX "chat_conversations_parent_idx" ON "chat_conversations" ("parent_conversation_id"); diff --git a/packages/db/src/migrations/0051_add_message_search_vector.sql b/packages/db/src/migrations/0051_add_message_search_vector.sql new file mode 100644 index 00000000..f5f2a84b --- /dev/null +++ b/packages/db/src/migrations/0051_add_message_search_vector.sql @@ -0,0 +1,5 @@ +ALTER TABLE "chat_messages" + ADD COLUMN "content_search" tsvector + GENERATED ALWAYS AS (to_tsvector('english', "content")) STORED; +CREATE INDEX "chat_messages_content_search_idx" + ON "chat_messages" USING GIN ("content_search"); diff --git a/packages/db/src/migrations/0052_create_chat_message_bookmarks.sql b/packages/db/src/migrations/0052_create_chat_message_bookmarks.sql new file mode 100644 index 00000000..90376a80 --- /dev/null +++ b/packages/db/src/migrations/0052_create_chat_message_bookmarks.sql @@ -0,0 +1,9 @@ +CREATE TABLE "chat_message_bookmarks" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid(), + "company_id" uuid NOT NULL REFERENCES "companies"("id"), + "message_id" uuid NOT NULL REFERENCES "chat_messages"("id") ON DELETE CASCADE, + "conversation_id" uuid NOT NULL REFERENCES "chat_conversations"("id") ON DELETE CASCADE, + "created_at" timestamp with time zone NOT NULL DEFAULT now() +); +CREATE INDEX "chat_bookmarks_company_message_idx" ON "chat_message_bookmarks" ("company_id", "message_id"); +CREATE INDEX "chat_bookmarks_company_conv_idx" ON "chat_message_bookmarks" ("company_id", "conversation_id"); diff --git a/packages/db/src/migrations/meta/_journal.json b/packages/db/src/migrations/meta/_journal.json index 73e14218..9e307a01 100644 --- a/packages/db/src/migrations/meta/_journal.json +++ b/packages/db/src/migrations/meta/_journal.json @@ -351,6 +351,27 @@ "when": 1775079588000, "tag": "0049_add_message_type", "breakpoints": true + }, + { + "idx": 50, + "version": "7", + "when": 1775200000000, + "tag": "0050_add_branch_columns", + "breakpoints": true + }, + { + "idx": 51, + "version": "7", + "when": 1775200001000, + "tag": "0051_add_message_search_vector", + "breakpoints": true + }, + { + "idx": 52, + "version": "7", + "when": 1775200002000, + "tag": "0052_create_chat_message_bookmarks", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema/chat_conversations.ts b/packages/db/src/schema/chat_conversations.ts index f1d781a9..cd6b8285 100644 --- a/packages/db/src/schema/chat_conversations.ts +++ b/packages/db/src/schema/chat_conversations.ts @@ -1,4 +1,4 @@ -import { pgTable, uuid, text, timestamp, index } from "drizzle-orm/pg-core"; +import { type AnyPgColumn, pgTable, uuid, text, timestamp, index } from "drizzle-orm/pg-core"; import { companies } from "./companies.js"; import { agents } from "./agents.js"; @@ -14,9 +14,12 @@ export const chatConversations = pgTable( deletedAt: timestamp("deleted_at", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), + parentConversationId: uuid("parent_conversation_id").references((): AnyPgColumn => chatConversations.id, { onDelete: "set null" }), + branchFromMessageId: uuid("branch_from_message_id"), }, (table) => ({ companyUpdatedIdx: index("chat_conversations_company_updated_idx").on(table.companyId, table.updatedAt), companyDeletedIdx: index("chat_conversations_company_deleted_idx").on(table.companyId, table.deletedAt), + parentIdx: index("chat_conversations_parent_idx").on(table.parentConversationId), }), ); diff --git a/packages/db/src/schema/chat_message_bookmarks.ts b/packages/db/src/schema/chat_message_bookmarks.ts new file mode 100644 index 00000000..81c90d15 --- /dev/null +++ b/packages/db/src/schema/chat_message_bookmarks.ts @@ -0,0 +1,19 @@ +import { pgTable, uuid, timestamp, index } from "drizzle-orm/pg-core"; +import { companies } from "./companies.js"; +import { chatMessages } from "./chat_messages.js"; +import { chatConversations } from "./chat_conversations.js"; + +export const chatMessageBookmarks = pgTable( + "chat_message_bookmarks", + { + id: uuid("id").primaryKey().defaultRandom(), + companyId: uuid("company_id").notNull().references(() => companies.id), + messageId: uuid("message_id").notNull().references(() => chatMessages.id, { onDelete: "cascade" }), + conversationId: uuid("conversation_id").notNull().references(() => chatConversations.id, { onDelete: "cascade" }), + createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), + }, + (table) => ({ + companyMessageIdx: index("chat_bookmarks_company_message_idx").on(table.companyId, table.messageId), + companyConvIdx: index("chat_bookmarks_company_conv_idx").on(table.companyId, table.conversationId), + }), +); diff --git a/packages/db/src/schema/chat_messages.ts b/packages/db/src/schema/chat_messages.ts index e3c79daa..2fc1de96 100644 --- a/packages/db/src/schema/chat_messages.ts +++ b/packages/db/src/schema/chat_messages.ts @@ -13,6 +13,7 @@ export const chatMessages = pgTable( messageType: text("message_type"), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow(), + // content_search tsvector column exists in Postgres (generated stored) — queried via sql`` only }, (table) => ({ conversationCreatedIdx: index("chat_messages_conversation_created_idx").on(table.conversationId, table.createdAt), diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts index 6b66fcf4..465f97ba 100644 --- a/packages/db/src/schema/index.ts +++ b/packages/db/src/schema/index.ts @@ -58,3 +58,4 @@ export { pluginWebhookDeliveries } from "./plugin_webhooks.js"; export { pluginLogs } from "./plugin_logs.js"; export { chatConversations } from "./chat_conversations.js"; export { chatMessages } from "./chat_messages.js"; +export { chatMessageBookmarks } from "./chat_message_bookmarks.js";