Popularity Scoring
The Express API runs a cron job every 10 minutes that recalculates a popularityScore for every activity. This score powers the "Popular" sort option and featured sections.
Scoring Formula
popularityScore = views × 1
+ bookingsCount × 10
+ averageRating × 20
+ (isFeatured ? 100 : 0)
- (daysSinceCreation × 0.5)Weight Breakdown
| Factor | Weight | Rationale |
|---|---|---|
| Views | 1× | Baseline engagement signal |
| Bookings count | 10× | Strong conversion signal |
| Average rating | 20× | Quality signal (0–5 → up to 100 pts) |
| Is featured | +100 | Admin boost for promoted activities |
| Age penalty | −0.5/day | Prevents old activities from dominating |
Example Calculation
For an activity that is:
- Viewed 1,000 times
- Booked 50 times
- Rated 4.5★
- Featured
- Created 30 days ago
score = 1000 × 1 = 1000
+ 50 × 10 = 500
+ 4.5 × 20 = 90
+ 100 = 100
- 30 × 0.5 = -15
──────────────────────
= 1675Cron Implementation
File: src/cron/updatePopularity.cron.ts
typescript
import cron from "node-cron";
cron.schedule("*/10 * * * *", async () => {
const activities = await prisma.activity.findMany();
for (const activity of activities) {
const daysSinceCreation = Math.floor(
(Date.now() - activity.createdAt.getTime()) / (1000 * 60 * 60 * 24)
);
const score =
activity.views * 1 +
activity.bookingsCount * 10 +
(activity.averageRating || 0) * 20 +
(activity.isFeatured ? 100 : 0) -
daysSinceCreation * 0.5;
await prisma.activity.update({
where: { id: activity.id },
data: { popularityScore: Math.max(0, Math.round(score)) },
});
}
});Usage
- Sort option:
GET /api/v1/activity?sort=popularity - Featured sections: Frontend calls
GET /api/v1/activity/featuredfor promoted content - Homepage: Popular activities section
Database Index
The popularityScore field is indexed for efficient sorting:
prisma
@@index([popularityScore])