diff --git a/src/messaging/message.entity.ts b/src/messaging/message.entity.ts index 55d0b109..5395eb81 100644 --- a/src/messaging/message.entity.ts +++ b/src/messaging/message.entity.ts @@ -5,6 +5,7 @@ import { CreateDateColumn, ManyToOne, JoinColumn, + Index, } from 'typeorm'; import { User } from '../users/entities/user.entity'; // Assuming a User entity exists @@ -14,15 +15,19 @@ export class Message { id: string; @Column({ type: 'uuid' }) + @Index('IDX_messages_senderId') senderId: string; @Column({ type: 'uuid' }) + @Index('IDX_messages_recipientId') recipientId: string; @Column({ type: 'text' }) content: string; @CreateDateColumn({ type: 'timestamptz' }) + @Index('IDX_messages_sender_recipient_createdAt') + @Index('IDX_messages_recipient_sender_createdAt') createdAt: Date; @Column({ type: 'timestamptz', nullable: true }) diff --git a/src/migrations/1804000000000-add-message-indexes.ts b/src/migrations/1804000000000-add-message-indexes.ts new file mode 100644 index 00000000..6839afa4 --- /dev/null +++ b/src/migrations/1804000000000-add-message-indexes.ts @@ -0,0 +1,40 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +/** + * Adds indexes to the `messages` table to cover the common query paths: + * + * - "load conversation between two users" + * → WHERE "senderId" = $1 AND "recipientId" = $2 + * or WHERE "recipientId" = $1 AND "senderId" = $2 + * → ORDER BY "createdAt" + * - "lookup by sender" / "lookup by recipient" + * → WHERE "senderId" = $1 + * → WHERE "recipientId" = $1 + * + * The composite indexes on (senderId, recipientId, createdAt) and + * (recipientId, senderId, createdAt) allow the conversation query to use + * index-only scans and avoid a separate sort step. + */ +export class AddMessageIndexes1804000000000 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + 'CREATE INDEX IF NOT EXISTS "IDX_messages_senderId" ON "messages" ("senderId")', + ); + await queryRunner.query( + 'CREATE INDEX IF NOT EXISTS "IDX_messages_recipientId" ON "messages" ("recipientId")', + ); + await queryRunner.query( + 'CREATE INDEX IF NOT EXISTS "IDX_messages_sender_recipient_createdAt" ON "messages" ("senderId", "recipientId", "createdAt")', + ); + await queryRunner.query( + 'CREATE INDEX IF NOT EXISTS "IDX_messages_recipient_sender_createdAt" ON "messages" ("recipientId", "senderId", "createdAt")', + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query('DROP INDEX IF EXISTS "IDX_messages_recipient_sender_createdAt"'); + await queryRunner.query('DROP INDEX IF EXISTS "IDX_messages_sender_recipient_createdAt"'); + await queryRunner.query('DROP INDEX IF EXISTS "IDX_messages_recipientId"'); + await queryRunner.query('DROP INDEX IF EXISTS "IDX_messages_senderId"'); + } +}