Skip to content

Full Text Search Has Landed#3531

Open
webysther wants to merge 32 commits intojaneczku:masterfrom
webysther:fts
Open

Full Text Search Has Landed#3531
webysther wants to merge 32 commits intojaneczku:masterfrom
webysther:fts

Conversation

@webysther
Copy link
Copy Markdown
Contributor

@webysther webysther commented Jan 24, 2026

Do you think this PR help you? Consider a donation: https://github.com/sponsors/webysther

Full Text Search is finally here, bringing a faster, more powerful way to discover books. Search now taps into Calibre’s full‑text index with an optional toggle in settings, while preserving the existing search as a safe fallback. Under the hood, the feature uses a dedicated FTS database, loads Calibre’s tokenizer extensions, and integrates cleanly with the existing /search flow. The result is an exciting, revitalizing upgrade to search—more accurate, more responsive, and ready for deeper discovery.

  • Allow enable/disable full text search
  • Allow merge the simple search to full text search results
  • Allow hide/show the archived books on full text search results
  • Button to show the search result by relevance
  • Bugfix: if search paginate show the total books found, not the current page size (now only fixed for full text search)
image image image
Recording.2026-01-29.182517.1.mp4

Current limitations

LD_LIBRARY_PATH=/app/calibre/lib
DOCKER_MODS=linuxserver/mods:universal-calibre

fix #2490
fix #2776
fix #3129
fix #3468
fix #2308
fix #1412

related #3476
related #2772
related #718

  - 【big picture】 outline entry points, create_app, and server runtime
  - 【workflows】 explain running locally and dependency locations
  - 【conventions】 document app globals, rate limits, and security headers
  - 【integrations】 note optional services and external binaries config
  - 【where to look】 point to bootstrap, web logic, and dependency files
- 【feat】 add config_fulltext_search boolean column to settings
- 【feat】 add checkbox to admin config page to toggle the feature
- 【feat】 check config before using full-text DB in search rendering
- 【what】replace sqlite3 usage with sqlalchemy engine and connection
- 【why】preserve fts extension loading and avoid sqlite thread issues
- 【how】obtain raw DB connection to load extension and run param sql
- 【fix】 replace config.config_binariesdir with constants.BASE_DIR
- 【fix】 build path bin/calibre/calibre-extensions/sqlite_extension
- 【fix】 import constants and load extension if it exists
- 【why】 use project base dir for reliable extension loading across envs
- 【feat】 include bm25(books_fts) as rank in FTS query to score matches
- 【feat】 order by rank so search results are returned by relevance

♻️ refactor(search): use fixed extension path and stable dedupe
- 【refactor】 load sqlite extension from container .so path for consistent env
- 【refactor】 deduplicate fts ids while preserving order using dict.fromkeys
- 【refactor】 simplify JOIN clause formatting for readability
@webysther webysther marked this pull request as ready for review January 25, 2026 00:05
@crocodilestick
Copy link
Copy Markdown

crocodilestick commented Jan 26, 2026

For those interested, this functionality has now been added to Calibre-Web Automated (CWA) in addition to a full management console & automated indexing for the process that allows users to better control over the process as well as the ability to view the current indexing status of their library ect. Amazing work @webysther <3

@webysther webysther changed the title Full Text Search Has Landed 🔎 Full Text Search Has Landed Jan 26, 2026
- 【what】 change FTS select to use DISTINCT to remove duplicate ids
- 【what】 remove bm25 rank, ordering and pagination logic
- 【what】 simplify SQLAlchemy query to only filter by FTS ids
- 【why】 fixes duplicate search results and simplifies retrieval logic
@crocodilestick
Copy link
Copy Markdown

Hey @webysther - I had to pull the FTS changes from the upcoming release. The latest version still has a few blocking issues that would ship broken behaviour:

The FTS SQL orders by rank but never selects a rank field, so the query errors and FTS silently falls back. That means relevance never actually works.
The FTS query dropped common_filters(True), which can expose books that users shouldn’t see (archived/restricted).
The merge logic combines already‑paginated base results with full FTS results, then paginates again. That makes counts and paging wrong.
Because of those, I reverted it from main and kept it on a separate full-text-search branch/PR so it can be finished safely.

What still needs fixing before it’s ready:

Restore a valid FTS ranking query (or select a rank column) so relevance sorting actually works.
Re‑apply common_filters(True) to keep permissions/visibility correct.
Fix pagination/counts by paginating after merging full result sets, or fetch unpaginated base results when merging.
Verify extension loading is correct on Python/SQLite (enable load extension before load_extension).

Once those are fixed and tested, I’ll bring it back into CWA!

@webysther
Copy link
Copy Markdown
Contributor Author

webysther commented Jan 29, 2026

Hey @webysther - I had to pull the FTS changes from the upcoming release. The latest version still has a few blocking issues that would ship broken behaviour:

The FTS SQL orders by rank but never selects a rank field, so the query errors and FTS silently falls back. That means relevance never actually works. The FTS query dropped common_filters(True), which can expose books that users shouldn’t see (archived/restricted). The merge logic combines already‑paginated base results with full FTS results, then paginates again. That makes counts and paging wrong. Because of those, I reverted it from main and kept it on a separate full-text-search branch/PR so it can be finished safely.

What still needs fixing before it’s ready:

Restore a valid FTS ranking query (or select a rank column) so relevance sorting actually works. Re‑apply common_filters(True) to keep permissions/visibility correct. Fix pagination/counts by paginating after merging full result sets, or fetch unpaginated base results when merging. Verify extension loading is correct on Python/SQLite (enable load extension before load_extension).

Once those are fixed and tested, I’ll bring it back into CWA!

All these are fixed already!

- add config_hide_archived_search boolean setting to _Settings model (default True)
- persist new config in admin _configuration_update_helper via checkbox
- add checkbox to config_edit.html to control hiding archived books
- update render_search_results to query user's ArchivedBook entries when enabled
- exclude archived book ids from base and fts merged results; on exception fall back to empty set
- add config_merge_search boolean setting to database model with default False
- add checkbox for config_merge_search to configuration UI (cps/templates/config_edit.html)
- register config_merge_search checkbox in admin config update helper (cps/admin.py)
- when enabled, include base simple-search results via calibre_db.get_search_results
- when enabled, merge base_entries into merged results while skipping archived and deduping by id
@OzzieIsaacs OzzieIsaacs changed the title 🔎 Full Text Search Has Landed Full Text Search Has Landed Jan 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants