Skip to main content

Fulltext Search with Meilisearch


Fulltext Search with Meilisearch

Scope of this page. This is a short public overview of how the documentation site's search is wired up. Operational detail — the Meilisearch server setup, the Apache reverse proxy, master-key handling, and the GitLab CI/CD variables — lives in the ICDP-internal wiki under "Meilisearch ops". Only the client-side and repository-facing pieces are described here.

Problem

The default VuePress search (@vuepress/plugin-search) only indexes page titles and headings, not body text. For ~300 documentation pages that is too shallow. A self-hosted Meilisearchopen in new window instance now provides full-text search over the rendered HTML, with results scoped to each page's main content.

What is used and why

PieceChoiceReason
Search engineMeilisearch (self-hosted, behind an Apache reverse proxy)Fast, self-hosted, no third-party credentials to lose.
Browser widget@algolia/autocomplete-jsopen in new window + @meilisearch/autocomplete-clientopen in new windowRenders the always-visible input with a dropdown of live results — the same UX pattern the old header-only search had, so users do not have to learn a new interaction. A separate search page is intentionally not built.
IndexingNode script in .vuepress/scripts/index-meilisearch.mjs, run as a CI stageReindexes the live site on every successful deploy.

Note: meilisearch-docsearch (the Algolia-DocSearch-v3-style package) was considered and rejected — it renders a button that opens a modal overlay, not an inline dropdown, and its container/placeholder API is frequently misdocumented online.

How it is wired into the repo

  1. Client widget.vuepress/components/Docsearch.vue renders an <div id="meili-autocomplete"> and initialises Algolia Autocomplete inside onMounted (client-side only; the heavy libraries are dynamically imported so they stay out of the SSR bundle). It is registered globally under the name Docsearch, which is the component name theme-hope's navbar already probes for in its "Search" navbar slot. No layout change is needed.

  2. Build-time configuration.vuepress/config.shared.ts declares a small meilisearch-define plugin that forwards three values to the client bundle via the bundler's define hook:

    • MEILISEARCH_HOST — public URL of the Meilisearch reverse proxy.
    • MEILISEARCH_SEARCH_API_KEY — a search-only key. This is safe to expose: it can only run search on the docs index.
    • MEILISEARCH_INDEX_UID — index name (e.g. docs).

    The master key is never sent to the browser; it is only used by the CI indexer job.

  3. Old search removed@vuepress/plugin-search was uninstalled and its registration deleted, so the navbar shows one search box, not two.

  4. Indexer.vuepress/scripts/index-meilisearch.mjs walks the built dist/, extracts each page's <h1>/<h2> hierarchy and body text (scoped to .theme-hope-content), and pushes documents to Meilisearch in batches of 100. It runs as the index search GitLab CI stage, after the HTML is deployed, with allow_failure: true so a Meilisearch outage never blocks a documentation release.

PDF build is unaffected

The vuepress-export-pdf CI job (.gitlab-ci.yml) overwrites .vuepress/client.ts with client.vuepress-export.ts and switches to @vuepress/theme-default before building, so the Docsearch component is neither registered nor rendered in the PDF. The 500-page offline PDF therefore contains no search box, as intended.

Operations (see internal wiki)

Server-side topics that do not belong in this public repo:

  • Running Meilisearch under systemd / Docker and persisting /meili_data.
  • The Apache ProxyPass snippet that exposes Meilisearch same-origin under https://data.icdp-online.org/mdis-docs/search/ (avoids CORS entirely).
  • Creating the search-only API key scoped to actions:["search"] on the docs index, and rotating it.
  • The four GitLab CI/CD variables that must be defined in the project settings: MEILISEARCH_HOST, MEILISEARCH_SEARCH_API_KEY, MEILISEARCH_MASTER_KEY (masked + protected), MEILISEARCH_INDEX_UID.
  • Backing up the data directory and the master key.

Local development

Without the env vars set, Docsearch.vue logs a warning and renders nothing, so npm run dev / npm run build work offline. To exercise the widget locally against a running Meilisearch:

export MEILISEARCH_HOST=http://localhost:7700
export MEILISEARCH_SEARCH_API_KEY=<your-search-only-key>
export MEILISEARCH_INDEX_UID=docs
npm run dev

To dry-run the indexer against a built dist/ without contacting Meilisearch:

DRY_RUN=1 DIST_DIR=./.vuepress/dist node .vuepress/scripts/index-meilisearch.mjs

Maintenance checklist

  • [ ] Monitor the index search CI job for failures.
  • [ ] Rotate the search-only API key periodically (cheap; just update the CI variable and redeploy).
  • [ ] Upgrade the Meilisearch binary/container and the @algolia/autocomplete-* / @meilisearch/* npm packages together, since their APIs move in lockstep.
  • [ ] Back up /meili_data and the master key off the server.