Add high-scale optimizations: pg_trgm GIN indexes, infinite scroll pagination, virtualized card grid, fast groupBy sidebar counts
This commit is contained in:
1 parent
7096631f1e
commit
e1a861faea
3 files changed
+350
-222
No files matched your search
@@ -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: {
|
||||
|
||||
Reference in new issue
Block a user