diff --git a/src/modules/cards/application/useCases/queries/GetLibrariesForUrlUseCase.ts b/src/modules/cards/application/useCases/queries/GetLibrariesForUrlUseCase.ts index 1857a49c..0d504ae9 100644 --- a/src/modules/cards/application/useCases/queries/GetLibrariesForUrlUseCase.ts +++ b/src/modules/cards/application/useCases/queries/GetLibrariesForUrlUseCase.ts @@ -49,7 +49,9 @@ export class GetLibrariesForUrlUseCase // Validate URL const urlResult = URL.create(query.url); if (urlResult.isErr()) { - return err(new ValidationError(`Invalid URL: ${urlResult.error.message}`)); + return err( + new ValidationError(`Invalid URL: ${urlResult.error.message}`), + ); } // Set defaults diff --git a/src/modules/cards/infrastructure/http/controllers/GetLibrariesForUrlController.ts b/src/modules/cards/infrastructure/http/controllers/GetLibrariesForUrlController.ts index 44ff6af6..8dffc23b 100644 --- a/src/modules/cards/infrastructure/http/controllers/GetLibrariesForUrlController.ts +++ b/src/modules/cards/infrastructure/http/controllers/GetLibrariesForUrlController.ts @@ -11,8 +11,12 @@ export class GetLibrariesForUrlController extends Controller { async executeImpl(req: Request, res: Response): Promise { try { const { url } = req.query; - const page = req.query.page ? parseInt(req.query.page as string) : undefined; - const limit = req.query.limit ? parseInt(req.query.limit as string) : undefined; + const page = req.query.page + ? parseInt(req.query.page as string) + : undefined; + const limit = req.query.limit + ? parseInt(req.query.limit as string) + : undefined; const sortBy = req.query.sortBy as CardSortField; const sortOrder = req.query.sortOrder as SortOrder; diff --git a/src/modules/cards/infrastructure/repositories/query-services/UrlCardQueryService.ts b/src/modules/cards/infrastructure/repositories/query-services/UrlCardQueryService.ts index 6333aef9..fa08dad9 100644 --- a/src/modules/cards/infrastructure/repositories/query-services/UrlCardQueryService.ts +++ b/src/modules/cards/infrastructure/repositories/query-services/UrlCardQueryService.ts @@ -275,12 +275,7 @@ export class UrlCardQueryService { }) .from(libraryMemberships) .innerJoin(cards, eq(libraryMemberships.cardId, cards.id)) - .where( - and( - eq(cards.url, url), - eq(cards.type, CardTypeEnum.URL), - ), - ) + .where(and(eq(cards.url, url), eq(cards.type, CardTypeEnum.URL))) .limit(limit) .offset(offset); @@ -291,12 +286,7 @@ export class UrlCardQueryService { .select({ count: count() }) .from(libraryMemberships) .innerJoin(cards, eq(libraryMemberships.cardId, cards.id)) - .where( - and( - eq(cards.url, url), - eq(cards.type, CardTypeEnum.URL), - ), - ); + .where(and(eq(cards.url, url), eq(cards.type, CardTypeEnum.URL))); const totalCount = totalCountResult[0]?.count || 0; const hasMore = offset + librariesResult.length < totalCount; diff --git a/src/modules/cards/infrastructure/repositories/schema/card.sql.ts b/src/modules/cards/infrastructure/repositories/schema/card.sql.ts index 0a006650..2be3c465 100644 --- a/src/modules/cards/infrastructure/repositories/schema/card.sql.ts +++ b/src/modules/cards/infrastructure/repositories/schema/card.sql.ts @@ -37,11 +37,12 @@ export const cards: PgTableWithColumns = pgTable( // Performance indexes // Optimizes sorting cards by type and update time in query results - typeUpdatedAtIdx: index('idx_cards_type_updated_at') - .on(table.type, table.updatedAt.desc()), + typeUpdatedAtIdx: index('idx_cards_type_updated_at').on( + table.type, + table.updatedAt.desc(), + ), // Index for getLibrariesForUrl - fast URL+type lookups - urlTypeIdx: index('idx_cards_url_type') - .on(table.url, table.type), + urlTypeIdx: index('idx_cards_url_type').on(table.url, table.type), // Partial index for finding NOTE cards by parent - only indexes NOTE type cards parentTypeIdx: index('idx_cards_parent_type') .on(table.parentCardId, table.type) diff --git a/src/modules/cards/infrastructure/repositories/schema/collection.sql.ts b/src/modules/cards/infrastructure/repositories/schema/collection.sql.ts index 85b42ce6..1710cbba 100644 --- a/src/modules/cards/infrastructure/repositories/schema/collection.sql.ts +++ b/src/modules/cards/infrastructure/repositories/schema/collection.sql.ts @@ -74,11 +74,14 @@ export const collectionCards = pgTable( ), // Performance indexes // Index for getCardsInCollection - sorted by add time - collectionAddedIdx: index('idx_collection_cards_collection_added') - .on(table.collectionId, table.addedAt.desc()), + collectionAddedIdx: index('idx_collection_cards_collection_added').on( + table.collectionId, + table.addedAt.desc(), + ), // Index for finding collections containing a card - cardCollectionIdx: index('idx_collection_cards_card_collection') - .on(table.cardId), + cardCollectionIdx: index('idx_collection_cards_card_collection').on( + table.cardId, + ), }; }, ); diff --git a/src/modules/cards/infrastructure/repositories/schema/libraryMembership.sql.ts b/src/modules/cards/infrastructure/repositories/schema/libraryMembership.sql.ts index 755967fd..998b3ad5 100644 --- a/src/modules/cards/infrastructure/repositories/schema/libraryMembership.sql.ts +++ b/src/modules/cards/infrastructure/repositories/schema/libraryMembership.sql.ts @@ -28,8 +28,9 @@ export const libraryMemberships = pgTable( cardUsersIdx: index('idx_card_users').on(table.cardId), // Performance indexes // Index for getUrlCardsOfUser - userTypeCoveringIdx: index('idx_library_memberships_user_type_covering') - .on(table.userId, table.addedAt.desc()), + userTypeCoveringIdx: index( + 'idx_library_memberships_user_type_covering', + ).on(table.userId, table.addedAt.desc()), }; }, ); diff --git a/src/modules/cards/tests/test-utils/createTestSchema.ts b/src/modules/cards/tests/test-utils/createTestSchema.ts index 61fc1fee..0cc3131c 100644 --- a/src/modules/cards/tests/test-utils/createTestSchema.ts +++ b/src/modules/cards/tests/test-utils/createTestSchema.ts @@ -100,32 +100,32 @@ export async function createTestSchema(db: PostgresJsDatabase) { ON library_memberships(user_id, added_at DESC) INCLUDE (card_id) `); - + // Optimizes sorting cards by type and update time in query results await db.execute(sql` CREATE INDEX IF NOT EXISTS idx_cards_type_updated_at ON cards(type, updated_at DESC) `); - + // Covering index for getLibrariesForUrl - fast URL+type lookups with card ID included await db.execute(sql` CREATE INDEX IF NOT EXISTS idx_cards_url_type ON cards(url, type) INCLUDE (id) `); - + // Covering index for getCardsInCollection - sorted by add time with cardId included await db.execute(sql` CREATE INDEX IF NOT EXISTS idx_collection_cards_collection_added ON collection_cards(collection_id, added_at DESC) INCLUDE (card_id) `); - + // Partial index for finding NOTE cards by parent - only indexes NOTE type cards await db.execute(sql` CREATE INDEX IF NOT EXISTS idx_cards_parent_type ON cards(parent_card_id, type) WHERE type = 'NOTE' `); - + // Covering index for finding collections containing a card - avoids table lookups await db.execute(sql` CREATE INDEX IF NOT EXISTS idx_collection_cards_card_collection diff --git a/src/shared/infrastructure/database/migrations/meta/0007_snapshot.json b/src/shared/infrastructure/database/migrations/meta/0007_snapshot.json index 020b6f1a..7332378e 100644 --- a/src/shared/infrastructure/database/migrations/meta/0007_snapshot.json +++ b/src/shared/infrastructure/database/migrations/meta/0007_snapshot.json @@ -224,12 +224,8 @@ "name": "cards_parent_card_id_cards_id_fk", "tableFrom": "cards", "tableTo": "cards", - "columnsFrom": [ - "parent_card_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["parent_card_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -237,12 +233,8 @@ "name": "cards_published_record_id_published_records_id_fk", "tableFrom": "cards", "tableTo": "published_records", - "columnsFrom": [ - "published_record_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["published_record_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -368,12 +360,8 @@ "name": "collection_cards_collection_id_collections_id_fk", "tableFrom": "collection_cards", "tableTo": "collections", - "columnsFrom": [ - "collection_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["collection_id"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -381,12 +369,8 @@ "name": "collection_cards_card_id_cards_id_fk", "tableFrom": "collection_cards", "tableTo": "cards", - "columnsFrom": [ - "card_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["card_id"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -394,12 +378,8 @@ "name": "collection_cards_published_record_id_published_records_id_fk", "tableFrom": "collection_cards", "tableTo": "published_records", - "columnsFrom": [ - "published_record_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["published_record_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -439,12 +419,8 @@ "name": "collection_collaborators_collection_id_collections_id_fk", "tableFrom": "collection_collaborators", "tableTo": "collections", - "columnsFrom": [ - "collection_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["collection_id"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -560,12 +536,8 @@ "name": "collections_published_record_id_published_records_id_fk", "tableFrom": "collections", "tableTo": "published_records", - "columnsFrom": [ - "published_record_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["published_record_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -664,12 +636,8 @@ "name": "library_memberships_card_id_cards_id_fk", "tableFrom": "library_memberships", "tableTo": "cards", - "columnsFrom": [ - "card_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["card_id"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -677,12 +645,8 @@ "name": "library_memberships_published_record_id_published_records_id_fk", "tableFrom": "library_memberships", "tableTo": "published_records", - "columnsFrom": [ - "published_record_id" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["published_record_id"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -690,10 +654,7 @@ "compositePrimaryKeys": { "library_memberships_card_id_user_id_pk": { "name": "library_memberships_card_id_user_id_pk", - "columns": [ - "card_id", - "user_id" - ] + "columns": ["card_id", "user_id"] } }, "uniqueConstraints": {}, @@ -925,12 +886,8 @@ "name": "auth_refresh_tokens_user_did_users_id_fk", "tableFrom": "auth_refresh_tokens", "tableTo": "users", - "columnsFrom": [ - "user_did" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["user_did"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -990,4 +947,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/src/shared/infrastructure/database/migrations/meta/_journal.json b/src/shared/infrastructure/database/migrations/meta/_journal.json index d1584f49..4e8f36d0 100644 --- a/src/shared/infrastructure/database/migrations/meta/_journal.json +++ b/src/shared/infrastructure/database/migrations/meta/_journal.json @@ -59,4 +59,4 @@ "breakpoints": true } ] -} \ No newline at end of file +}