From 991edf0109199f98952ab98584673997fae3e2bd Mon Sep 17 00:00:00 2001 From: aneesazc Date: Tue, 30 Jul 2024 11:33:14 +0530 Subject: [PATCH 1/2] speed up queries for tournaments page --- app/api/tournaments/[id]/route.js | 5 +- app/api/tournaments/route.js | 92 +++++++++++++++---- app/page.js | 6 +- app/tournaments/[id]/page.js | 5 +- components/TournamentSection.jsx | 141 +----------------------------- lib/prefetchTournaments.js | 18 ++++ model/Tournament.js | 13 +++ package-lock.json | 14 ++- package.json | 6 +- 9 files changed, 138 insertions(+), 162 deletions(-) create mode 100644 lib/prefetchTournaments.js diff --git a/app/api/tournaments/[id]/route.js b/app/api/tournaments/[id]/route.js index 555abee..653ba70 100644 --- a/app/api/tournaments/[id]/route.js +++ b/app/api/tournaments/[id]/route.js @@ -15,8 +15,9 @@ export async function GET(request, { params }) { Organizer; const tournament = await Tournament.findById(id) - .populate('gameId') - .populate('organizerId') + .select('tournamentName tournamentDates gameType prize slots registeredNumber gameId organizerId') + .populate('gameId', 'name gameBannerPhoto') + .populate('organizerId', 'orgName bannerPhoto') .lean(); if (tournament) { diff --git a/app/api/tournaments/route.js b/app/api/tournaments/route.js index a08f897..a970695 100644 --- a/app/api/tournaments/route.js +++ b/app/api/tournaments/route.js @@ -3,38 +3,47 @@ import dbConnect from '../../../lib/dbConnect'; import Tournament from '../../../model/Tournament'; import Games from '../../../model/Games'; import Organizer from '../../../model/Organizer'; +import { prefetchTournaments } from '../../../lib/prefetchTournaments'; export async function GET(request) { - await dbConnect(); - const { searchParams } = new URL(request.url); const page = parseInt(searchParams.get('page') || '1'); const limit = parseInt(searchParams.get('limit') || '10'); const gameType = searchParams.get('gameType'); const organizerId = searchParams.get('organizerId'); - const skip = (page - 1) * limit; + try { + const { tournaments: allTournaments } = await prefetchTournaments(); - const query = {}; - if (gameType) query.gameType = gameType; - if (organizerId) query.organizerId = organizerId; + // Apply filters + let filteredTournaments = allTournaments; + if (gameType) { + filteredTournaments = filteredTournaments.filter(t => t.gameType === gameType); + } + if (organizerId) { + filteredTournaments = filteredTournaments.filter(t => t.organizerId._id.toString() === organizerId); + } - try { - const tournaments = await Tournament.find(query) - .populate('gameId', 'name category gameBannerPhoto') - .populate('organizerId', 'orgName bannerPhoto') - .skip(skip) - .limit(limit) - .lean(); + // Sort tournaments (adjust the sorting field as needed) + filteredTournaments.sort((a, b) => new Date(b.tournamentDates.started) - new Date(a.tournamentDates.started)); - const total = await Tournament.countDocuments(query); + // Apply pagination + const total = filteredTournaments.length; + const start = (page - 1) * limit; + const end = start + limit; + const paginatedTournaments = filteredTournaments.slice(start, end); return NextResponse.json({ - tournaments, + tournaments: paginatedTournaments, totalPages: Math.ceil(total / limit), currentPage: page, + }, { + headers: { + 'Cache-Control': 'public, s-maxage=60, stale-while-revalidate=600', + }, }); } catch (error) { + console.error('Error in GET /tournaments:', error); return NextResponse.json({ error: 'Internal Server Error', details: error.message @@ -124,3 +133,56 @@ export async function POST(request) { }, { status: 500 }); } } + + + + + + + + + +// import { NextResponse } from 'next/server'; +// import dbConnect from '../../../lib/dbConnect'; +// import Tournament from '../../../model/Tournament'; +// import Games from '../../../model/Games'; +// import Organizer from '../../../model/Organizer'; + +// export async function GET(request) { +// await dbConnect(); + +// const { searchParams } = new URL(request.url); +// const page = parseInt(searchParams.get('page') || '1'); +// const limit = parseInt(searchParams.get('limit') || '10'); +// const gameType = searchParams.get('gameType'); +// const organizerId = searchParams.get('organizerId'); + +// const skip = (page - 1) * limit; + +// const query = {}; +// if (gameType) query.gameType = gameType; +// if (organizerId) query.organizerId = organizerId; + +// try { +// const tournaments = await Tournament.find(query) +// .populate('gameId', 'name category gameBannerPhoto') +// .populate('organizerId', 'orgName bannerPhoto') +// .skip(skip) +// .limit(limit) +// .lean(); + +// const total = await Tournament.countDocuments(query); + +// return NextResponse.json({ +// tournaments, +// totalPages: Math.ceil(total / limit), +// currentPage: page, +// }); +// } catch (error) { +// return NextResponse.json({ +// error: 'Internal Server Error', +// details: error.message +// }, { status: 500 }); +// } +// } + diff --git a/app/page.js b/app/page.js index 5c43ab1..0807ce1 100644 --- a/app/page.js +++ b/app/page.js @@ -1,8 +1,12 @@ import HeroSection from "../components/HeroSection"; // import FeatureSection from "../components/FeatureSection/FeatureSection"; import FaqSection from "../components/FaqSection"; +import { prefetchTournaments } from '../lib/prefetchTournaments'; + +export default async function Home() { + // Prefetch tournaments data + await prefetchTournaments(); -export default function Home() { return (
diff --git a/app/tournaments/[id]/page.js b/app/tournaments/[id]/page.js index 6606df0..c3c40aa 100644 --- a/app/tournaments/[id]/page.js +++ b/app/tournaments/[id]/page.js @@ -1,6 +1,6 @@ import Image from 'next/image'; import Link from 'next/link'; -import { CalendarIcon, ClockIcon, PersonIcon, CrossCircledIcon, LockClosedIcon } from '@radix-ui/react-icons'; +import { CalendarIcon, ClockIcon } from '@radix-ui/react-icons'; import { Trophy, DollarSign, Users, Shield } from 'lucide-react'; import dbConnect from '../../../lib/dbConnect'; import Tournament from '../../../model/Tournament'; @@ -142,6 +142,9 @@ export default async function TournamentPage({ params }) { ); } +// Add caching and revalidation +export const revalidate = 60; // Revalidate this page every 60 seconds + // Static tournament data for UI demonstration // const tournament = { // id: "1", diff --git a/components/TournamentSection.jsx b/components/TournamentSection.jsx index 85f1fa8..dddd532 100644 --- a/components/TournamentSection.jsx +++ b/components/TournamentSection.jsx @@ -11,23 +11,17 @@ export default function TournamentSection({ filters }) { useEffect(() => { async function fetchTournaments() { - setIsLoading(true); try { const response = await fetch("/api/tournaments"); if (!response.ok) { - const errorData = await response.json(); - console.error("Error in fetchTournaments:", errorData); - throw new Error( - `Failed to fetch tournaments: ${response.status}. ${errorData.error} - ${errorData.details}` - ); + throw new Error("Failed to fetch tournaments"); } const data = await response.json(); - console.log("Fetched tournaments:", data); setTournaments(data.tournaments); + setIsLoading(false); } catch (err) { console.error("Error in fetchTournaments:", err); setError(err.message); - } finally { setIsLoading(false); } } @@ -76,6 +70,7 @@ export default function TournamentSection({ filters }) { key={tournament._id} href={`/tournaments/${tournament._id}`} className="hover:scale-105 transition-all" + prefetch={true} > @@ -176,133 +171,3 @@ function TournamentCard({ ); } - -// Example data for tournaments -// const tournamentsData = [ -// { -// image: -// "https://media.battlexo.com/tournament/668292838ab430dcee21f257/banner/icon/29fd717a-2be9-4c19-8394-865ff112a15a.webp", -// title: "KINGS ESPORTS PRO SCRIMS (12PM)", -// date: "JUL 1, 2024", -// time: "11:00 PM", -// entryFee: 500, -// mode: "Squad", -// participants: "16/21", -// host: { -// image: -// "https://media.battlexo.com/space/262/icon/43ed1dc5-1493-4932-ab9c-c7bedbdfe584.webp", -// name: "KINGS ESPORTS", -// }, -// }, -// { -// image: -// "https://media.battlexo.com/tournament/668292838ab430dcee21f257/banner/icon/29fd717a-2be9-4c19-8394-865ff112a15a.webp", -// title: "KINGS ESPORTS PRO SCRIMS (12PM)", -// date: "JUL 1, 2024", -// time: "11:00 PM", -// entryFee: 500, -// mode: "Duo", -// participants: "16/21", -// host: { -// image: -// "https://media.battlexo.com/space/262/icon/43ed1dc5-1493-4932-ab9c-c7bedbdfe584.webp", -// name: "KINGS ESPORTS", -// }, -// }, -// { -// image: -// "https://media.battlexo.com/tournament/668292838ab430dcee21f257/banner/icon/29fd717a-2be9-4c19-8394-865ff112a15a.webp", -// title: "KINGS ESPORTS PRO SCRIMS (12PM)", -// date: "JUL 1, 2024", -// time: "11:00 PM", -// entryFee: 500, -// mode: "Squad", -// participants: "16/21", -// host: { -// image: -// "https://media.battlexo.com/space/262/icon/43ed1dc5-1493-4932-ab9c-c7bedbdfe584.webp", -// name: "KINGS ESPORTS", -// }, -// }, -// { -// image: -// "https://media.battlexo.com/tournament/668292838ab430dcee21f257/banner/icon/29fd717a-2be9-4c19-8394-865ff112a15a.webp", -// title: "KINGS ESPORTS PRO SCRIMS (12PM)", -// date: "JUL 1, 2024", -// time: "11:00 PM", -// entryFee: 500, -// mode: "Squad", -// participants: "16/21", -// host: { -// image: -// "https://media.battlexo.com/space/262/icon/43ed1dc5-1493-4932-ab9c-c7bedbdfe584.webp", -// name: "KINGS ESPORTS", -// }, -// }, -// { -// image: -// "https://media.battlexo.com/tournament/668292838ab430dcee21f257/banner/icon/29fd717a-2be9-4c19-8394-865ff112a15a.webp", -// title: "KINGS ESPORTS PRO SCRIMS (12PM)", -// date: "JUL 1, 2024", -// time: "11:00 PM", -// entryFee: 500, -// mode: "Squad", -// participants: "16/21", -// status: "Completed", -// host: { -// image: -// "https://media.battlexo.com/space/262/icon/43ed1dc5-1493-4932-ab9c-c7bedbdfe584.webp", -// name: "KINGS ESPORTS", -// }, -// }, -// { -// image: -// "https://media.battlexo.com/tournament/668292838ab430dcee21f257/banner/icon/29fd717a-2be9-4c19-8394-865ff112a15a.webp", -// title: "KINGS ESPORTS PRO SCRIMS (12PM)", -// date: "JUL 1, 2024", -// time: "11:00 PM", -// entryFee: 500, -// mode: "Squad", -// participants: "16/21", -// host: { -// image: -// "https://media.battlexo.com/space/262/icon/43ed1dc5-1493-4932-ab9c-c7bedbdfe584.webp", -// name: "KINGS ESPORTS", -// }, -// }, -// { -// image: -// "https://media.battlexo.com/tournament/668292838ab430dcee21f257/banner/icon/29fd717a-2be9-4c19-8394-865ff112a15a.webp", -// title: "KINGS ESPORTS PRO SCRIMS (12PM)", -// date: "JUL 1, 2024", -// time: "11:00 PM", -// entryFee: 500, -// mode: "Squad", -// participants: "16/21", -// status: "Live", -// host: { -// image: -// "https://media.battlexo.com/space/262/icon/43ed1dc5-1493-4932-ab9c-c7bedbdfe584.webp", -// name: "KINGS ESPORTS", -// }, -// }, -// { -// image: -// "https://media.battlexo.com/tournament/668292838ab430dcee21f257/banner/icon/29fd717a-2be9-4c19-8394-865ff112a15a.webp", -// title: "KINGS ESPORTS PRO SCRIMS (12PM)", -// date: "JUL 1, 2024", -// time: "11:00 PM", -// entryFee: 500, -// mode: "Solo", -// participants: "16/21", -// host: { -// image: -// "https://media.battlexo.com/space/262/icon/43ed1dc5-1493-4932-ab9c-c7bedbdfe584.webp", -// name: "KINGS ESPORTS", -// }, -// }, -// ].map((tournament, index) => ({ -// ...tournament, -// id: index + 1, -// status: tournament.status || "Open", -// })); diff --git a/lib/prefetchTournaments.js b/lib/prefetchTournaments.js new file mode 100644 index 0000000..3f856ad --- /dev/null +++ b/lib/prefetchTournaments.js @@ -0,0 +1,18 @@ +import { cache } from 'react'; +import dbConnect from './dbConnect'; +import Tournament from '../model/Tournament'; + +export const prefetchTournaments = cache(async () => { + await dbConnect(); + try { + const tournaments = await Tournament.find() + .select('tournamentName tournamentDates gameType prize slots registeredNumber gameId organizerId') + .populate('gameId', 'gameBannerPhoto') + .populate('organizerId', 'orgName bannerPhoto') + .lean(); + return { tournaments }; + } catch (error) { + console.error('Error prefetching tournaments:', error); + return { tournaments: [] }; + } +}); \ No newline at end of file diff --git a/model/Tournament.js b/model/Tournament.js index 145c2f0..79e42f6 100644 --- a/model/Tournament.js +++ b/model/Tournament.js @@ -56,6 +56,19 @@ const TournamentSchema = new Schema({ size: String }); +// Add indexes +TournamentSchema.index({ organizerId: 1 }); +TournamentSchema.index({ gameId: 1 }); +TournamentSchema.index({ gameType: 1 }); +TournamentSchema.index({ tournamentVisibility: 1 }); +TournamentSchema.index({ tournamentStartDate: 1 }); +TournamentSchema.index({ registrationEndDate: 1 }); + +// Compound indexes for common query patterns +TournamentSchema.index({ organizerId: 1, gameType: 1 }); +TournamentSchema.index({ gameId: 1, tournamentStartDate: 1 }); +TournamentSchema.index({ tournamentVisibility: 1, registrationEndDate: 1 }); + const Tournament = mongoose.models.Tournament || mongoose.model('Tournament', TournamentSchema); module.exports = Tournament; diff --git a/package-lock.json b/package-lock.json index ede353e..076ba07 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "react-hook-form": "^7.52.1", "react-icons": "^5.2.1", "resend": "^3.4.0", + "swr": "^2.2.5", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", "usehooks-ts": "^3.1.0", @@ -14839,6 +14840,18 @@ "react": ">=17.0" } }, + "node_modules/swr": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.5.tgz", + "integrity": "sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==", + "dependencies": { + "client-only": "^0.0.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -15585,7 +15598,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", - "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } diff --git a/package.json b/package.json index 75191aa..cf46deb 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "dev": "next dev", - "build": "prisma generate && next build", + "build": "next build", "start": "next start", "lint": "next lint" }, @@ -12,7 +12,6 @@ "seed": "node prisma/seed.js" }, "dependencies": { - "@ewanmellor/brackets-viewer": "^1.6.911", "@hookform/resolvers": "^3.7.0", "@portabletext/react": "^3.0.18", "@prisma/client": "^5.16.1", @@ -28,8 +27,6 @@ "axios": "^1.7.2", "bcrypt": "^5.1.1", "bcryptjs": "^2.4.3", - "brackets-manager": "^1.6.4", - "brackets-viewer": "^1.6.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cobe": "^0.6.3", @@ -48,6 +45,7 @@ "react-hook-form": "^7.52.1", "react-icons": "^5.2.1", "resend": "^3.4.0", + "swr": "^2.2.5", "tailwind-merge": "^2.4.0", "tailwindcss-animate": "^1.0.7", "usehooks-ts": "^3.1.0", From 1d56b8bc39864296edfeff4e623d3f4ba1e7db81 Mon Sep 17 00:00:00 2001 From: aneesazc Date: Tue, 30 Jul 2024 11:38:58 +0530 Subject: [PATCH 2/2] speed up queries for tournaments page --- components/TournamentSection.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/TournamentSection.jsx b/components/TournamentSection.jsx index dddd532..ad41239 100644 --- a/components/TournamentSection.jsx +++ b/components/TournamentSection.jsx @@ -70,7 +70,7 @@ export default function TournamentSection({ filters }) { key={tournament._id} href={`/tournaments/${tournament._id}`} className="hover:scale-105 transition-all" - prefetch={true} + prefetch={true} // prefetch the tournament page >