Skip to content

Prototype: "Nearby Books" carousel — DDC shelf-adjacency browsing (Lit component) #13159

Closed

Description

@mekarpeles

Summary

Physical library patrons browse by "shelf adjacency" — pull one book, glance left/right, find something related by call number. We'd like a small prototype: a "Nearby Books" carousel, a Lit web component that, given an edition, shows other available editions with numerically/lexically close Dewey Decimal Classification (DDC) numbers — the digital analog of scanning the shelf. Ideally it also filters by the edition's language, so you're not shown a book that's physically adjacent but unreadable to you.

This is explicitly a prototype: small, exploratory, not a production epic.

Feasibility findings

1. DDC/LCC exist in Solr today, but as string fields, aggregated per-work (not per-edition)

  • Schema: conf/solr/conf/managed-schema.xml:175-176
    <field name="ddc" type="string" multiValued="true" />
    <field name="ddc_sort" type="string" stored="false" /> <!-- Need non-multiValued for sorting -->
    lcc is the same shape (managed-schema.xml:173).
  • Populated in openlibrary/solr/updater/work.py:483-490 (ddc/ddc_sort) and :471-478 (lcc/lcc_sort). Both are computed by aggregating across all editions of a work (get_edition_ddcs(), work.py:691-705) and normalizing via openlibrary/utils/ddc.py::normalize_ddc() (zero-pads the integer part so lexical string sort approximates numeric order) and choose_sorting_ddc() picks one representative value for ddc_sort.
  • Implication: DDC/LCC are per-work Solr fields, not per-edition. There's an open issue (Move LC classification and DDC from editions to work records #897, filed by @cdrini, still open) proposing to formally move classification from editions to works specifically because it "will also enable better UX suggestions for related works, (akin to shelf browsing)" — i.e., this prototype's premise already has a named precedent/blessing, but also means a "nearby edition" query is really a "nearby work" query today.

2. Nearest-neighbor is NOT a native numeric query today — but there's working precedent for a lexical-range approximation

  • ddc/ddc_sort/lcc/lcc_sort are Solr type="string", not numeric/float. There is no sort=abs(sub(ddc,813.54)) asc-style function query available without a schema change (new numeric field + reindex) — real risk, not hidden.
  • However, Library Explorer already solves this exact problem with lexical range queries, and it's running in production:
    • openlibrary/components/LibraryExplorer/utils.js:69 — decrementStringSolr(string, caseSensitive, numeric) computes the lexically-previous string value.
    • openlibrary/components/LibraryExplorer/components/BookRoom.vue:224-231 uses it to find "position nearest a target classification": ${field}_sort:[* TO ${predecessor}] counts items before a target, to scroll a shelf to the right offset.
    • openlibrary/components/LibraryExplorer/components/OLCarousel.vue:200-254 fetches search.json with fields: 'key,title,author_name,cover_i,ddc,lcc,...', sorted by ddc_sort/lcc_sort (LibraryToolbar.vue:486-487), paginated.
  • Recommendation: reuse this pattern rather than inventing one — two range queries against ddc_sort ([* TO X] desc-limited and [X TO *] asc-limited) to get N works just "before" and "after" a target DDC. Because of the zero-padding in normalize_ddc(), this reproduces correct numeric ordering for practical purposes. No schema change needed for a prototype.

3. Language filtering — straightforward

  • language field: managed-schema.xml:158, type="string" multiValued="true", also per-work (aggregated in work.py:623-624: {lang for ed in self._solr_editions for lang in ed.language}).
  • A simple fq=language:eng-style filter works today, no schema risk. Same per-work caveat as DDC applies: filtering matches works that have any edition in that language, not necessarily the specific edition being viewed.

Component approach

  • Correction to initial framing: Lit is not unprecedented in this codebase. openlibrary/components/lit/ has ~17 existing Lit components (OLButton.js, OlDialog.js, OlPopover.js, OlToast.js, OpenLibraryOTP.js, etc.), built via vite-lit.config.mjs into ol-components.js. So the tooling/build pattern already exists — no new build infra needed.
  • What actually would be new: none of the existing Lit components query Solr and render a list of book cards. That pattern — fetch results, paginate, render book covers/metadata — exists today only in Vue (LibraryExplorer/components/OLCarousel.vue) and in the server-rendered template path (openlibrary/templates/books/custom_carousel.html + custom_carousel_card.html, mounted via .carousel--progressively-enhanced + data-config JSON blob). This prototype would be the first Lit component that is a Solr-backed book carousel — a smaller novelty than "first Lit component ever," but still a first for this specific pattern, worth flagging as a scope note.
  • Design decision for Mek/Lokesh: render inside the existing carousel shell (custom_carousel.html's .carousel-section / .carousel-section-header markup and CSS, card partial reused) for visual consistency with other book-page carousels — vs. a fully independent Lit web component with its own markup. Given the ask specifically frames this as "a Lit web component," leaning toward independent — but flagging the visual-consistency tradeoff explicitly.

Suggested scope for the prototype

In scope:

  • Given an edition key: resolve its work's DDC (ddc_sort) and language.
  • Query Solr for N works with ddc_sort immediately below + N immediately above the target, filtered by matching language, excluding the current work — reusing the Library Explorer range-query pattern (no schema change).
  • Render as a horizontal-scrolling Lit component, one card per result (cover, title, author).
  • Works with no DDC: skip the component entirely (no placeholder, no fallback classification).

Out of scope for v1:

Open questions for Mek/Lokesh

  1. Is showing "nearby works" (not literally nearby editions, since DDC/language are per-work in Solr today) acceptable for a prototype, or does this need edition-level data first (blocked on Move LC classification and DDC from editions to work records #897)?
  2. Is the lexical string range-query approach (reusing Library Explorer's decrementStringSolr/_sort pattern) sufficient, or is a numeric DDC field + true nearest-neighbor sort worth exploring as a v2 (schema change + reindex, non-trivial infra cost)?
  3. Should this reuse the existing custom_carousel.html shell/CSS for visual consistency, or be a fully independent Lit component?
  4. Where does it render — work page, edition page, or both?
  5. Should LCC be a fallback classification for editions lacking DDC, or explicitly deferred to a later pass?

Related/sibling prototype: #13158 (Genre Explorer) — both are Library-Explorer-adjacent shelf-browsing ideas, though this one is scoped to a single book-page carousel rather than a full explorer UI.

Activity

  1. added
    Type: Feature RequestIssue describes a feature or enhancement we'd like to implement. [managed]
    Theme: CSSIssues related to CSS stylesheets. [managed]
    Team: Front-endIssues belonging to the Front-end team [experimental tag]
    Lead: @lokeshIssues pertaining to front-end design system [css, js, components]
    Needs: Staff DecisionIssues that are blocked on a staff member's decision
    on Jul 15, 2026
  2. added
    Needs: TriageThis issue needs triage. The team needs to decide who should own it, what to do, by when. [managed]
    Needs: BreakdownThis big issue needs a checklist or subissues to describe a breakdown of work. [managed]
    Theme: SearchIssues related to search UI and backend. [managed]
    Module: SolrIssues related to the configuration or use of the Solr subsystem. [managed]
    Module: JavaScriptIssues related to the JavaScript functionality. [managed]
    Affects: UIIssues with the web site's user interface. [managed]
    on Jul 15, 2026
  3. openlibrary-bot commented on Jul 15, 2026

    @openlibrary-bot
    Collaborator

    ⚠️ Contributors, this issue will be ready to work on once:

    • Triage — maintainers will add Priority: *
    • Needs: Designs — no mockup exists yet for the carousel card/shell; visual direction should come from @lokesh
    • Needs: Staff Decision — nearby-works-vs-editions scope, query approach (v1 lexical range vs. v2 numeric field), carousel shell to reuse, and page placement (see Open questions below)
    • Needs: Breakdown — no task checklist present yet; a suggested one is below

    Context

    Verified the issue's own research against the current tree (commit 8404a0c) — the file/line citations are accurate. A few additional findings:


    Issue Assessment

    Open questions

    • "Is the lexical string range-query approach... sufficient?" (Answer: confirmed working in production today via BookRoom.vue:224-231 / decrementStringSolr — no schema change needed for a v1, per the issue's own finding.)
    • Is showing nearby works (not editions) acceptable for a prototype, or does this need to block on Move LC classification and DDC from editions to work records #897?
    • Reuse custom_carousel.html's shell, the newly-found <ol-carousel> Lit shell, or a fully bespoke component?
    • Where does it render — work page, edition page, or both — and how does it relate to the existing RelatedWorksCarousel on type/edition/view.html?
    • Should Book Page: Recommend Available Alternatives #12743 be closed/merged into this issue, or tracked separately?

    Suggested breakdown

    • Data layer: given an edition key, resolve its work's ddc_sort + language (work or search.json lookup)
    • Solr query: two ddc_sort range queries (before/after target) filtered by language, excluding the current work — port the decrementStringSolr pattern out of LibraryExplorer/utils.js (or import it, if scoping allows a shared util)
    • Component: decide on shell (see open questions) and build card rendering for fetched results
    • Mount point: wire into the chosen page template(s)
    • No-DDC case: component renders nothing (per issue's stated scope)

    None of these are standalone Good First Issue candidates — the shell/placement decisions block all of them.


    🔔 Stakeholders

    @cdrini — owns the Solr DDC/LCC aggregation logic (work.py, utils/ddc.py) and the Library Explorer range-query pattern this prototype reuses; also the filer of #897, which this issue's scope decision (open question #1) depends on.

    Full triage checklist
    • Triage — labels applied per current state (see above)
    • Problem / opportunity — problem and audience clear (shelf-adjacency browsing); no measurable-impact/success metric stated, but explicitly framed as a prototype/spike rather than a scored feature
    • Scope — well-scoped; explicit in/out-of-scope lists
    • Justification — no measurable impact or success definition given (acceptable for a prototype, but flagged)
    • Success & testing criteria — not defined; "prototype" framing leaves "done" undefined
    • Proposal & breakdown — strong technical proposal; no task checklist (suggested one above)
    • If bug: environment & repro steps — n/a, feature request
    • Risks, concerns, open questions — thorough open-questions section already present in the issue body
    • Richy context — code, commits, and related issues reviewed; no exact duplicate, but Book Page: Recommend Available Alternatives #12743 overlaps significantly
    • References — permalinks and prior art cited throughout

    Note

    This comment was automatically generated by Richy, Open Library's issue enrichment assistant, on behalf of @mekarpeles. This is NOT a sign-off. This issue needs lead review before work begins.

  4. added this to the Sprint 2026-H2 milestone on Jul 15, 2026
  5. 10 remaining items

  6. Sravan1011 commented on Sep 3, 2026

    @Sravan1011
    Contributor

    @lokesh could you review this if any changes are required then i'll make those changes

  7. removed
    Needs: ResponseIssues which require feedback from lead
    Needs: Staff DecisionIssues that are blocked on a staff member's decision
    Needs: TriageThis issue needs triage. The team needs to decide who should own it, what to do, by when. [managed]
    Needs: BreakdownThis big issue needs a checklist or subissues to describe a breakdown of work. [managed]
    on Sep 15, 2026
  8. added a commit that references this issue on Oct 5, 2026
  9. Sravan1011 commented on Oct 5, 2026

    @Sravan1011
    Contributor

    @lokesh you can close this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Affects: UIIssues with the web site's user interface. [managed]Lead: @lokeshIssues pertaining to front-end design system [css, js, components]Module: CarouselsModule: JavaScriptIssues related to the JavaScript functionality. [managed]Module: SolrIssues related to the configuration or use of the Solr subsystem. [managed]Needs: Review AssigneeTeam: Front-endIssues belonging to the Front-end team [experimental tag]Theme: CSSIssues related to CSS stylesheets. [managed]Theme: SearchIssues related to search UI and backend. [managed]Type: Feature RequestIssue describes a feature or enhancement we'd like to implement. [managed]

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions