๐ Client-side search for MediaWiki.
SifterSearch adds a fast, fully client-side search box to a MediaWiki site, backed by a Pagefind index. The index is a set of static files and every query runs in the browser, so there is no search server and no database query at request time.
It was built for Wikven, a generator that exports a MediaWiki as a static site where there is no backend to answer searches, so search has to run entirely in the browser. Nothing ties it to Wikven, though: it works on any MediaWiki wiki, whether live or statically exported, and suits smaller sites that want search without running a search server.
The index is built from page content, not by crawling a deployed site, so the same path serves a live wiki and a static export:
- When an indexed page changes, SifterSearch queues a rebuild job. The job de-duplicates, so a burst of edits โ or a bulk import during a static build โ collapses into a single rebuild.
- The job keeps the rendered HTML of indexed pages in a cache directory and
re-renders only what changed since the last run, then runs the bundled
Pagefind binary over the cache to write the
pagefind/bundle to$wgSifterSearchOutputDir. On a live wiki the queue runs via your normal job runner; on a static build it is drained by the build'srunJobsstep, so SifterSearch needs no knowledge of the build pipeline. - On every page, the client feeds the skin's own search box from the bundle
rather than replacing it: Vector 2022's and Minerva's Codex typeaheads are
mounted on Pagefind, and skins using core's
mediawiki.searchSuggestkeep the classic suggestions widget with Pagefind behind it. On the page named by$wgSifterSearchResultsPage, if one is configured, it also mounts Pagefind's full results UI, and sends both the search form and the "search for pages containing" affordance there.
The indexer is a self-contained binary โ no Node.js or other runtime is needed.
The extension auto-detects the bundled binary for the running platform (see
bin/README.md). The default distribution ships only the most
common server platform; on another platform, drop the matching binary in bin/
or set $wgSifterSearchPagefindBinary.
| Setting | Default | Description |
|---|---|---|
$wgSifterSearchOutputDir |
"" |
The Pagefind bundle directory itself, served at the bundle path (e.g. <docroot>/pagefind). Empty disables indexing. |
$wgSifterSearchBundlePath |
/pagefind/ |
URL path the client loads the bundle from. Its parent is taken as the site root, which is what anchors a results-page URL a host answers document-relative. |
$wgSifterSearchCacheDir |
"" |
Rendered-HTML cache for incremental rebuilds. Defaults to a subdirectory of $wgCacheDirectory. |
$wgSifterSearchFullText |
true |
Whether the wiki answers a full-text search of its own. Set false where it does not, e.g. a static export: with no results page either, the "search for pages containing" affordance is dropped and a plain submit goes to the top result. |
$wgSifterSearchResultsPage |
"" |
Title of a content page to mount the full results UI on, driven by ?search=. Where it is set, the search form and the "search for pages containing" affordance both lead there. |
$wgSifterSearchNamespaces |
[ NS_MAIN ] |
Namespace IDs to index. |
$wgSifterSearchPagefindBinary |
"" |
Override the Pagefind binary path. Empty auto-detects bin/. |
$wgSifterSearchBatchSeconds |
0 |
Delay rebuilds so bursts coalesce into one batch. |
Asked once per page before it is indexed. Every page of the configured namespaces is indexed by default, which is the only thing SifterSearch can decide on its own: it sees titles and content, not what the wiki means by them. A wiki that knows one of its pages is not worth answering with โ a duplicate of another under a second title, a page kept for machinery rather than readers โ says so here.
public function onSifterSearchIndexPage( Title $title, bool &$index ) {
if ( $this->isADuplicateOfAnotherPage( $title ) ) {
$index = false;
}
}A page turned away is dropped from the index the way a deleted one is, so a wiki may change its mind: the next run carries it out either way.
GPL-3.0-or-later