docs: add documentation on new search capabilities

This commit is contained in:
Christian Benincasa
2025-11-19 12:49:20 -05:00
parent 80fccd257a
commit ea496321fd
3 changed files with 1015 additions and 0 deletions

73
docs/misc/search.md Normal file
View File

@@ -0,0 +1,73 @@
---
status: new
---
<style>
table {
width: 100%;
}
</style>
Tunarr features a built-in search index powered by [Meilisearch](https://www.meilisearch.com/). When scanning libraries from your media sources, Tunarr will add information to the search index, which can then be queried to filter content.
Tunarr's search supports a basic query language to create structured searches, as well as a "free text" search which looks within all fields. A syntax diagram of the Tunarr query language can be viewed [here](/search_syntax.html).
## Syntax
Tunarr's search feature different typed fields, such as `string`, `number`, and `date`. Each type has a set of supported operators.
## Strings
| Operator | Description | Example
|---|---|---|
| `:` or `=` | Equals | `title:"30 Rock"`|
| `<` or `<=` | Starts With | `title <= A` |
| `!=` | Not Equals | `title != "Sesame Street"` |
| `contains` | Contains | `title contains Hours` |
| `in` | Set includes | `title in ["30 Rock", "Arrested Development"]` |
## Number & Date
| Operator | Description | Example
|---|---|---|
| `:` or `=` | Equals | `video_width = 1920`|
| `<` | Less Than | `minutes < 30` |
| `<=` | Less Than or Equal To | `minutes <= 22` |
| `>` | Greater Than | `minutes > 60` |
| `<=` | Greater Than or Equal To | `minutes >= 60` |
| `!=` | Not Equals | `video_height != 2160` |
| `between` | Range query (`[]` used for inclusive and `()` for exclusive ranges) | `minutes between [10, 30]` |
## Compound Queries
Query clauses can be combined using the standard boolean operators `AND` and `OR`. Parentheses can be used to disambiguage query clause groups in more complex queries. For example:
```
genre IN [Horror, Comedy] AND title <= A
```
## Fields
Fields available for search:
| Field | Type | Description | Examples |
|-------|------|---|----|
| `title` | `string` | Program title | 30 Rock |
| `type` | `string` | Program type | `show`, `movie`, `episode` |
| `rating` | `string` | Program content rating | PG-13 |
| `duration` | `number` | Program duration in milliseconds | - |
| `minutes` | `number` | Program duration in minutes | - |
| `seconds` | `number` | Program duration in seconds | - |
| `actor` | `string` | Actor name | Dwyane Johnson |
| `writer` | `string` | Writer name | - |
| `director` | `string` | Director name | - |
| `genre` | `string` | Program genre | `Comedy` |
| `tag` | `string` | Program tags | - |
| `video_codec` | `string` | Video codec of the program | `hevc` |
| `audio_codec` | `string` | Audio codec of the program | `ac3` |
| `video_height` | `number` | Video height dimension in pixels | `1080` |
| `video_width` | `number` | Video width dimension in pixels | `1920` |
| `video_bit_depth` | `number` | Video pixel bit depth | `10` |
| `audio_channels` | `number` | Whole number audio channels | `2`, `5.1` => `6` |
| `release_year` | `number` | Program release year | `1990` |
| `release_date` | `date` | Program's original release date | `1990-12-05` (`YYYY-MM-DD` or `YYYYMMDD`) |

940
docs/search_syntax.html Normal file
View File

@@ -0,0 +1,940 @@
<!-- This is a generated file -->
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background-color: hsl(30, 20%, 95%)
}
</style>
<link rel='stylesheet' href='https://unpkg.com/chevrotain@11.0.3/diagrams/diagrams.css'>
<script src='https://unpkg.com/chevrotain@11.0.3/diagrams/vendor/railroad-diagrams.js'></script>
<script src='https://unpkg.com/chevrotain@11.0.3/diagrams/src/diagrams_builder.js'></script>
<script src='https://unpkg.com/chevrotain@11.0.3/diagrams/src/diagrams_behavior.js'></script>
<script src='https://unpkg.com/chevrotain@11.0.3/diagrams/src/main.js'></script>
<div id="diagrams" align="center"></div>
<script>
window.serializedGrammar = [
{
"type": "Rule",
"name": "searchValue",
"orgText": "",
"definition": [
{
"type": "Alternation",
"idx": 0,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "Quote",
"label": "Quote",
"idx": 0,
"terminalLabel": "str_open",
"pattern": "\""
},
{
"type": "RepetitionMandatory",
"idx": 0,
"definition": [
{
"type": "Repetition",
"idx": 0,
"definition": [
{
"type": "Alternation",
"idx": 2,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "Identifier",
"label": "Identifier",
"idx": 2,
"terminalLabel": "query",
"pattern": "[a-zA-Z0-9-]+"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "Integer",
"label": "Integer",
"idx": 2,
"terminalLabel": "query",
"pattern": "\\d+"
}
]
}
]
}
]
}
]
},
{
"type": "Terminal",
"name": "Quote",
"label": "Quote",
"idx": 3,
"terminalLabel": "str_close",
"pattern": "\""
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Repetition",
"idx": 2,
"definition": [
{
"type": "Alternation",
"idx": 3,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "Identifier",
"label": "Identifier",
"idx": 4,
"terminalLabel": "query",
"pattern": "[a-zA-Z0-9-]+"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "Integer",
"label": "Integer",
"idx": 4,
"terminalLabel": "query",
"pattern": "\\d+"
}
]
}
]
}
]
}
]
}
]
}
]
},
{
"type": "Rule",
"name": "parenGroup",
"orgText": "",
"definition": [
{
"type": "Terminal",
"name": "OpenParenGroup",
"label": "OpenParenGroup",
"idx": 0,
"pattern": "\\("
},
{
"type": "RepetitionMandatory",
"idx": 0,
"definition": [
{
"type": "NonTerminal",
"name": "searchClause",
"idx": 0
}
]
},
{
"type": "Terminal",
"name": "CloseParenGroup",
"label": "CloseParenGroup",
"idx": 0,
"pattern": "\\)"
}
]
},
{
"type": "Rule",
"name": "string_operator",
"orgText": "",
"definition": [
{
"type": "Alternation",
"idx": 0,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Alternation",
"idx": 2,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "EqOperator",
"label": "EqOperator",
"idx": 0,
"pattern": ":|="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "NeqOperator",
"label": "NeqOperator",
"idx": 0,
"pattern": "!="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "LTEOperator",
"label": "LTEOperator",
"idx": 0,
"pattern": "<="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "LTOperator",
"label": "LTOperator",
"idx": 0,
"pattern": "<"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "ContainsOperator",
"label": "ContainsOperator",
"idx": 0,
"pattern": "~"
}
]
}
]
},
{
"type": "NonTerminal",
"name": "searchValue",
"idx": 0
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "InOperator",
"label": "InOperator",
"idx": 0,
"pattern": "in"
},
{
"type": "Terminal",
"name": "OpenArray",
"label": "OpenArray",
"idx": 2,
"pattern": "\\["
},
{
"type": "RepetitionMandatoryWithSeparator",
"idx": 0,
"separator": {
"type": "Terminal",
"name": "Comma",
"label": "Comma",
"idx": 1,
"pattern": ","
},
"definition": [
{
"type": "NonTerminal",
"name": "searchValue",
"idx": 2
}
]
},
{
"type": "Terminal",
"name": "CloseArray",
"label": "CloseArray",
"idx": 2,
"pattern": "]"
}
]
}
]
}
]
},
{
"type": "Rule",
"name": "numeric_operator",
"orgText": "",
"definition": [
{
"type": "Alternation",
"idx": 0,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Alternation",
"idx": 2,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "EqOperator",
"label": "EqOperator",
"idx": 0,
"pattern": ":|="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "NeqOperator",
"label": "NeqOperator",
"idx": 0,
"pattern": "!="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "LTEOperator",
"label": "LTEOperator",
"idx": 0,
"pattern": "<="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "GTEOperator",
"label": "GTEOperator",
"idx": 0,
"pattern": ">="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "LTOperator",
"label": "LTOperator",
"idx": 0,
"pattern": "<"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "GTOperator",
"label": "GTOperator",
"idx": 0,
"pattern": ">"
}
]
}
]
},
{
"type": "NonTerminal",
"name": "numeric_value",
"idx": 0
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "BetweenOperator",
"label": "BetweenOperator",
"idx": 0,
"pattern": "between"
},
{
"type": "Alternation",
"idx": 3,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "OpenParenGroup",
"label": "OpenParenGroup",
"idx": 2,
"pattern": "\\("
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "OpenArray",
"label": "OpenArray",
"idx": 2,
"pattern": "\\["
}
]
}
]
},
{
"type": "NonTerminal",
"name": "numeric_value",
"idx": 2
},
{
"type": "Option",
"idx": 0,
"definition": [
{
"type": "Terminal",
"name": "Comma",
"label": "Comma",
"idx": 2,
"pattern": ","
}
]
},
{
"type": "NonTerminal",
"name": "numeric_value",
"idx": 3
},
{
"type": "Alternation",
"idx": 4,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "CloseParenGroup",
"label": "CloseParenGroup",
"idx": 3,
"pattern": "\\)"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "CloseArray",
"label": "CloseArray",
"idx": 3,
"pattern": "]"
}
]
}
]
}
]
}
]
}
]
},
{
"type": "Rule",
"name": "numeric_value",
"orgText": "",
"definition": [
{
"type": "Alternation",
"idx": 0,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "FloatingPoint",
"label": "FloatingPoint",
"idx": 0,
"pattern": "\\d+\\.\\d+"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "Integer",
"label": "Integer",
"idx": 0,
"pattern": "\\d+"
}
]
}
]
}
]
},
{
"type": "Rule",
"name": "date_operator",
"orgText": "",
"definition": [
{
"type": "Alternation",
"idx": 0,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Alternation",
"idx": 2,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "EqOperator",
"label": "EqOperator",
"idx": 0,
"pattern": ":|="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "LTEOperator",
"label": "LTEOperator",
"idx": 0,
"pattern": "<="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "LTOperator",
"label": "LTOperator",
"idx": 0,
"pattern": "<"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "GTEOperator",
"label": "GTEOperator",
"idx": 0,
"pattern": ">="
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "GTOperator",
"label": "GTOperator",
"idx": 0,
"pattern": ">"
}
]
}
]
},
{
"type": "NonTerminal",
"name": "searchValue",
"idx": 0
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "BetweenOperator",
"label": "BetweenOperator",
"idx": 0,
"pattern": "between"
},
{
"type": "Alternation",
"idx": 3,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "OpenParenGroup",
"label": "OpenParenGroup",
"idx": 2,
"pattern": "\\("
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "OpenArray",
"label": "OpenArray",
"idx": 2,
"pattern": "\\["
}
]
}
]
},
{
"type": "NonTerminal",
"name": "searchValue",
"idx": 2
},
{
"type": "Option",
"idx": 0,
"definition": [
{
"type": "Terminal",
"name": "Comma",
"label": "Comma",
"idx": 2,
"pattern": ","
}
]
},
{
"type": "NonTerminal",
"name": "searchValue",
"idx": 3
},
{
"type": "Alternation",
"idx": 4,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "CloseParenGroup",
"label": "CloseParenGroup",
"idx": 3,
"pattern": "\\)"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "CloseArray",
"label": "CloseArray",
"idx": 3,
"pattern": "]"
}
]
}
]
}
]
}
]
}
]
},
{
"type": "Rule",
"name": "singleStringSearch",
"orgText": "",
"definition": [
{
"type": "Terminal",
"name": "StringField",
"label": "StringField",
"idx": 0,
"terminalLabel": "field",
"pattern": "actor|genre|director|writer|library_id|title|video_codec|video_dynamic_range|audio_codec|tags|rating|type"
},
{
"type": "NonTerminal",
"name": "string_operator",
"idx": 0,
"label": "op"
}
]
},
{
"type": "Rule",
"name": "singleNumericSearch",
"orgText": "",
"definition": [
{
"type": "Terminal",
"name": "NumericField",
"label": "NumericField",
"idx": 0,
"pattern": "duration|minutes|seconds|video_bit_depth|video_height|video_width|audio_channels|release_year"
},
{
"type": "NonTerminal",
"name": "numeric_operator",
"idx": 0
}
]
},
{
"type": "Rule",
"name": "singleDateSearch",
"orgText": "",
"definition": [
{
"type": "Terminal",
"name": "DateField",
"label": "DateField",
"idx": 0,
"terminalLabel": "field",
"pattern": "release_date"
},
{
"type": "NonTerminal",
"name": "date_operator",
"idx": 0,
"label": "op"
}
]
},
{
"type": "Rule",
"name": "singleSearch",
"orgText": "",
"definition": [
{
"type": "Alternation",
"idx": 0,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "NonTerminal",
"name": "singleStringSearch",
"idx": 0
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "NonTerminal",
"name": "singleNumericSearch",
"idx": 0
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "NonTerminal",
"name": "singleDateSearch",
"idx": 0
}
]
}
]
}
]
},
{
"type": "Rule",
"name": "searchClause",
"orgText": "",
"definition": [
{
"type": "Alternation",
"idx": 0,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "NonTerminal",
"name": "parenGroup",
"idx": 0
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "NonTerminal",
"name": "singleSearch",
"idx": 0
},
{
"type": "Option",
"idx": 0,
"definition": [
{
"type": "Alternation",
"idx": 2,
"definition": [
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "CombineOr",
"label": "CombineOr",
"idx": 0,
"pattern": "OR"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "CombineAnd",
"label": "CombineAnd",
"idx": 0,
"pattern": "AND"
}
]
},
{
"type": "Alternative",
"definition": [
{
"type": "Terminal",
"name": "WhiteSpace",
"label": "WhiteSpace",
"idx": 0,
"pattern": "\\s+"
}
]
}
]
},
{
"type": "NonTerminal",
"name": "searchClause",
"idx": 0
}
]
}
]
}
]
}
]
},
{
"type": "Rule",
"name": "searchExpression",
"orgText": "",
"definition": [
{
"type": "RepetitionMandatory",
"idx": 0,
"definition": [
{
"type": "NonTerminal",
"name": "searchClause",
"idx": 0
}
]
}
]
}
];
</script>
<script>
var diagramsDiv = document.getElementById("diagrams");
main.drawDiagramsFromSerializedGrammar(serializedGrammar, diagramsDiv);
</script>

View File

@@ -80,6 +80,7 @@ nav:
- Plex: configure/clients/plex.md
- Jellyfin: configure/clients/jellyfin.md
- Misc.:
- Search: misc/search.md
- Common Issues: misc/common-issues.md
- Troubleshooting: misc/troubleshooting.md
- Development:
@@ -101,6 +102,7 @@ markdown_extensions:
- pymdownx.snippets:
auto_append:
- docs-extras/includes/definitions.md
- tables
extra_css:
- stylesheets/extra.css