Skip to content

Commit d1562b2

Browse files
authored
[STAGING] FAC-122.1 to FAC-123.2 fix schema drift (#311)
* FAC-122.1 chore: add @Index expression metadata for soft-delete partial indexes (#308) * FAC-122.2 fix: explicitly type CustomBaseEntity.deletedAt to fix varchar(255) reflection (#309) TypeScript's emitDecoratorMetadata can't reflect optional Date types without initializers, causing MikroORM to fall back to varchar(255). This adds explicit type: 'datetime' to the deletedAt decorator and includes a migration to convert 29 affected tables from varchar(255) to timestamptz. Closes #306 * FAC-122.3 fix: resolve MikroORM migration drift for audit_log, recommended_action, and playing_with_neon (#310) - Add defaultRaw: 'now()' and length: 6 to AuditLog.occurredAt entity - Add Opt type annotation and app-level default to match codebase pattern - Update snapshot to align audit_log.occurred_at.default with entity - Fix Migration20260412153923 to drop/recreate matviews before altering deleted_at columns - Clean Migration20260412161915 to remove redundant deleted_at statements - Add SET DEFAULT now() to audit_log.occurred_at in migration UP Verified: npx mikro-orm migration:check exits 0, all 885 tests pass
1 parent 8f33425 commit d1562b2

10 files changed

Lines changed: 461 additions & 529 deletions

src/entities/audit-log.entity.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ export class AuditLog {
3939
ipAddress?: string;
4040

4141
@Index()
42-
@Property()
43-
occurredAt!: Date;
42+
@Property({ defaultRaw: 'now()', length: 6 })
43+
occurredAt: Date & Opt = new Date();
4444
}

src/entities/base.entity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export abstract class CustomBaseEntity {
1111
@Property()
1212
updatedAt: Date & Opt = new Date();
1313

14-
@Property({ nullable: true })
14+
@Property({ type: 'datetime', nullable: true })
1515
deletedAt?: Date & Opt;
1616

1717
SoftDelete() {

src/entities/questionnaire-type.entity.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import { Entity, Property, Index } from '@mikro-orm/core';
22
import { CustomBaseEntity } from './base.entity';
33
import { QuestionnaireTypeRepository } from '../repositories/questionnaire-type.repository';
44

5+
@Index({
6+
name: 'questionnaire_type_code_unique',
7+
expression:
8+
'create unique index "questionnaire_type_code_unique" on "questionnaire_type" ("code") where "deleted_at" is null',
9+
})
510
@Entity({ repository: () => QuestionnaireTypeRepository })
611
export class QuestionnaireType extends CustomBaseEntity {
712
@Property()

src/entities/report-job.entity.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ export type ReportJobStatus =
1010
| 'failed'
1111
| 'skipped';
1212

13+
@Index({
14+
name: 'uq_report_job_pending',
15+
expression: `create unique index "uq_report_job_pending" on "report_job" ("faculty_id", "semester_id", "questionnaire_type_code", "report_type") where status in ('waiting', 'active') and deleted_at is null`,
16+
})
17+
@Index({
18+
name: 'report_job_batch_id_index',
19+
expression:
20+
'create index "report_job_batch_id_index" on "report_job" ("batch_id") where batch_id is not null',
21+
})
22+
@Index({
23+
name: 'report_job_status_completed_at_index',
24+
properties: ['status', 'completedAt'],
25+
})
1326
@Entity({ repository: () => ReportJobRepository })
1427
export class ReportJob extends CustomBaseEntity {
1528
@Property()
@@ -36,7 +49,7 @@ export class ReportJob extends CustomBaseEntity {
3649
questionnaireTypeCode: string;
3750

3851
@Property({ nullable: true })
39-
@Index()
52+
// Indexed via class-level @Index (name: 'report_job_batch_id_index') — see top of file
4053
batchId?: string;
4154

4255
@Property({ nullable: true })

src/entities/sentiment-result.entity.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ import { SentimentResultRepository } from '../repositories/sentiment-result.repo
44
import { SentimentRun } from './sentiment-run.entity';
55
import { QuestionnaireSubmission } from './questionnaire-submission.entity';
66

7+
@Index({
8+
name: 'idx_sr_submission_processed',
9+
expression:
10+
'create index "idx_sr_submission_processed" on "sentiment_result" ("submission_id", "processed_at" desc) where "deleted_at" is null',
11+
})
12+
@Index({
13+
name: 'sentiment_result_run_id_submission_id_unique',
14+
expression:
15+
'create unique index "sentiment_result_run_id_submission_id_unique" on "sentiment_result" ("run_id", "submission_id") where "deleted_at" is null',
16+
})
717
@Entity({ repository: () => SentimentResultRepository })
818
@Index({ properties: ['run'] })
919
@Index({ properties: ['submission'] })

src/entities/submission-embedding.entity.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import { CustomBaseEntity } from './base.entity';
44
import { QuestionnaireSubmission } from './questionnaire-submission.entity';
55
import { SubmissionEmbeddingRepository } from '../repositories/submission-embedding.repository';
66

7+
@Index({
8+
name: 'submission_embedding_submission_id_unique',
9+
expression:
10+
'create unique index "submission_embedding_submission_id_unique" on "submission_embedding" ("submission_id") where "deleted_at" is null',
11+
})
712
@Entity({ repository: () => SubmissionEmbeddingRepository })
813
@Index({ properties: ['submission'] })
914
export class SubmissionEmbedding extends CustomBaseEntity {

src/entities/topic-assignment.entity.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import { TopicAssignmentRepository } from '../repositories/topic-assignment.repo
44
import { Topic } from './topic.entity';
55
import { QuestionnaireSubmission } from './questionnaire-submission.entity';
66

7+
@Index({
8+
name: 'topic_assignment_topic_id_submission_id_unique',
9+
expression:
10+
'create unique index "topic_assignment_topic_id_submission_id_unique" on "topic_assignment" ("topic_id", "submission_id") where "deleted_at" is null',
11+
})
712
@Entity({ repository: () => TopicAssignmentRepository })
813
@Index({ properties: ['topic'] })
914
@Index({ properties: ['submission'] })

0 commit comments

Comments
 (0)