Add high-scale optimizations: pg_trgm GIN indexes, infinite scroll pagination, virtualized card grid, fast groupBy sidebar counts

This commit is contained in:
chrisf committed 2026-10-22 17:25:23 -06:00
1 parent 7096631f1e
commit e1a861faea
3 files changed
+350 -222

No files matched your search

+30 -18
View File
@@ -16,6 +16,8 @@ export async function GET() {
shortcutNotes,
allNotesCount,
trashCount,
notebookCountsRaw,
tagCountsRaw,
] = await Promise.all([
prisma.user.findMany({
orderBy: { createdAt: 'asc' },
@@ -27,24 +29,10 @@ export async function GET() {
prisma.notebook.findMany({
where: { userId },
orderBy: [{ isDefault: 'desc' }, { sortOrder: 'asc' }, { name: 'asc' }],
include: {
_count: {
select: {
notes: { where: { isTrashed: false } },
},
},
},
}),
prisma.tag.findMany({
where: { userId },
orderBy: { name: 'asc' },
include: {
_count: {
select: {
notes: { where: { note: { isTrashed: false, userId } } },
},
},
},
}),
prisma.savedSearch.findMany({
where: { userId },
@@ -57,25 +45,49 @@ export async function GET() {
}),
prisma.note.count({ where: { userId, isTrashed: false } }),
prisma.note.count({ where: { userId, isTrashed: true } }),
// Fast single GROUP BY for notebook note counts
prisma.note.groupBy({
by: ['notebookId'],
where: { userId, isTrashed: false },
_count: { _all: true },
}),
// Fast single raw GROUP BY query for tag note counts
prisma.$queryRaw<Array<{ tagId: string; count: bigint }>>`
SELECT nt."tagId", COUNT(nt."noteId") as count
FROM "NoteTag" nt
JOIN "Note" n ON n.id = nt."noteId"
WHERE n."userId" = ${userId} AND n."isTrashed" = false
GROUP BY nt."tagId"
`,
]);
const notebookCountMap = new Map<string, number>();
for (const item of notebookCountsRaw) {
notebookCountMap.set(item.notebookId, item._count._all);
}
const tagCountMap = new Map<string, number>();
for (const item of tagCountsRaw) {
tagCountMap.set(item.tagId, Number(item.count));
}
return NextResponse.json({
currentUser,
users,
stacks,
notebooks: notebooks.map((nb) => ({
...nb,
noteCount: nb._count.notes,
noteCount: notebookCountMap.get(nb.id) || 0,
})),
tags: tags.map((t) => ({
...t,
noteCount: t._count.notes,
noteCount: tagCountMap.get(t.id) || 0,
})),
savedSearches,
shortcuts: {
notes: shortcutNotes,
notebooks: notebooks.filter((n) => n.isShortcut),
tags: tags.filter((t) => t.isShortcut),
notebooks: notebooks.filter((n) => n.isShortcut).map(nb => ({ ...nb, noteCount: notebookCountMap.get(nb.id) || 0 })),
tags: tags.filter((t) => t.isShortcut).map(t => ({ ...t, noteCount: tagCountMap.get(t.id) || 0 })),
savedSearches: savedSearches.filter((s) => s.isShortcut),
},
counts: {