diff --git a/.jules/bolt.md b/.jules/bolt.md index 1fe6484..24c7363 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -1,3 +1,7 @@ ## 2026-02-10 - Atomic Database Updates **Learning:** Read-modify-write patterns for counters (like `playCount`) cause race conditions and extra DB round trips. **Action:** Use atomic SQL updates (e.g., `playCount = playCount + 1`) with `returning()` to ensure data integrity and performance. + +## 2026-02-11 - Drizzle Query Instability +**Learning:** The `.nullsLast()` method on `desc()` sort operations causes type errors (`Property 'nullsLast' does not exist`) in this environment. +**Action:** Avoid `.nullsLast()` and rely on default null handling (or explicit `sql` fragments) for sorting. diff --git a/server/storage.ts b/server/storage.ts index c8fc131..0c55bd7 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -10,7 +10,7 @@ import { type Playlist, type InsertPlaylist, } from "@shared/schema"; -import { eq, desc, and, inArray, sql, getTableColumns } from "drizzle-orm"; +import { eq, desc, asc, and, inArray, sql, getTableColumns } from "drizzle-orm"; export interface IStorage { // Song CRUD @@ -140,7 +140,7 @@ export class DatabaseStorage implements IStorage { .from(songs) .innerJoin(songLikes, eq(songs.id, songLikes.songId)) .where(eq(songLikes.userId, userId)) - .orderBy(desc(songLikes.createdAt).nullsLast()); + .orderBy(desc(songLikes.createdAt)); } // === Playlists === @@ -160,22 +160,12 @@ export class DatabaseStorage implements IStorage { const playlist = await this.getPlaylist(id); if (!playlist) return undefined; - const playlistSongRows = await db.select({ songId: playlistSongs.songId }) - .from(playlistSongs) - .where(eq(playlistSongs.playlistId, id)); - - const songIds = playlistSongRows.map(r => r.songId).filter(id => typeof id === 'number' && !isNaN(id)); - - if (songIds.length === 0) { - return { ...playlist, songs: [] }; - } - - const songsResult = await db.select() + // Optimized: Single query with innerJoin to avoid N+1 and ID mapping overhead + const songsList = await db.select(getTableColumns(songs)) .from(songs) - .where(inArray(songs.id, songIds)); - - const songMap = new Map(songsResult.map(s => [s.id, s])); - const songsList = songIds.map(id => songMap.get(id)).filter((s): s is Song => !!s); + .innerJoin(playlistSongs, eq(songs.id, playlistSongs.songId)) + .where(eq(playlistSongs.playlistId, id)) + .orderBy(asc(playlistSongs.addedAt)); return { ...playlist, songs: songsList }; }