Skip to Content
Housr WebappFeaturesFeature: Search

Feature: Search

Property search is a UK-only feature that queries the MySQL database directly (not the REST API). All search logic lives in src/features/search/.

Directory Structure

src/features/search/ actions/ get-properties.ts # Main property search query get-area-property-counts.ts # Area-level property count queries api/ components/ hooks/ types/ utils/

How Search Works

Direct MySQL Queries

Search bypasses the REST API and queries the houses table directly via src/lib/db.ts. This is a server action (runs server-side only).

The main query in get-properties.ts:

  1. Builds a WHERE clause from search params (city, bedrooms, bathrooms, map bounds)
  2. JOINs with bills_prices_selectable (from the bills database) to calculate total price including bills
  3. JOINs with bills_included_agents to account for agents that include certain bills
  4. Applies HAVING conditions for price range filters (since total price is a calculated column)
  5. Orders results based on sort preference
  6. Paginates with LIMIT/OFFSET

Search Parameters

type SearchParams = { city: string; page?: string; minBedrooms?: string; maxBedrooms?: string; minBathrooms?: string; maxBathrooms?: string; minPrice?: string; maxPrice?: string; sort?: string; // "recommended" | "price-low" | "price-high" | "newest" | "oldest" limit?: number; // default: 24 bounds?: { // map viewport bounds west: number; south: number; east: number; north: number; }; };

Sort Options

Sort ValueSQL Order
recommended (default)tier ASC, house_priority DESC, RAND()
price-lowtotal_price ASC
price-hightotal_price DESC
newestcreated_at DESC
oldestcreated_at ASC

Total Price Calculation

Total price = rent per week + bills cost. The bills cost is calculated in SQL by joining with the bills pricing table and accounting for which utilities the agent already includes:

  • If agent includes both electric and gas, energy cost = 0
  • If agent includes only gas, energy cost = electric only
  • If agent includes only electric, energy cost = total energy minus electric
  • Water and WiFi are added unless the agent includes them

Caching Strategy

Two unstable_cache wrappers handle caching:

  • getCachedProperties(city) — Used when no filters are applied. Cache key: ["properties"], revalidation: 3 hours.
  • getCachedPropertiesWithFilters(...) — Used when filters are present. Cache key: ["properties-filtered"], revalidation: 3 hours.

Both share the "properties" cache tag for on-demand revalidation.

Map-bounds queries are never cached — when bounds is provided, fetchPropertiesFromDb() is called directly.

Area Property Counts

get-area-property-counts.ts provides getAreaPropertyCounts(city, areas):

  • Takes a city name and an array of areas with bounding boxes
  • Runs parallel COUNT(*) queries for each area
  • Results are cached via unstable_cache with the same 3-hour TTL and "properties" tag

Response Shape

type PropertiesResponse = { houses: House[]; totalHouses: number; totalPages: number; currentPage: number; filters: Filters; };

Each House object includes id, title, address, city, images (parsed from JSON), price_pw, total_price (rent + bills), bedrooms, bathrooms, lat, lng, tier, and timestamps.

Last updated on