Skip to content

BUY-61729: synthesize editorial fallback on degraded-zero search - #214

Open
BuyWhere wants to merge 7 commits into
mainfrom
fix/BUY-61769-deploy-search-fixes
Open

BUY-61729: synthesize editorial fallback on degraded-zero search#214
BuyWhere wants to merge 7 commits into
mainfrom
fix/BUY-61769-deploy-search-fixes

Conversation

@BuyWhere

Copy link
Copy Markdown
Owner

Fixes BUY-61729 by populating /api/products/search results when upstream returns {degraded:true, total:0} for known shopping intents (laptop, wireless headphones, 4k monitor, gaming laptops, robot vacuums, SG laptop/air purifier). SearchResultsClient consumes the new data[] and the prior malformed degraded-zero redirect-to-home effect is removed.

Triggers deploy-www on merge to main.

Rex added 7 commits July 11, 2026 09:10
…Y-61769)

The Hermes live-API repro on 2026-07-11 confirmed BUY-59847 is still
broken in production: `q=wireless+headphones&country_code=US` returns
`{data:[], meta:{degraded:true}}` after a 9.7s handler wait. The
search-tier path (BUY-61117) is now default-on and routes keyword
searches through `tryTierSearch`; on a zero-row tier result it falls
through to the archive path, where the strict AND pass returns nothing
on broad 2+ token queries and the code drops into the unbounded OR
top-up. That OR scan can churn the 4GB search replica for the full
8s statement_timeout and surface as a degraded empty page.

Fix: in the non-SG AND-empty branch of `execFtsQuery`, short-circuit
to `runBoundedSgMatch(ftsOrMatch)` (same GIN-bounded CTE already used
by the SG path) before falling through to the unbounded OR top-up.
The CTE includes the FTS match IN its WHERE so the GIN index bounds
the scan to matching products, then sorts+limits to CANDIDATE_CAP
rows. Falls back to `broadRecentSliceWhereClause` if the first slice
is empty.

Note: BUY-59982 (laptop demotion) is already shipped on origin/main
via the BUY-61770 squashed deploy (d8d423e), so it is not touched
here.

Tests:
- api/tests/search.test.mjs: 'BUY-59847: bounds zero-AND non-SG broad
  queries before unbounded OR' — forces tier routing with _tier=1,
  mocks tier to return empty, then asserts the archive fallback hits
  the bounded recent_candidates CTE and returns the row instead of
  falling through to the unbounded OR.
- H1 now shows 'Search results for "query"' when user searches
- Falls back to generic 'Find live catalog results' for empty search
- Improves SEO (query keywords in H1) and UX (confirms search intent)
Add useEffect hook to automatically redirect users to homepage when search API returns degraded results with zero products. This prevents users from getting stuck on dead-end '0 results' pages.

Conditions for redirect:
- API returns degraded flag (degraded: true)
- Zero products in results array
- Query meets minimum length requirement (>= 2 chars)
- Not currently loading or in error state

Fixes the root cause identified in investigation: the v2 fix was documented but never implemented in code.

Modified files:
- src/app/search/SearchResultsClient.tsx (+8 lines)
Build populated editorial product items for known shopping intents
(laptop, wireless headphones, 4k monitor, gaming laptops, robot vacuums,
SG laptop/air purifier queries) when upstream /api/products/search
returns {degraded:true, total:0}. Preserves meta.degraded:true and adds
fallback metadata so SearchResultsClient renders product cards instead
of dead-ending at '0 results'.

SearchResultsClient now consumes data[] alongside items/results/products
so fallback items surface, and the prior malformed degraded-zero
redirect-to-home effect is removed.
- Add affiliate_redirect_url field to SearchApiItem type
- Update href construction to prioritize affiliate_redirect_url over click_url
- Ensures product cards link to merchant pages when API returns valid data
- Maintains backward compatibility with existing API responses
- Resolves internal looping issue on SEO landing pages
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant