Wire catalog cascade into GET /library/ via fuzzySearchLibrary (BS#972)#973
Merged
Conversation
Push the catalog-track-search cascade (CTA -> LML /lookup) down from searchLibrary into searchLibraryBothMode so both /library/search and /library/ (the catalog-facing route dj-site + iOS call) traverse it. Catalog responses now carry `matched_via` for cascade-sourced hits. Refactor: extract searchLibraryByCTARaw + searchLibraryByTrackRaw that return TaggedLibraryViewEntry[] (LibraryArtistViewEntry + optional matched_via), keeping the enriched wrappers for request-line callers. The LRU cache for Track 2 moves to the raw layer so both call sites share it. fuzzySearchLibrary's both-mode branch now returns the tagged shape, which serializeLibraryArtistViewEntry preserves through to the AlbumSearchResult wire format. Behavior is byte-identical when CATALOG_TRACK_SEARCH_CTA_ENABLED and CATALOG_TRACK_SEARCH_DISCOGS_ENABLED are both off (the default), so this is the cascade wiring alone -- no behavior change without a flag flip.
This was referenced May 21, 2026
jakebromberg
added a commit
that referenced
this pull request
May 21, 2026
`searchLibraryQueryEndpoint` runs its own field-aware ILIKE primary path, so the Track 1 (CTA) + Track 2 (LML `/lookup`) cascade owned by `searchLibraryBothMode` was unreachable from the modern Card Catalog. That left modern dj-site without `matched_via` chips even after BS#972/#973 wired the cascade onto the classic-experience `/library/` route. When the primary query returns no rows AND the parsed query is a single bareword (no `artist:`/`album:`/`label:` qualifier, no quoted exact, no NOT), fall through to `searchLibraryByCTARaw` + `searchLibraryByTrackRaw` under the existing `CATALOG_TRACK_SEARCH_CTA_ENABLED` / `CATALOG_TRACK_SEARCH_DISCOGS_ENABLED` flags. Apply `genre` / `format` / `on_streaming` filters and the requested `sort` + `order` over the bounded fallback list in-memory before returning. Cascade fallback is single-page (`page > 0` stays empty) so the client doesn't keep scrolling into a bounded list. Both flags default to `false`, so when the operator hasn't opted in the function path is byte-identical to today. Closes #977
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The catalog-track-search cascade (
CTA→LML /lookup) lived only insidesearchLibrary, whichGET /library/searchcalls. Catalog clients (dj-siteuseSearchCatalogQuery, iOSsearchLibrary) hitGET /library/instead, which callsfuzzySearchLibraryand tops out at tsvector + trigram. FlippingCATALOG_TRACK_SEARCH_CTA_ENABLED/CATALOG_TRACK_SEARCH_DISCOGS_ENABLEDtotruein prod had no observable effect on the catalog page or the iOS app, andmatched_viawas always absent fromAlbumSearchResult[]responses — making three already-merged client surfaces (MatchedTrackBadge, modernMatchedTrackChips, classicMatchedTrackChips) dormant.This PR pushes the cascade down one layer:
searchLibraryByCTARawandsearchLibraryByTrackRaware new, returningTaggedLibraryViewEntry[](i.e.LibraryArtistViewEntryplus an optionalmatched_via). The existingsearchLibraryByCTA/searchLibraryByTrackare kept as enriched-shape wrappers so request-line callers (artistPlusAlbum,swappedInterpretation,songAsArtist,trackOnCompilation) are unaffected.searchLibraryBothModenow owns the full tsvector → trigram → CTA → LML cascade and returnsTaggedLibraryViewEntry[].fuzzySearchLibrary's both-mode branch (the dj-site shape, whereartist_name === album_title) routes throughsearchLibraryBothModeand inherits the cascade. Split-field queries stay on the legacy fuzzy path — out of scope.serializeLibraryArtistViewEntrycarriesmatched_viathrough to the wire (LibraryArtistViewResponsenow declares the optional field).Both feature flags default off, so a deployment that hasn't opted in is byte-identical to before. This is wiring only — no behavior change without a flag flip.
Acceptance criteria (from #972)
GET /library/?artist_name=q&album_title=q&n=10withCATALOG_TRACK_SEARCH_CTA_ENABLED=truereturnsmatched_via: [{ title, source: "cta" }, …]on tsvector+trigram 0-hits (new unit + integration tests assert this).CATALOG_TRACK_SEARCH_DISCOGS_ENABLED=truereturnsmatched_via: [{ title, source: "discogs…" }]on CTA misses (new integration test against the existing Track 2 fixtures hitsvi scose poise→ Confield)./library/responses are byte-identical to today (cascade no-op verified by the existinglibrary.spec.js+ ranking suites).matched_via(unit + integration test).searchForAlbumrequest shape (code_letterslookup,on_streaming-only browse, missing-params 400 unchanged)./library/?artist_name=vi+scose+poise&album_title=vi+scose+poisereturns the Confield row withmatched_via.sourcematchingdiscogs*(uses the existing Track 2 mock-LML fixture; no live LML required in CI).LIBRARY_SEARCH_ENRICHMENT_BUDGET_MSartwork race; the cascade adds at most one CTA SQL + one LML/lookupper 0-hit (unchanged from/library/search).Test plan
npm run test:unit(1991 passed; newfuzzySearchLibrary Both-mode cascadeblock + existingsearchLibrary cascadeblock both green)npm run typecheck(clean across all workspaces)npm run lint(0 errors)npm run format:check(clean)npm run build(all workspaces)npm run test:integration(newGET /library cascade — catalog route serves matched_via (BS#972)block plus existinglibrary.spec.jsregressions — exercised in CI viaci:testmock)Related
WXYC/wiki/plans/catalog-track-search.md§4.1, §5.1.wxyc-shared/api.yaml:1072-1079(AlbumSearchResult.matched_via).