# Filter counts update dynamically reflecting current filter intersection

- **Pattern:** `ab-001059` (`directory-search-discovery.facets-filters.filter-counts-dynamic`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001059
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001059)

## Why it matters

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.

## Severity rationale

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.

## Remediation

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.

```tsx
// /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.

## Detection

- **ID:** `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:

  ```tsx
  // /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.

## External references

- iso-25010 functional-suitability

Taxons: data-integrity, user-experience

HTML version: https://auditbuffet.com/patterns/ab-001059
