Skip to content

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

FactorWeightRationale
ViewsBaseline engagement signal
Bookings count10×Strong conversion signal
Average rating20×Quality signal (0–5 → up to 100 pts)
Is featured+100Admin boost for promoted activities
Age penalty−0.5/dayPrevents 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
      ──────────────────────
      = 1675

Cron 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/featured for promoted content
  • Homepage: Popular activities section

Database Index

The popularityScore field is indexed for efficient sorting:

prisma
@@index([popularityScore])

Built with VitePress