Improve the search experience and reduce “0 results” (query relaxation + ranking adjustment)
The data and search logic examples in this article are generalized from client cases.
For clarity of explanation, they are reproduced using Airbnb public data. They differ from actual customer data and system configurations.
Summary
| Perspective | Content |
|---|---|
| Issue | Because of exact match search, even slightly off conditions easily led to “0 results,” causing users to repeat searches with small changes and eventually churn. |
| Approach | Introduced a mechanism that scores search candidates and displays them in order of “closest to the conditions.” Applied score tuning (function_score + gauss) so that scores decrease smoothly even when distance or match level slightly differ. |
| Operation | Continuously monitored relaxation steps / weights / thresholds via a dashboard, and kept validating and tuning side effects through A/B testing. |
| Results | Significantly reduced “0-result searches,” decreased the number of re-searches, and increased session duration. By improving acceptance of alternative suggestions, the search experience became smoother without disruption. |
| Impact | Delivered a search experience with a sense of substance, contributing to improved inventory utilization and booking rates. Also secured room for future ML-ization / expansion to vector search. |
Background
Business context
In accommodation booking search, users can set many conditions: period, number of guests, price range, location, amenities, instant booking availability, and more.
However, when conditions were strict, there were a certain number of cases with “0 matching results,” and users kept re-searching while finely tweaking conditions. As a result, churn rates were high.
Current issues
- Search conditions were exact-match based, so even slightly loosening a condition easily led to 0 results.
- It was not possible to naturally present “candidates with slightly relaxed conditions” or “alternative suggestions (nearby dates / surrounding areas),” which fragmented the experience.
Business and non-functional requirements
- Continuously reduce the occurrence of zero-result searches and increase the “substance” of search results.
- Keep node load and costs under control while maintaining accurate price and inventory information.
- Regularly verify that search improvements do not cause side effects such as churn or complaints.
Approach
Basic policy
Reduce zero results without deviating from user intent.
To that end, combine a multi-layer logic that gradually relaxes search conditions, scoring that preserves closeness, and visibility into the reasons.
Logic design
-
Multi-stage relaxation steps
Exact match → slight date relaxation → area expansion → price range expansion → alternatives (popularity, rating, etc.).
If no results appear in an upper step, proceed to the next, and at each step keep metadata on “what was relaxed and by how much.” -
Scoring that preserves closeness
Use Elasticsearchscript_scoreandgaussto weight distance, price difference, proximity to desired dates, etc.
Rank results so that “candidates close to the conditions” appear naturally at the top. -
Visibility of reasons (explainability)
Clearly show in the UI what was relaxed, such as “dates relaxed by ±1 day” or “price expanded by +10%.”
Allow users to adjust the relaxation width themselves via toggles or sliders. -
Balancing performance and operations
Separate queries by relaxation step while designing for cache reuse.
Continuously optimize query patterns and ES load so the system can withstand bursts from map moves and filter changes.
Validation phase
Purpose of validation
Confirm that changes to the search logic “reduce zero results while still presenting results aligned with user intent.”
Assuming inventory and price accuracy, we evaluated three aspects: UX, performance, and side effects.
Validation perspectives
-
Effectiveness
Measured how much each relaxation step avoided “zero-result searches.”
Tracked hit rates by relaxation type and click-through rates on alternatives, and analyzed whether users were naturally accepting alternative suggestions. -
Performance
Measured response times for frequently used operations (moving and zooming the map, changing dates, etc.).
Visualized query structure, cache hit rate, and Elasticsearch node load, and stabilized response times within a range that does not harm UX. -
Side effects
Monitored whether “disappointing results” were increasing due to over-relaxation.
Compared metrics such as churn rate and number of inquiries via A/B testing, and adjusted the balance of relaxation width and score design.
Improvement cycle
- Continuously monitored key metrics (zero-result rate, click-through rate, latency, load) on a dashboard.
- Reviewed relaxation logic and score functions monthly, and fine-tuned thresholds and weights.
- Reflected validation results in the next release plan and continuously improved the search experience.
Results
Quantitative results
- Significantly reduced the zero-result search rate, achieving a state where search results almost always return some candidates.
- Introduction of relaxation steps reduced the number of re-searches and increased time on site per session.
- From the distribution of hit rates by relaxation type, confirmed that fine-tuning dates and areas was particularly effective.
- Response speed on search result pages also stabilized, maintaining throughput even under increased load.
Qualitative results
- Resolved the frustration of “getting 0 results as soon as I slightly change conditions,” improving continuity of the search experience.
- By clearly stating the reasons for relaxing conditions, secured user acceptance and trust.
- As the search felt more substantial, actions such as bookmarking and revisits increased.
- On the business side, improvements spread to inventory utilization and booking rates, increasing the value of the search funnel.
Reflections and learnings
- Confirmed the importance of not just naively reducing zero results, but measuring “which relaxations are acceptable” with data.
- Re-recognized that scoring design and UX expression (how reasons are shown) are two sides of the same coin.
Challenges and ingenuity
Balancing performance and flexibility (backend × frontend collaboration)
A structure that executes relaxation queries step by step is flexible, but the increased number of operations tends to increase ES node load and network round trips.
In addition to backend optimization (query reuse, short-term caching), the frontend also reduced unnecessary requests with the following techniques:
-
Debouncing / throttling
- For continuous operations such as “moving and zooming the map” and “adjusting sliders,” debounce with a short delay and control send frequency with throttling while dragging.
- Avoid rapid-fire intermediate states and wait for the user’s intended operation to “settle” before firing a search.
-
Collapsing duplicate queries (coalescing)
- Reuse results for the same normalized query (e.g., rounded viewport, main conditions excluding sort/paging).
- Bucket or grid viewports and conditions to consolidate nearly identical requests.
Understanding and adjusting side effects
In the early stages, there were cases where stronger relaxation led to increased churn or complaints.
We regularly analyzed A/B test results and iteratively updated the design while measuring “how far we can relax before dissatisfaction appears.”
Establishing a system where the whole team made decisions based on data led to stable long-term improvements.
References
Elasticsearch search queries
gauss- Smoothly subtracts points as you move away from the center. Ideal for “closeness” in distance, dates, and numeric values.
script_score- Express business rules as formulas (e.g., difference from target price, bonus for number of reviews / rating, penalty for max guests).
function_score- A framework for giving “additional scores” to documents hit by a query. You can combine multiple functions to create a “total score.”
- Use
weightandscore_modeto balance “distance × price × reviews.”
Example
{
"function_score": {
"query": { "bool": { "filter": [ /* basic conditions */ ] } },
"functions": [
{
"gauss": {
"location": {
"origin": { "lat": 35.7058, "lon": 139.7514 },
"scale": "1500m",
"offset": "200m",
"decay": 0.7
}
},
"weight": 2.0
},
{
"script_score": {
"script": {
"source": """
double p = (doc.containsKey('price') && !doc['price'].empty) ? doc['price'].value : 0;
double diff = Math.abs(p - params.target);
// -0.2 points for every ±10,000 yen (adjustable)
return Math.max(0, 1.0 - (diff / 10000.0) * 0.2);
""",
"params": { "target": 15000 }
}
},
"weight": 1.0
},
{
"script_score": {
"script": {
"source": """
// Review credibility: combine count and rating to 0~1
double n = (doc.containsKey('number_of_reviews') && !doc['number_of_reviews'].empty) ? doc['number_of_reviews'].value : 0;
double r = (doc.containsKey('review_scores_rating') && !doc['review_scores_rating'].empty) ? doc['review_scores_rating'].value : 0;
// Assume count caps at 100, rating out of 100 points
double nScore = Math.min(n, 100) / 100.0;
double rScore = r / 100.0;
return 0.5 * nScore + 0.5 * rScore; // coefficients are adjustable
"""
}
},
"weight": 0.8
}
],
"score_mode": "sum",
"boost_mode": "sum"
}
}
weight: Relative importance of each factor (e.g., distance 2.0, price 1.0, reviews 0.8).score_mode: How to combine functions (sumis intuitive,multiplymakes the whole score drop if one factor is low).boost_mode: How to combine with the base_score(text match). If you want to emphasize text, considermultiplyorsum+boost.
Performance Optimization
Enhanced system response speed and stability through database and delivery route optimization.
Developer Productivity & Quality Automation
Maintained continuous development velocity through quality assurance automation and build pipeline improvements.
Enhanced User Experience
Improved usability and reliability from the user's perspective, including search experiences and reservation systems.