Filter counts that show global totals instead of intersected totals (ISO 25010:2011 functional-suitability) actively mislead users. A user filtering by "Restaurants" who then sees "Rating: 4+ stars (847)" expects that number to reflect restaurants with high ratings — if it still reflects all 847 rated listings in the directory, they'll apply the filter expecting hundreds of results and land on a handful. This mismatch generates rage-clicks, support tickets, and abandoned sessions. The data contract between UI label and backend query must be exact: a count badge is a promise.
High because stale global counts misrepresent the result set, causing users to make filter decisions based on incorrect data and destroying trust in the search experience.
Expose a /api/listings/filter-counts endpoint that accepts all active filter params and returns counts scoped to their intersection. Call it every time the active filter set changes.
// /api/listings/filter-counts
export async function GET(req) {
const category = req.nextUrl.searchParams.get('category')
const baseWhere = { category } // extend with all active filters
return Response.json({
ratings: {
'5': await countListings({ ...baseWhere, rating: 5 }),
'4plus': await countListings({ ...baseWhere, rating: { gte: 4 } }),
}
})
}
On the frontend, trigger a refetch of filter counts inside the same effect that fires when any active filter changes, and update the displayed badge values from the response.
ID: directory-search-discovery.facets-filters.filter-counts-dynamic
Severity: high
What to look for: Enumerate all relevant files and Apply a filter (e.g., category = "Restaurants"). Then examine the count next to other filters (e.g., "Rating: 4+ stars (23)" should change to reflect the intersection with the current category). Apply a second filter and verify counts update again. The counts should show how many results match that filter in combination with currently active filters, not globally.
Pass criteria: Filter counts (if shown) update dynamically as filters are applied/removed. Counts reflect the intersection of all active filters, not global counts. If no filter counts are shown, this check passes (counts are optional).
Fail criteria: Filter counts do not update when other filters are applied, showing static/global counts instead of filtered counts.
Skip (N/A) when: Directory has fewer than 3 filterable attributes (see Global N/A Rule), or filter counts are not displayed in the UI.
Detail on fail: "Price range shows '0-100: (500 results)' globally, but does not update when category filter is applied. Should show intersection count, e.g., '0-100: (47 results for restaurants)'." or "Filter counts show stale values that don't match current filter state."
Remediation: Fetch updated filter counts from the backend whenever filters change:
// /api/listings/filter-counts
export async function GET(req) {
const category = req.nextUrl.searchParams.get('category')
const minPrice = req.nextUrl.searchParams.get('minPrice')
// ... other active filters
const baseQuery = { category, price: { gte: minPrice } }
return Response.json({
ratings: {
'5': await countListings({ ...baseQuery, rating: 5 }),
'4': await countListings({ ...baseQuery, rating: 4 }),
// ... etc
},
// ... other filter counts reflecting the current filter state
})
}
On the frontend, call this endpoint whenever active filters change and update the displayed counts.