Skip to main content

Listing Search Pipeline

End-to-end flow from request to response.

ListingRequest

1. Parse filters (city, dates, guests, amenities, strategy)

2. Filter listings from DB (dynamic SQL, joins only when needed)

3. Concurrent fetch: availability + offers

4. Process each listing in PARALLEL:
├─ Build property candidates (property + live availability)
├─ Cap guest requirements to listing's own capacity
├─ Invoke picking strategy → selected properties + quantities
├─ Calculate price (nightly rates + extra guest charges + tax)
├─ Resolve offers (promotion → bank offer → cancellation plans)
└─ Return ProcessedListing

5. Filter: keep only listings with price > 0

6. Split: "fits alone" vs "needs combining"

7. Sort single-fit by score (best first)

8. If allowCombination and few single results:
└─ Build multi-listing groups via MultiListingCombinationService

9. Map to response (ListingSearchResponse)

Step-by-Step Detail

1. Parse Filters

ListingFilter.fromRawParams() extracts structured filters from the generic rawParams list:

  • Location: city, state, country, locality
  • Dates: checkInDate, checkOutDate → StayDates (nights calculated via ChronoUnit.DAYS.between)
  • Guests: adults, children, infants, pets
  • Property: propertyType, brand, amenities, tags, petFriendly, stressProperties
  • Price: minPrice, maxPrice
  • Search: searchQuery (text search on title/name)
  • Strategy: strategy (GREEDY, OPTIMAL, CHEAPEST_FIRST, HIGHEST_RATED, MIN_WASTE)
  • Combination: allowCombination, maxListingsInGroup, proximityMeters

2. Filter Listings (Dynamic SQL)

The ListingSearchRepository builds SQL dynamically — joins are only added when needed:

  • Listing-only filters (city, state, locality, searchQuery on title): no property join
  • Property-level filters (brand, type, petFriendly, price, guests): join listing_property + properties
  • Aggregate filters (minBedrooms, minAdults): added as HAVING clauses

3. Concurrent Data Fetch

Two operations run simultaneously via CompletableFuture:

OperationSourceReturns
AvailabilityChannelRateDao → PostgreSQLMap of propertyId → nightly rates + inventory
OffersOfferCacheService → in-memory cacheMap of listingId → promotions, bank offers, cancellation plans

4. Process Each Listing

Parallel via listings.parallelStream(). For each listing:

  1. Build PropertyCandidate objects (Property + live availability)
  2. If dates present, availability data is merged; unavailable properties get filtered
  3. Cap requirements to listing's own capacity (for combinations: partial fulfillment is OK)
  4. Invoke the picking strategy → returns selected properties with quantities
  5. Calculate total price and average rating

5-7. Filter, Split, Sort

  • Listings with selectedPrice == 0 are discarded
  • "Fits alone" = listing's total capacity covers the full guest requirement
  • Sorted by listingScore (lower is better): considers price + rating

8. Combinations (Optional)

Only triggered when allowCombination=true and single results are fewer than minSingleResults (default 3). See Combinations for details.