Skip to main content

Filtering & Search

Codex provides powerful filtering and search capabilities to help you find content in your library. This guide covers the filter UI, condition-based filtering, and full-text search.

Quick Filters

The library view includes a filter panel with common filters:

Accessing Filters

  1. Navigate to a library
  2. Click the Filter button in the toolbar
  3. The filter panel opens as a drawer

Filter Groups

Read Status

Filter by reading progress:

StatusDescription
UnreadNo reading progress on any book
In ProgressCurrently reading (some progress, not completed)
ReadAll books completed

Publication Status

Filter by series publication status:

StatusDescription
OngoingSeries is still being published
EndedSeries is complete
HiatusSeries is on hold

Book Type (Books)

Filter books by their type classification:

TypeDescription
ComicWestern comic books
MangaJapanese manga
NovelFull-length novels
NovellaShort novels
AnthologyStory collections
ArtbookArt collections
OneshotSingle standalone issues
OmnibusCombined volumes
Graphic NovelGraphic novels
MagazineMagazines and periodicals

Genres & Tags

Filter by genres and tags extracted from your media metadata (ComicInfo.xml, EPUB metadata).

Filter Modes

Each filter group supports two modes:

  • All selected (AND): Series must match ALL selected values
  • Any selected (OR): Series must match ANY of the selected values

Include vs Exclude

Click a filter chip to cycle through states:

StateVisualBehavior
NeutralGray outlineNot applied
IncludeBlue filledMust match
ExcludeRed filled with XMust NOT match

Active Filters

Active filters appear as chips below the toolbar. Click the X on any chip to remove it, or click "Clear all" to reset.

URL Persistence

Filters are saved in the URL for easy sharing and bookmarking:

/library/abc123?gf=all:Action,Comedy:-Horror&sf=ongoing

Parameters:

  • gf: Genre filter (mode:include1,include2:-exclude1)
  • tf: Tag filter
  • sf: Status filter
  • rf: Read status filter
  • bbt: Book type filter (books only)

Advanced Search Page

For questions that combine a text query with structured filters and a sort order, open /search (also reachable via the "Advanced search →" link in the global search bar dropdown).

The page offers:

  • A text query box with debounced search.
  • A filter builder that exposes the full condition grammar — including nested allOf / anyOf groups and operators not available on the chip-based list-page panels.
  • A sort selector. When a text query is present, a "Relevance (best match)" option appears at the top; clearing the selector returns to the default sort.
  • Series / Books tabs with live counts. Both tabs fetch in parallel so the counts update as soon as you change the filter.
  • A Presets sidebar for saving and reloading the current (filter, query, sort) combination — see Filter Presets below.

If fuzzy search is enabled on the server, a "Fuzzy enabled" badge appears next to the page title. See Full-Text Search for the search-side behaviour.

Shareable URLs

The page encodes its full state in the URL so you can bookmark or share a specific search. The q, sort, tab, and page parameters are human-readable; the filter expression rides in a base64url-encoded c parameter to keep nested conditions compact.

When a filter is too complex to fit in the URL, the page drops the c parameter and shows a yellow notice suggesting you save it as a preset. The preset will reload exactly as built, no length limit.

Filter Presets

A preset is a saved filter combination you can reload later. Presets are per-user — only you can see, edit, or delete your own.

Where to save and load presets

Codex has two preset entry points:

  • List-page filter drawer. Open the filter drawer on the series or books list page and use the Presets controls at the top. Saves use the chip- based grammar of the drawer.
  • Advanced search sidebar on /search. Saves include the text query, the full filter expression, and the sort — everything needed to reproduce the search.

Both entry points share the same storage and the same Manage presets modal, which lists all of your presets grouped by where they're used ("List pages" / "Advanced search") with inline rename and delete.

Saving a preset

  1. Build a filter (and, on /search, optionally add a query and sort).
  2. Click the Save preset icon.
  3. Name the preset.
  4. On a list page that's scoped to a single library, choose This library or All libraries:
    • This library — the preset appears only on this library's filter panel.
    • All libraries — the preset appears on every library's filter panel, including the "All libraries" cross-library view.
  5. Save.

When you save from the "All libraries" view, the preset is always global.

Applying a preset

Open the filter drawer (or the /search sidebar) and pick a preset from the "Apply preset…" menu. The page state updates immediately and pagination returns to page 1.

Presets saved from the advanced search page may use filters that the chip- based list-page drawer can't represent — for example, year ranges, nested groups, or path/format filters. When you try to apply one of those on a list page, Codex surfaces a notification suggesting you open the preset on /search instead.

Renaming and deleting

The Manage presets modal (cog icon next to "Save preset") shows all of your presets and offers inline rename + delete. Deleting prompts for confirmation; renames take effect immediately.

Advanced Filtering (API)

For complex queries, use the POST /series/list or POST /books/list endpoints.

Condition Structure

Filters use a tree structure with combinators:

{
"condition": {
"allOf": [
{ "genre": { "operator": "is", "value": "Action" } },
{ "genre": { "operator": "isNot", "value": "Horror" } }
]
}
}

Combinators

CombinatorSQL EquivalentDescription
allOfANDAll conditions must match
anyOfORAny condition can match

Field Operators

Used by string-valued fields (genre, tag, name, path, format, …).

OperatorDescriptionExample
isEquals{"operator": "is", "value": "Action"}
isNotNot equals{"operator": "isNot", "value": "Horror"}
isNullField is null{"operator": "isNull"}
isNotNullField has value{"operator": "isNotNull"}
containsContains substring{"operator": "contains", "value": "bat"}
doesNotContainDoes not contain substring{"operator": "doesNotContain", "value": "draft"}
beginsWithStarts with{"operator": "beginsWith", "value": "The"}
endsWithEnds with{"operator": "endsWith", "value": "Volume 1"}

Number Operators

Used by numeric fields (year, pageCount).

OperatorDescriptionExample
eqEquals{"operator": "eq", "value": 2024}
neNot equals{"operator": "ne", "value": 0}
gtGreater than{"operator": "gt", "value": 100}
gteGreater than or equal{"operator": "gte", "value": 100}
ltLess than{"operator": "lt", "value": 500}
lteLess than or equal{"operator": "lte", "value": 500}
betweenWithin a range (open-ended supported){"operator": "between", "min": 2000, "max": 2010}
isNullNo value set{"operator": "isNull"}
isNotNullHas a value{"operator": "isNotNull"}

between accepts either bound as null to express open-ended ranges, e.g. {"min": 2000, "max": null} means "year >= 2000".

Date Operators

Used by timestamp fields (dateAdded). Values are ISO-8601 UTC strings.

OperatorDescriptionExample
afterStrictly after{"operator": "after", "value": "2024-01-01T00:00:00Z"}
beforeStrictly before{"operator": "before", "value": "2024-12-31T23:59:59Z"}
onOrAfterAt or after{"operator": "onOrAfter", "value": "2024-01-01T00:00:00Z"}
onOrBeforeAt or before{"operator": "onOrBefore", "value": "2024-12-31T23:59:59Z"}
betweenWithin a range (open-ended supported){"operator": "between", "start": "2024-01-01T00:00:00Z", "end": "2024-06-30T23:59:59Z"}
isNullNo value set{"operator": "isNull"}
isNotNullHas a value{"operator": "isNotNull"}

Series Filter Fields

FieldDescriptionOperators
libraryIdLibrary UUIDis, isNot
genreGenre nameis, isNot
tagTag nameis, isNot
sharingTagSharing tag name (admin)is, isNot
statusPublication statusis, isNot
publisherPublisher nameis, isNot, isNull, isNotNull
languageLanguage code (BCP47)is, isNot, isNull, isNotNull
nameSeries nameis, isNot, contains, beginsWith
titleSortSort titleis, isNot, contains, beginsWith
readStatusReading status (unread / in_progress / read)is, isNot
completionSeries is completeisTrue, isFalse
hasExternalSourceIdLinked to an external sourceisTrue, isFalse
hasUserRatingUser has rated this seriesisTrue, isFalse
isTrackedRelease tracking enabledisTrue, isFalse
yearPublication yearAll number operators
authorAuthor name (matches any role)is, isNot, contains, beginsWith
dateAddedWhen the series first appeared in the libraryAll date operators

Book Filter Fields

FieldDescriptionOperators
libraryIdLibrary UUIDis, isNot
seriesIdSeries UUIDis, isNot
genreGenre name (inherited from series)is, isNot
tagTag name (inherited from series)is, isNot
titleBook titleis, isNot, contains
readStatusReading statusis, isNot
hasErrorHas parsing errorisTrue, isFalse
bookTypeBook type classificationis, isNot, isNull, isNotNull
pathFile path (substring match)is, isNot, contains, beginsWith, endsWith
formatFile format (cbz, cbr, epub, pdf, …)is, isNot
pageCountNumber of pagesAll number operators
dateAddedWhen the book first appeared in the libraryAll date operators

Example Queries

Simple Genre Filter

{
"condition": {
"genre": { "operator": "is", "value": "Action" }
}
}

Multiple Genres (OR)

Match series with Action OR Comedy:

{
"condition": {
"anyOf": [
{ "genre": { "operator": "is", "value": "Action" } },
{ "genre": { "operator": "is", "value": "Comedy" } }
]
}
}

Genre with Exclusion

Match Action series but exclude Horror:

{
"condition": {
"allOf": [
{ "genre": { "operator": "is", "value": "Action" } },
{ "genre": { "operator": "isNot", "value": "Horror" } }
]
}
}

Complex Nested Query

(Action AND Comedy) OR (Fantasy AND NOT Horror):

{
"condition": {
"anyOf": [
{
"allOf": [
{ "genre": { "operator": "is", "value": "Action" } },
{ "genre": { "operator": "is", "value": "Comedy" } }
]
},
{
"allOf": [
{ "genre": { "operator": "is", "value": "Fantasy" } },
{ "genre": { "operator": "isNot", "value": "Horror" } }
]
}
]
}
}

Read Status Filter

Find series currently being read:

{
"condition": {
"readStatus": { "operator": "is", "value": "in_progress" }
}
}

Multi-Field Filter

Ongoing Action series with Favorite tag:

{
"condition": {
"allOf": [
{ "status": { "operator": "is", "value": "ongoing" } },
{ "genre": { "operator": "is", "value": "Action" } },
{ "tag": { "operator": "is", "value": "Favorite" } }
]
}
}

Book Type Filter

Find all manga books:

{
"condition": {
"bookType": { "operator": "is", "value": "manga" }
}
}

Find books without a type classification:

{
"condition": {
"bookType": { "operator": "isNull" }
}
}

Format Filter

CBZ files only:

{
"condition": {
"format": { "operator": "is", "value": "cbz" }
}
}

Year Range

Series published between 2000 and 2009 (inclusive):

{
"condition": {
"year": { "operator": "between", "min": 2000, "max": 2009 }
}
}

Open-ended: everything from 2020 onward:

{
"condition": {
"year": { "operator": "gte", "value": 2020 }
}
}

Recently Added

Books added in the last 30 days (the client supplies the cutoff ISO timestamp):

{
"condition": {
"dateAdded": { "operator": "onOrAfter", "value": "2026-04-19T00:00:00Z" }
}
}

Combine full-text search with condition filters:

{
"fullTextSearch": "batman",
"condition": {
"genre": { "operator": "is", "value": "Action" }
}
}

The text query is combined with the structured condition using AND logic and respects the user's sharing-tag permissions.

Fuzzy Matching

When the search.fuzzy.enabled server setting is on, text queries route through an in-memory fuzzy index that tolerates typos and partial matches and ranks results by relevance. The index is updated incrementally as items are added, edited, or removed.

When fuzzy is off, the same query falls back to a case-insensitive LIKE match against titles. No behavioural difference besides ranking quality.

Relevance Sort

When a text query is set, the /search page defaults to ranking results by fuzzy score. You can override this from the sort dropdown to use any of the standard sort fields (release date, title, page count, etc.) — the relevance ranking is dropped when you do so.

The API mirrors this:

  • sort unset + fullTextSearch set → implicit relevance ranking.
  • sort=relevance → explicit relevance (also accepts score and relevance,asc/desc); falls back to the natural default if no query is present.
  • Any other explicit sort value → that sort wins.

The header search bar searches both series and books:

  1. Type at least 2 characters.
  2. Results appear in a dropdown grouped by type.
  3. Click a result to navigate, press Enter for the simple results page, or click Advanced search → to open /search pre-filled with your query.

Performance Tips

  1. Use specific filters: More specific filters run faster
  2. Combine with library_id: Always include library_id when filtering within a library
  3. Avoid deep nesting: Very deeply nested conditions may be slower
  4. Use pagination: Always paginate results for large libraries

Next Steps