Repository navigation
Prototype: "Nearby Books" carousel — DDC shelf-adjacency browsing (Lit component) #13159
Description
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 -->
lccis 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 viaopenlibrary/utils/ddc.py::normalize_ddc()(zero-pads the integer part so lexical string sort approximates numeric order) andchoose_sorting_ddc()picks one representative value forddc_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_sortare Solrtype="string", not numeric/float. There is nosort=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-231uses 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-254fetchessearch.jsonwithfields: 'key,title,author_name,cover_i,ddc,lcc,...', sorted byddc_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 innormalize_ddc(), this reproduces correct numeric ordering for practical purposes. No schema change needed for a prototype.
3. Language filtering — straightforward
languagefield:managed-schema.xml:158,type="string" multiValued="true", also per-work (aggregated inwork.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 viavite-lit.config.mjsintool-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-configJSON 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-headermarkup 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_sortimmediately below + N immediately above the target, filtered by matchinglanguage, 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:
- LCC-based nearby (DDC only for the first pass).
- Any Solr schema/reindex work (numeric DDC field, true nearest-neighbor function queries).
- Genre/subject-adjacency reasoning (that's a separate, already-filed prototype: Epic: Browse the Stacks: a discovery/explore-centric homepage #13158, Genre Explorer).
- Availability-aware sorting beyond what Solr naturally returns (e.g., not prioritizing "currently lendable" editions).
- Edition-level (vs. work-level) classification — would require the Move LC classification and DDC from editions to work records #897 migration.
Open questions for Mek/Lokesh
- 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)?
- Is the lexical string range-query approach (reusing Library Explorer's
decrementStringSolr/_sortpattern) sufficient, or is a numeric DDC field + true nearest-neighbor sort worth exploring as a v2 (schema change + reindex, non-trivial infra cost)? - Should this reuse the existing
custom_carousel.htmlshell/CSS for visual consistency, or be a fully independent Lit component? - Where does it render — work page, edition page, or both?
- 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
openlibrary-bot commentedon Jul 15, 2026
Priority: *Context
Verified the issue's own research against the current tree (commit
8404a0c) — the file/line citations are accurate. A few additional findings:openlibrary/components/lit/OlCarousel.js(exported atindex.js#L25) — a<ol-carousel>custom element with page-based nav, swipe/spring physics, and a slot for arbitrary child cards. It's currently only demoed intemplates/design/carousel.html.jinja, not used on any live page. This changes the calculus on the issue's open question catalog/onix/onix.py doesn't handle books with multiple title tags #3 (custom_carousel.html shell vs. independent Lit component) — there may be a third option: slot fetched book cards into the existing<ol-carousel>shell rather than building carousel-UI from scratch or reusing the legacy jQuery/Templetor shell.Priority: 2,Needs: Breakdown,Needs: Designs) proposes "leveraging library classification numbers (LCCN or DDC) to do something similar to library explorer, where solr queries are done to show neighboring books" — with an attached mockup screenshot — for the same book-page dead-end problem this prototype targets. Worth deciding whether Prototype: "Nearby Books" carousel — DDC shelf-adjacency browsing (Lit component) #13159 supersedes/scopes-down Book Page: Recommend Available Alternatives #12743, or whether they should be linked as parent/child.get_edition_ddcs()already strips the92/920/092biography-suffix noise the issue flags as a normalization concern — that edge case is handled, not a new risk.RelatedWorksCarousel.html(mounted fromtype/edition/view.html:555-559) usesmacros.QueryCarousel(..., user_lang_only=True)against subject/author queries — a same-page precedent for "more like this" carousels the new component would sit alongside, worth checking for visual/placement consistency.Issue Assessment
Open questions
BookRoom.vue:224-231/decrementStringSolr— no schema change needed for a v1, per the issue's own finding.)custom_carousel.html's shell, the newly-found<ol-carousel>Lit shell, or a fully bespoke component?RelatedWorksCarouselontype/edition/view.html?Suggested breakdown
ddc_sort+language(work or search.json lookup)ddc_sortrange queries (before/after target) filtered bylanguage, excluding the current work — port thedecrementStringSolrpattern out ofLibraryExplorer/utils.js(or import it, if scoping allows a shared util)None of these are standalone
Good First Issuecandidates — 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
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.
10 remaining items
Sravan1011 commentedon Sep 3, 2026
@lokesh could you review this if any changes are required then i'll make those changes
Prototype: Add Nearby Books carousel Lit component (internetarchive#1…
Prototype: Add Nearby Books carousel Lit component (internetarchive#1…
Prototype: Add Nearby Books carousel using Python helper & RawQueryCa…
Refactor Nearby Books to Python helper & RawQueryCarousel with strict…
Move Nearby Books to a lazy-loaded partial anchored on the work's ddc…
Merge pull request #13526 from Sravan1011/nearby-books-prototype
Sravan1011 commentedon Oct 5, 2026
@lokesh you can close this issue