Problem / Opportunity
Today when patrons search on openlibrary.org/search, the right-sidebar "Zoom In" section displays search facets (Language, Author, Subject, etc.) loaded asynchronously via /partials/SearchFacets.json (introduced in PR #9397 by @jimchamp). The current endpoint returns a full HTML partial for the entire sidebar — it does not expose individual facet field values as a JSON API.
As part of the new Search UX work (PR #12949 by @lokesh), individual facet fields are becoming interactive OlSelectPopover-based droppers. When a patron opens one of these droppers after performing a search, the options shown should reflect only the values relevant to that search — not every value known to the system.
Example: Searching "Lord of the Rings" and opening the Language dropper should show:
English 665
German 32
Spanish 18
French 15
Chinese 11
...not all 25+ languages in the system, many of which yield zero results for this query.
Why this matters: Showing irrelevant facet values steers patrons into dead ends (zero-result filtered pages) and degrades the search experience. Search-context-aware facets are a UX best practice used by major library and e-commerce platforms. With the new Search UX landing in #12949, this is the right moment to add the backing API.
Current UI for reference:
Success: When a patron opens any facet dropper with an active search query, the options shown are limited to values with ≥1 matching result, ordered by count descending.
Proposal
Add a new Facet Values JSON API endpoint — e.g., GET /search/facets.json — that accepts a Solr field name plus the current search parameters, and returns an ordered list of {value, count} pairs scoped to that search context.
Proposed behavior:
- With an active search query: Query Solr with
facet=true&facet.field=<field>&rows=0 alongside the current search params; return only {value, count} pairs where count > 0, ordered by count descending.
- Without an active search (e.g., homepage, empty query): Fall back to a sensible default — e.g., popular values, browser-locale language prioritized, etc.
- Respects existing SFW filtering.
Example response for GET /search/facets.json?field=language&q=lord+of+the+rings:
[
{"value": "English", "count": 665},
{"value": "German", "count": 32},
{"value": "Spanish", "count": 18}
]
Implementation Details (for maintainers)
Summary
We'd continue to use search.json for search. There would be a second Facets JSON API (that defines responses/values for each facet type that gets triggered on-click of each Search UX Facet to populate its relevant, context-aware results)
Related files
Refer to this map of common Endpoints:
Requirements Checklist
Checklist of requirements that need to be satisfied in order for this issue to be closed:
Stakeholders
Instructions for Contributors
Problem / Opportunity
Today when patrons search on openlibrary.org/search, the right-sidebar "Zoom In" section displays search facets (Language, Author, Subject, etc.) loaded asynchronously via
/partials/SearchFacets.json(introduced in PR #9397 by @jimchamp). The current endpoint returns a full HTML partial for the entire sidebar — it does not expose individual facet field values as a JSON API.As part of the new Search UX work (PR #12949 by @lokesh), individual facet fields are becoming interactive
OlSelectPopover-based droppers. When a patron opens one of these droppers after performing a search, the options shown should reflect only the values relevant to that search — not every value known to the system.Example: Searching "Lord of the Rings" and opening the Language dropper should show:
...not all 25+ languages in the system, many of which yield zero results for this query.
Why this matters: Showing irrelevant facet values steers patrons into dead ends (zero-result filtered pages) and degrades the search experience. Search-context-aware facets are a UX best practice used by major library and e-commerce platforms. With the new Search UX landing in #12949, this is the right moment to add the backing API.
Current UI for reference:
Success: When a patron opens any facet dropper with an active search query, the options shown are limited to values with ≥1 matching result, ordered by count descending.
Proposal
Add a new Facet Values JSON API endpoint — e.g.,
GET /search/facets.json— that accepts a Solr field name plus the current search parameters, and returns an ordered list of{value, count}pairs scoped to that search context.Proposed behavior:
facet=true&facet.field=<field>&rows=0alongside the current search params; return only{value, count}pairs wherecount > 0, ordered by count descending.Example response for
GET /search/facets.json?field=language&q=lord+of+the+rings:[ {"value": "English", "count": 665}, {"value": "German", "count": 32}, {"value": "Spanish", "count": 18} ]Implementation Details (for maintainers)
Summary
We'd continue to use search.json for search. There would be a second Facets JSON API (that defines responses/values for each facet type that gets triggered on-click of each Search UX Facet to populate its relevant, context-aware results)
Related files
Refer to this map of common Endpoints:
openlibrary/fastapi/partials.py— Add new endpoint here alongside the existingGET /partials/SearchFacets.json(L33–51)openlibrary/plugins/openlibrary/partials.py—SearchFacetsPartial.generate_async()(L324–374) for Solr query reference; the new endpoint can reuse the same Solr query plumbing withrows=0andfacet.field=<field>openlibrary/templates/search/work_search_facets.html— current sidebar template showing which facet fields are supportedopenlibrary/plugins/openlibrary/js/search.js— current JS handler for/partials/SearchFacets.json(L67–130); new dropper components will call the new endpoint insteadopenlibrary/components/lit/OlSelectPopover.js— Lit dropper component that will call the new endpoint on openRequirements Checklist
Checklist of requirements that need to be satisfied in order for this issue to be closed:
GET /search/facets.json) in FastAPI acceptingfield,q, and active facet filters as query paramsfacet=true&facet.field=<field>&rows=0alongside the current search paramscount > 0, ordered by count descending, as[{value, count}]JSONOlSelectPopover/OlFacetSelectcomponents wire up to call the new endpoint when opening a dropper with an active searchStakeholders
OlSelectPopover/OlFacetSelectand PR Shared reading preference + author-page filter bar (results-toolbar Phase 0–1) #12949; integration point for new APIInstructions for Contributors