Search Hybrid

Hybrid search — keyword overlap + send_count weighting + recency.

v0 is application-side scoring (no pgvector); we score every article that hits at least one query token, then rank by: score = 1.5 * keyword_overlap + 0.4 * log(send_count+1) + recency_bonus

Graduates to pgvector once batch embeddings ship — keeping the same return shape so the frontend won't churn.

Performance (SEV 2026-06-15): storyflo.com/search chip clicks were 5-39s cold because this endpoint scans up to 500 rows (full-text contains) + Python-side scoring + a per-row RenderJob.status == done correlated subquery. Cold path on Fly measured 39.5s for q=SpaceX before this cache. Wrapped in the same search_cache.SearchCache singleton /v1/stories/search uses (5-min TTL, single-flight, stale-while-revalidate, 200-entry LRU). Key disambiguator "hybrid" keeps hybrid + stories/search entries from colliding inside the shared singleton.

Operator can bypass per-request with X-Storyflo-Search-Cache: bypass (smoke probes measuring cold latency) or globally via env SEARCH_CACHE_TTL_SEC=0.

Pool-exhaustion fix (P0 2026-06-20 12:25 PDT, Sentry TimeoutError: QueuePool limit of size 5 overflow 3 reached): deliberately no db: AsyncSession = Depends(get_db) parameter. With basic-plan PG sized at DB_POOL_SIZE=5 + MAX_OVERFLOW=3 (total 8 conns, see [[storyflo-prod-deploy-discipline-2026-06-19]]), a request- scoped Depends session would be held for the full duration of cache.get_or_compute — including for non-owners who are merely awaiting another coroutine's Future inside search_cache.py (lines 246-248). With 3+ concurrent search requests that all hit the single-flight wait path, the pool exhausts in seconds even though only one DB query is actually running. The owner opens a short-lived session inside _compute and releases it before the Future resolves; non-owners hold zero connections while waiting.

get/v1/search/hybrid

Request

  • Base URL: https://api.storyflo.com (the document declares no server; this is the origin it was published from)
  • URL: https://api.storyflo.com/v1/search/hybrid
  • Auth: none declared

Query parameters

qstring required
verticalstring nullable
limitinteger

Response

Successful Response

object required

Changes

No recorded changes to this operation across all 1 revision of this API.