The confessions page under the university tab was only showing confession counts but was missing:
- Student Count: Number of students from each university
- Total Votes: Total votes from students who attended that university
Root Cause: The original implementation was trying to match users by university ID, but users actually store university names as strings in the university field.
Created three API endpoints for university statistics:
- Uses MongoDB aggregation pipelines for maximum performance
- Calculates student counts and vote totals in parallel
- Includes aggressive caching (30 minutes)
- Best for production use
- Uses simple Prisma queries
- More reliable but slower
- Includes moderate caching (15 minutes)
- Fallback when optimized endpoint fails
- Basic implementation for reference
- Not used in production
Added performance indexes:
// User collection
{ university: 1 } // For fast student count queries
// ConfessionVote collection
{ userId: 1, confessionId: 1 } // For vote aggregation
// University collection
{ confessionCount: -1, isVerified: -1, name: 1 } // For sorting
{ region: 1, country: 1 } // For filteringUpdated useUniversities hook with:
- Fallback mechanism: Tries optimized endpoint first, falls back to simple endpoint
- Error handling: Graceful degradation if endpoints fail
- Caching: Development cache-busting with production caching
- Performance logging: Shows which endpoint was used
Each university now returns:
{
id: string;
name: string;
domain?: string;
isVerified: boolean;
confessionCount: number; // ✅ Already working
studentCount: number; // ✅ NEW: Students from this university
totalVotes: number; // ✅ NEW: Votes from students of this university
createdAt: string;
updatedAt: string;
}- Indexes: Added strategic indexes for fast queries
- Aggregation: Uses MongoDB aggregation pipelines for bulk operations
- Parallel Processing: Calculates stats for multiple universities simultaneously
- Caching: 30-minute cache for optimized endpoint, 15-minute for simple
- Pagination: Efficient pagination with limit/offset
- Error Handling: Graceful fallbacks prevent complete failures
- Lazy Loading: Only loads data when universities tab is active
- Debounced Search: Prevents excessive API calls during typing
- Infinite Scroll: Efficient pagination for large datasets
The university tab now displays:
- Confession Count: Number of confessions from this university
- Student Count: Number of students registered from this university
- Total Votes: Total votes cast by students from this university
Created test scripts:
scripts/test-university-stats.ts: Validates data accuracyscripts/create-university-stats-indexes.ts: Sets up performance indexes
The implementation includes:
- Console logging for endpoint usage
- Error tracking for failed requests
- Performance metrics for query execution
Potential improvements:
- Real-time Updates: WebSocket updates for live statistics
- Advanced Analytics: Trend analysis and growth metrics
- Caching Layer: Redis for even faster response times
- Background Jobs: Scheduled recalculation of statistics
src/app/api/universities/stats/route.tssrc/app/api/universities/stats-optimized/route.tssrc/app/api/universities/stats-simple/route.tsscripts/create-university-stats-indexes.tsscripts/test-university-stats.tsUNIVERSITY_STATISTICS_IMPLEMENTATION.md
src/hooks/useUniversities.ts: Updated to use new endpoints with fallbacksrc/app/meetups/confessions/page.tsx: Now displays student count and total votes
The implementation works with the existing schema:
User.university: String field storing university name (not ID)ConfessionVote: Links votes to usersUniversity.confessionCount: Pre-calculated confession count
Key Fix: Changed queries to match users by university name instead of university ID, since users store the university name as a string.
No schema changes required - the solution works with existing data structure.