YetiSearch Pro Documentation
Install, configure, and use YetiSearch Pro for powerful local search
Found an issue or a problem that you can't find an answer for? Create an issue in our Premium Issue Tracker.
Before you start
Before you can install a Premium Plugin, you are required to have the License Manager plugin installed. It is a free plugin available in GPM and can be installed like any other through the Plugin section of the admin plugin, or via command line.
$ bin/gpm install license-manager
After installation you should install your product license. This was sent to you via email when you purchased the product. If you have lost your license, you can retrieve it from the new License Portal. Ensure you use the same email address you used to purchase the product.
Installing a License
Once you have the License Manager plugin installed, you can install your license. You can do this in two ways:
Via Admin Plugin
- Navigate to the License Manager section of the admin plugin.
- Click the Install License button.
- Enter your license key and click Install.
- You should see a success message and the license will be installed.
Via Command Line
You can also install your license via command line. To do this, you need to run the following command:
$ bin/plugin license-manager add -s <product-slug> -l <license-key>
Installation
First ensure you are running the latest version of Grav 1.7 (-f forces a refresh of the GPM index).
$ bin/gpm selfupgrade -f
Install YetiSearch Pro via GPM:
$ bin/gpm install yetisearch-pro
You can also install the plugin via the Plugins section in the admin plugin.
After installation, run your first index to populate the search database:
$ bin/plugin yetisearch-pro index --flush
Features
- 100% Local Search - File-backed search with zero external dependencies
- Two Modal Designs - Choose between "simple" (lightweight typeahead) or "full" (two-panel with preview)
- Vanilla JS - No framework dependencies, works with any theme
- CSS Variables - Easy theming with CSS custom properties
- Smart Indexing - Fingerprint-based change detection for efficient updates
- Orphan Cleanup - Automatically removes stale documents from deleted pages
- Real-time Updates - Index updates on page save/delete (optional)
- Multi-language - Per-language or single-index strategies
- Fuzzy Search - Configurable typo tolerance
- Chunked Indexing - Precise search results within long content
- Analytics - Track searches, clicks, and popular terms
- Geospatial Search - Location-based queries with radius and bounding box filters
- Permissions - Granular admin permissions (view, modify, admin)
- Scheduler - Automated reindex and maintenance via Grav scheduler
- CLI Commands - Full command-line interface for indexing, querying, and maintenance
Configuration
YetiSearch Pro is configured via user/plugins/yetisearch-pro/yetisearch-pro.yaml or through the Admin plugin interface.
Basic Configuration
enabled: true
engine:
storage_dir: 'user://data/yetisearch-pro'
index_prefix: 'grav_'
timeout: 5
max_batch_size: 10
search:
snippet_length: 75 # default snippet window length
indexes:
pages:
strategy: single # or 'per_language'
languages: [] # empty: derive from Grav languages/versions
fields: [title, url, taxonomy.*, meta.description, content]
facets: [taxonomy.category, taxonomy.tag]
geo_fields: []
options:
fuzzy: true
typo_tolerance: 2
Index Strategies
YetiSearch Pro supports two index strategies:
-
single(default) - One database for all languages. Language is stored in each document.- File:
user/data/yetisearch-pro/<prefix><index>.db(e.g.,grav_pages.db) - Query with
--index pages --lang frto leverage language-aware processing.
- File:
-
per_language- One database file per language/version.- File:
user/data/yetisearch-pro/<prefix><index>_<lang>.db(e.g.,grav_pages_17.db) - Query with
--index pages_17or--index pages --lang 17.
- File:
Query Defaults & Field Boosting
Configure default search fields and boost weights per index:
indexes:
pages:
query_defaults:
per_page: 10
fields: [title, headers_h1, headers_h2, headers_h3, headers, content, excerpt, tags]
boost:
title: 5.0
headers_h1: 4.0
headers_h2: 3.0
headers_h3: 2.0
headers: 1.5
tags: 2.5
excerpt: 2.0
content: 1.0
rerank:
title_exact: 10.0 # bonus for exact word match in title
title_prefix: 8.0 # bonus for prefix/starts-with title match
title_word: 5.0 # bonus for word match in title
headers_exact: 7.0 # bonus for exact word match in headers
headers_prefix: 6.0 # bonus for prefix match in headers
headers_word: 3.0 # bonus for word match in headers
chunk: 1.0 # slight preference for chunk hits
fuzzy_factor: 1.2 # scale rerank bonuses when fuzzy is enabled
demote_prefixes: ['/api']
demote_penalty: 10.0
The rerank settings allow client-side re-ranking after engine scoring, giving you fine control over result ordering.
Environment Modes
The plugin supports environment-aware configuration to prevent index collisions:
environment:
mode: auto # auto | production | staging | development
append_environment_to_index: true # suffix index prefix with environment name
realtime_in_development: true # enable realtime indexing in dev mode
When mode is auto, the plugin follows Grav's environment detection. The append_environment_to_index option prevents staging/development indexes from colliding with production.
Modal Configuration
YetiSearch Pro includes two modal designs that themes can use.
Modal Types
-
full(default) - Two-panel modal with results on the left and a live preview on the right. Includes pagination, filter controls, breadcrumbs, and On This Page sections. Best for documentation sites with longer content. -
simple- Lightweight typeahead modal with grouped results. Single panel, no preview. Best for general sites that want a clean, fast search experience.
Configuration
modal:
type: full # 'full' or 'simple'
branding: true # Show "Powered by YetiSearch" in footer
Theme Integration
Adding the Search Modal
Include the modal in your theme's base template:
{# In your theme's partials/base.html.twig or similar #}
{% if config.plugins['yetisearch-pro'].enabled %}
{% include 'partials/yetisearch-pro/modal.html.twig' %}
{% endif %}
The modal automatically selects the correct type based on your configuration. To include a specific modal directly:
{% include 'partials/yetisearch-pro/modal-simple.html.twig' %}
{# or #}
{% include 'partials/yetisearch-pro/modal-full.html.twig' %}
Search Trigger Button
Include a ready-made search button:
{% include 'partials/yetisearch-pro/search-trigger.html.twig' %}
{# With custom options #}
{% include 'partials/yetisearch-pro/search-trigger.html.twig' with {
placeholder: 'Search docs...',
show_shortcut: true,
class: 'my-custom-class'
} %}
Opening the Modal Programmatically
Add data-ys-open to any element to make it open the search modal:
<button data-ys-open>Search</button>
<input type="text" data-ys-open placeholder="Search...">
Keyboard shortcuts: Cmd/Ctrl+K or / (when not in a text field).
Customizing with CSS Variables
Both modals use CSS variables for easy theming:
/* Simple modal variables */
:root {
--ys-simple-dialog-bg: #ffffff;
--ys-simple-text: #111827;
--ys-simple-accent: #3b82f6;
--ys-simple-highlight-bg: #fef08a;
}
/* Full modal variables */
:root {
--ys-panel-bg: #181a1f;
--ys-text: #d3d7df;
--ys-accent: #4ea1ff;
}
Twig Variables
The plugin exposes the modal type to Twig:
{{ yetisearch_modal_type }} {# 'full' or 'simple' #}
Customizing Modal Templates
Both modals use a base template + extends pattern that lets themes override specific parts without reimplementing the entire modal.
Template Inheritance
Plugin: partials/yetisearch-pro/base/modal-simple.html.twig ← canonical ({% block %} tags)
Plugin: partials/yetisearch-pro/modal-simple.html.twig ← thin {% extends %} wrapper
Theme: partials/yetisearch-pro/modal-simple.html.twig ← theme override (extends base)
The theme's file shadows the plugin's wrapper via Grav's template resolution. The {% extends %} path points to base/... which only lives in the plugin, so there's no circular lookup.
The same pattern applies to modal-full.html.twig.
Available Blocks – Simple Modal
| Block | Description |
|---|---|
modal |
Entire modal container |
modal_data_attrs |
Empty slot for extra data-* attributes on the root <div> |
backdrop |
Backdrop overlay |
search_input |
Input wrapper (icon + input + ESC badge) |
results |
Full results section |
results_header |
Results count header |
loading |
Loading spinner |
empty |
Empty state message |
type_more |
"Type more characters" prompt |
results_list |
Grouped results container |
footer |
Footer section |
keyboard_hints |
Keyboard shortcut hints |
branding |
"Powered by YetiSearch" link |
Available Blocks – Full Modal
| Block | Description |
|---|---|
config_script |
Inline <script> for endpoint/theme detection |
modal |
Entire modal container |
modal_data_attrs |
Empty slot for extra data-* attributes on the root <div> |
backdrop |
Backdrop overlay |
search_input |
Input row (icon + input + filters + cancel button) |
body |
Body container (left + right panels) |
results_panel |
Left panel (meta + list + pagination) |
preview_panel |
Right panel (breadcrumbs + title + excerpt) |
item_template |
<template> element for result item cloning |
Example: Theme Override
A minimal theme override that injects a custom data attribute:
{# themes/my-theme/templates/partials/yetisearch-pro/modal-full.html.twig #}
{% extends 'partials/yetisearch-pro/base/modal-full.html.twig' %}
{% block modal_data_attrs %}
data-ys-extra-params="filter[version][eqor]={{ some_version|url_encode }}"
{% endblock %}
This passes extra query parameters to the search endpoint without touching any other part of the modal.
Extra Query Parameters (data-ys-extra-params)
Add a data-ys-extra-params attribute to the modal's root element to append arbitrary parameters to every search request. The value is appended as-is to the fetch URL:
<!-- Rendered output -->
<div id="ys-modal" data-ys-extra-params="filter[version][eqor]=17">
The plugin's JS reads this attribute at init and appends it to every search URL:
/ys?q=twig&page=1&type=content&ajax=1&filter[version][eqor]=17
The modal_data_attrs block is the recommended way to set this from Twig.
Example: Replacing the Preview Panel
{% extends 'partials/yetisearch-pro/base/modal-full.html.twig' %}
{% block preview_panel %}
<div class="ys-right custom-preview">
{# Your custom preview layout #}
</div>
{% endblock %}
Example: Adding Content After Results
{% extends 'partials/yetisearch-pro/base/modal-simple.html.twig' %}
{% block footer %}
<div class="my-custom-footer">
Custom footer content
</div>
{{ parent() }}
{% endblock %}
Use {{ parent() }} to include the original block content alongside your additions.
Page-Level Controls
Frontmatter Options
Control indexing per-page via frontmatter:
yetisearch:
index-page: false # Exclude this page from the index
ignore: true # Same as index-page: false
index-children: false # Exclude child pages from the index
Ignore Shortcode
Exclude specific content blocks from indexing while keeping them visible on the page:
[yetisearch=ignore]
This content will be visible but not indexed for search.
[/yetisearch]
Default Document Fields
Each indexed document contains:
- Content fields:
title,content,excerpt,url,route, taxonomy-derivedtags,category - Metadata:
taxonomy,meta,breadcrumbs,date,updated_at - Language:
language(set per-page for single-index strategy) - Stable ID:
<lang>:<route>
Admin Dashboard
YetiSearch Pro includes a full admin dashboard accessible from the sidebar in the Admin plugin.
Index Dashboard
Monitor index health and trigger maintenance actions:
- View total documents, index sizes, and last updated timestamps
- Reindex all content with progress tracking
- Clear and reset indexes
Search Analytics
Track search engagement and performance:
- Total and unique queries
- Result clicks and zero-result searches
- Average response time
- Popular searches and top clicked results
- Frequent zero-result queries (to identify content gaps)
Index Browser
Inspect and manage indexed documents:
- Browse all documents with pagination
- Search within the index
- View individual document details with full JSON
- Edit document data directly
- Debug index statistics and frequent terms
- Optimize indexes
Permissions
YetiSearch Pro registers granular admin permissions:
yetisearch-pro.view- View search dashboard and statisticsyetisearch-pro.modify- Trigger reindex and manage indexesyetisearch-pro.admin- Full access including configuration changes
CLI Commands
All commands are run from the Grav site root.
Index Command
bin/plugin yetisearch-pro index [options]
Options:
-x, --index=INDEX Index key (e.g., pages) [default: pages]
-l, --lang=LANG Language/Version (e.g., 17)
-f, --flush Drop and recreate the target index before indexing
-q, --quiet Minimal output
-r, --raw Raw JSON status
Examples:
# Full rebuild for one language
bin/plugin yetisearch-pro index --index pages --lang 17 --flush
# Full rebuild for all languages (per_language)
bin/plugin yetisearch-pro index --index pages --flush
# Index a specific suffixed index
bin/plugin yetisearch-pro index --index pages_17 --flush
Query Command
bin/plugin yetisearch-pro query QUERY [options]
Core:
-x, --index=INDEX Index key or suffixed key
-l, --lang=LANG Language/Version
-p, --page=PAGE Page number (default: 1)
--per-page=N Results per page (default: 10)
--compact Compact output
-r, --raw Raw JSON output
Search behavior:
--fuzzy Enable fuzzy search
--fuzziness=F Fuzziness score (0..1, default: 0.8)
--fields=LIST Comma-separated fields to search
-s, --sort=SPEC Sort spec, e.g. "title:asc,updated_at:desc"
-F, --filter=EXPR Filter: field[op]value (op: =,!=,>,>=,<,<=)
--no-highlight Disable highlight markup
--highlight-length=N Snippet window length (default: 75)
-d, --distinct[=BOOL] Distinct per route (default: true)
--suggestions Show suggestions after results
--suggestions-limit=N Suggestion count (default: 10)
Examples:
# Fuzzy search in a per-language index
bin/plugin yetisearch-pro query "install theme" --index pages_17 --fuzzy
# Search with language in single-index strategy
bin/plugin yetisearch-pro query "installer" --index pages --lang fr
# With filters and sort
bin/plugin yetisearch-pro query "config" -x pages_17 -F "taxonomy.category=plugins" -s "title:asc"
Schedule Command
bin/plugin yetisearch-pro schedule [options]
Options:
-l, --list Display configured schedule summary
-t, --task=TASK Task to run: reindex, maintenance, or all (default: all)
-f, --force Run even if the task is disabled
Cache Command
bin/plugin yetisearch-pro cache [ACTION] [options]
Actions:
stats Display cache statistics (default)
monitor Monitor cache activity in real-time
purge Clear all cache entries
warm Pre-populate cache with common queries
Options:
-i, --index=INDEX Index name (auto-detected if omitted)
-d, --details Show detailed cache entries
Query Endpoint & Filters
The JSON query endpoint (query_route, default /ys) is handled during onPluginsInitialized when the request explicitly asks for JSON (Accept: application/json, ?ajax=1, or .json). This short-circuits page initialization for fast typeahead and API responses.
Filter DSL
Pass filters via the filter query parameter using the syntax field[op]value:
/ys?q=search+terms&filter=taxonomy.category[=]plugins&filter=version[=?]v3
Supported operators: =, !=, >, >=, <, <=, =? (match value OR null).
Suggestions Endpoint
A lightweight suggestions endpoint is available at <query_route>/suggest (e.g., /ys/suggest):
- Parameters:
termorq(input string),limit(default: 10) - Returns JSON array of
{ text, score, count }with minimal Grav overhead
Events (Extensibility)
These Grav events allow custom indexing and document shaping:
onYetisearchCollectObjects(index, lang, &documents)- Add or modify non-page documents to indexonYetisearchBuildDocument(subject, lang, index, &doc)- Shape fields per document (e.g., add custom frontmatter fields, facets, geo data)onYetisearchProBeforeSearch(query, lang, &filters, &options)- Add custom filters or modify search options before a query is executedonYetisearchPageSkip(page, index, lang)- Decide whether to skip a page during indexing
Example: Adding Custom Fields
public function onYetisearchBuildDocument(Event $event)
{
$page = $event['subject'];
$doc = &$event['doc'];
// Add a custom field from page headers
$doc['author'] = $page->header()->author ?? '';
}
Example: Indexing FlexObjects
public function onYetisearchCollectObjects(Event $event)
{
$documents = &$event['documents'];
// Add FlexObjects or other custom content
$flex = $this->grav['flex'];
$collection = $flex->getCollection('my-objects');
foreach ($collection as $object) {
$documents[] = [
'id' => 'flex:' . $object->getKey(),
'title' => $object->getProperty('title'),
'content' => $object->getProperty('description'),
'url' => $object->url(),
];
}
}
Helios Theme Integration
YetiSearch Pro and the Helios documentation theme are designed to work together. Helios includes built-in YetiSearch Pro support with the Cmd+K / Ctrl+K keyboard shortcut pre-configured.
To enable YetiSearch Pro in Helios, set the search provider in your Helios configuration:
# user/config/themes/helios.yaml
search:
provider: yetisearch
Tips & Best Practices
Indexing
- Run
--flushfor the first index or after major content restructuring - Use the Grav Scheduler for automated reindexing on production sites
- Enable real-time updates for sites where content changes frequently in Admin
Search Tuning
- Adjust
boostweights to prioritize title and header matches over body content - Use
rerank.demote_prefixesto push less relevant sections (like API references) lower in results - Enable fuzzy search for better typo tolerance, but tune
typo_toleranceto avoid too many false positives
Performance
- The
singleindex strategy is simpler and works well for most sites - Use
per_languagestrategy for large multi-language sites where index size matters - Configure
max_batch_sizebased on your server's memory constraints - Use the cache warm command to pre-populate frequently searched terms