Search
The theme adds built-in support for @vuepress/plugin-docsearch, @vuepress/plugin-meilisearch, @vuepress/plugin-slimsearch and @vuepress/plugin-search. Just install the plugin you want and config it, you will get a search box in navbar.
Use @vuepress/plugin-docsearch
You need to submit the URL of your site to join the DocSearch program.
The DocSearch team will send apiKey and indexName to your email once the index is generated. Then you can configure this plugin to enable DocSearch in VuePress.
Alternatively, you can run your own crawler to generate the index, and then use your own appId, apiKey and indexName to configure this plugin.
Set up the Algolia Crawler correctly according to the following requirements. You should go to Algolia Crawler to update your crawler config.
Setting Crawler Config
Here are config options and descriptions:
new Crawler({ appId: "YOUR_APP_ID", apiKey: "YOUR_API_KEY", rateLimit: 8, startUrls: [ // These are urls which algolia start to craw // If your site is divided in to multiple parts, // you may want to set multiple entry links "https://YOUR_WEBSITE_URL/", ], sitemaps: [ // if you are using sitemap plugins (e.g.: @vuepress/plugin-sitemap), you may provide one "https://YOUR_WEBSITE_URL/sitemap.xml", ], ignoreCanonicalTo: false, exclusionPatterns: [ // You can use this to stop algolia crawling some paths ], discoveryPatterns: [ // These are urls which algolia looking for, "https://YOUR_WEBSITE_URL/**", ], // Crawler schedule, set it according to your docs update frequency schedule: "at 02:00 every 1 day", actions: [ // you may have multiple actions, especially when you are deploying multiple docs under one domain { // name the index with name you like indexName: "YOUR_INDEX_NAME", // paths where the index take effect pathsToMatch: ["https://YOUR_WEBSITE_URL/**"], // controls how algolia extracts records from your site recordExtractor: ({ $, helpers }) => { // The following are the default options for vuepress-theme-hope return helpers.docsearch({ recordProps: { lvl0: { selectors: [".vp-sidebar-link.active", "[vp-content] h1"], defaultValue: "Documentation", }, lvl1: "[vp-content] h1", lvl2: "[vp-content] h2", lvl3: "[vp-content] h3", lvl4: "[vp-content] h4", lvl5: "[vp-content] h5", lvl6: "[vp-content] h6", content: "[vp-content] p, [vp-content] li", }, recordVersion: "v3", }); }, }, ], initialIndexSettings: { // controls how index are initialized // only has effects before index are initialize // you may need to delete your index and recraw after modification YOUR_INDEX_NAME: { attributesForFaceting: ["type", "lang"], attributesToRetrieve: ["hierarchy", "content", "anchor", "url"], attributesToHighlight: ["hierarchy", "hierarchy_camel", "content"], attributesToSnippet: ["content:10"], camelCaseAttributes: ["hierarchy", "hierarchy_radio", "content"], searchableAttributes: [ "unordered(hierarchy_radio_camel.lvl0)", "unordered(hierarchy_radio.lvl0)", "unordered(hierarchy_radio_camel.lvl1)", "unordered(hierarchy_radio.lvl1)", "unordered(hierarchy_radio_camel.lvl2)", "unordered(hierarchy_radio.lvl2)", "unordered(hierarchy_radio_camel.lvl3)", "unordered(hierarchy_radio.lvl3)", "unordered(hierarchy_radio_camel.lvl4)", "unordered(hierarchy_radio.lvl4)", "unordered(hierarchy_radio_camel.lvl5)", "unordered(hierarchy_radio.lvl5)", "unordered(hierarchy_radio_camel.lvl6)", "unordered(hierarchy_radio.lvl6)", "unordered(hierarchy_camel.lvl0)", "unordered(hierarchy.lvl0)", "unordered(hierarchy_camel.lvl1)", "unordered(hierarchy.lvl1)", "unordered(hierarchy_camel.lvl2)", "unordered(hierarchy.lvl2)", "unordered(hierarchy_camel.lvl3)", "unordered(hierarchy.lvl3)", "unordered(hierarchy_camel.lvl4)", "unordered(hierarchy.lvl4)", "unordered(hierarchy_camel.lvl5)", "unordered(hierarchy.lvl5)", "unordered(hierarchy_camel.lvl6)", "unordered(hierarchy.lvl6)", "content", ], distinct: true, attributeForDistinct: "url", customRanking: [ "desc(weight.pageRank)", "desc(weight.level)", "asc(weight.position)", ], ranking: [ "words", "filters", "typo", "attribute", "proximity", "exact", "custom", ], highlightPreTag: '<span class="algolia-docsearch-suggestion--highlight">', highlightPostTag: "</span>", minWordSizefor1Typo: 3, minWordSizefor2Typos: 7, allowTyposOnNumericTokens: false, minProximity: 1, ignorePlurals: true, advancedSyntax: true, attributeCriteriaComputedByMinProximity: true, removeWordsIfNoResults: "allOptional", }, }, });Warning
initialIndexSettings.YOUR_INDEX_NAME.attributesForFacetingfield must contain"lang", otherwise the plugin will not work properly.Install
@vuepress/plugin-docsearchpnpmpnpm add -D @vuepress/plugin-docsearch@nextyarnyarn add -D @vuepress/plugin-docsearch@nextnpmnpm i -D @vuepress/plugin-docsearch@nextCustomize the plugin with
plugins.docsearchin theme options.import {hopeTheme} from "vuepress-theme-hope"; export defaulthopeTheme({plugins: {docsearch: { // plugin options here // appId, apiKey and indexName are required }, }, });
More
See plugin docs for how to use docsearch plugin and its available options.
Use @vuepress/plugin-slimsearch
Install
@vuepress/plugin-slimsearchpnpmpnpm add -D @vuepress/plugin-slimsearch@nextyarnyarn add -D @vuepress/plugin-slimsearch@nextnpmnpm i -D @vuepress/plugin-slimsearch@nextCustomize
plugins.slimsearchin theme options.You can set
plugins.slimsearchtotrueto enable it directly, or set it to an object to customize the plugin.import {hopeTheme} from "vuepress-theme-hope"; export defaulthopeTheme({plugins: {slimsearch: { // plugin options here }, // or slimsearch: true, }, });
More
See plugin docs for available options.
Use @vuepress/plugin-meilisearch
Tips
This requires you to have a own server with docker to run MeiliSearch.
First pull latest MeiliSearch docker image:
docker pull getmeili/meilisearch:latestStart the docker:
docker run -it --rm \ # set container name to "MeiliSearch" --name MeiliSearch \ # set your own master key # replace <YOUR_MASTER_KEY> with your own master key -e MEILI_MASTER_KEY='<YOUR_MASTER_KEY>' \ # switch to production mode -e MEILI_ENV=production \ # disable meilisearch analytics -e MEILI_NO_ANALYTICS=1 \ # mapping 7700 port to host -p 7700:7700 \ # mounting index database to host # you can change the path to anywhere you want -v $(pwd)/meili_data:/meili_data \ getmeili/meilisearch:latestHere
<YOUR_MASTER_KEY>is the master key for MeiliSearch that you should set yourself (required >= 16 bytes), which is used to access the MeiliSearch API.Never expose Master Key
Search key can be generated for public access, which only allows search operations.
Your Master Key should only be used for internal server access (including scraping), as it grants full operational permissions. Do not mix use them and never expose this key!
Pull the latest MeiliSearch Scraper image:
docker pull getmeili/docs-scraper:latestCreate a
scraper.jsonfile with the following content on your sever:{ "index_uid": "<YOUR_INDEX_NAME>", "start_urls": ["https://<YOUR_WEBSITE_URL>/"], "sitemap_urls": ["https://<YOUR_WEBSITE_URL>/sitemap.xml"], "selectors": { "lvl0": { "selector": "[vp-content] h1", "global": true, "default_value": "Documentation" }, "lvl1": "[vp-content] h1", "lvl2": "[vp-content] h2", "lvl3": "[vp-content] h3", "lvl4": "[vp-content] h4", "lvl5": "[vp-content] h5", "lvl6": "[vp-content] h6", "content": "[vp-content] p, [vp-content] li", "lang": { "selector": "/html/@lang", "global": true, "type": "xpath" } }, "custom_settings": { "searchableAttributes": [ "hierarchy_radio_lvl0", "hierarchy_radio_lvl1", "hierarchy_radio_lvl2", "hierarchy_radio_lvl3", "hierarchy_radio_lvl4", "hierarchy_radio_lvl5", "hierarchy_lvl0", "hierarchy_lvl1", "hierarchy_lvl2", "hierarchy_lvl3", "hierarchy_lvl4", "hierarchy_lvl5", "hierarchy_lvl6", "content", "lang", "objectID", "page_rank", "level", "position" ], "displayedAttributes": [ "hierarchy_radio_lvl0", "hierarchy_radio_lvl1", "hierarchy_radio_lvl2", "hierarchy_radio_lvl3", "hierarchy_radio_lvl4", "hierarchy_radio_lvl5", "hierarchy_lvl0", "hierarchy_lvl1", "hierarchy_lvl2", "hierarchy_lvl3", "hierarchy_lvl4", "hierarchy_lvl5", "hierarchy_lvl6", "anchor", "url", "lang", "content", "objectID" ], "filterableAttributes": ["lang"] } }index_uidshould be a unique name for your index, which will be used to search.start_urlsandsitemap_urls(optional) shall be customized according to the website to be scraped.selectorsfield can be customized according to third-party theme DOM structure.- You can add new fields to
custom_settingsaccording to your needs.
Requirements for the configuration file
To let the plugin work:
langselector must be kept as is inselectorsfiled- All fields that are currently in
custom_settingsmust not be removed.
Make sure MeiliSearch is currently running, then start scraping the document by running the docker:
docker run -t --rm \ --network=host \ -e MEILISEARCH_HOST_URL='<MEILISEARCH_HOST_URL>' \ -e MEILISEARCH_API_KEY='<MEILISEARCH_MASTER_KEY>' \ -v <absolute-path-to-your-config-file>:/docs-scraper/config.json \ getmeili/docs-scraper:latest pipenv run ./docs_scraper config.jsonHere:
<MEILISEARCH_HOST_URL>should be the host URL of your MeiliSearch instance<MEILISEARCH_MASTER_KEY>shall be the master key you provided.<absolute-path-to-your-config-file>is the absolute path to the configuration file you created above.
When the scraper completes, MeiliSearch will update the existing index with latest document content.
Use the following command to create a search-only access key:
curl \ # Replace <YOUR_HOST> with your MeiliSearch host URL # You may need to bind a hostname and ssl certificate via reverse proxy -X POST '<YOUR_HOST>/keys' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer <MASTER_KEY>' \ # description f --data-binary '{ "indexes": ["<YOUR_INDEX_NAME>"], "actions": ["search"], "expiresAt": null, "description": "Search key for <YOUR_INDEX_NAME>" }'Here:
<YOUR_HOST>is the host URL of your MeiliSearch instance<MASTER_KEY>is the master key generated by MeiliSearch<YOUR_INDEX_NAME>is the name of the index you createdactionsspecifies the actions that this key can perform. In this case, it is set to["search"], which means it can only perform search operations.expiresAtsets the expiration date for the key, allowing you to control how long the key remains valid,nullmeans it will never expire.
If successful, the response would look like this:
{ "name": null, "description": "Search key for <YOUR_INDEX_NAME>", "key": "adaf72e2a6d6f428ec465bc786ec41de868bbd53121997e89ba2299e9566c88213", "uid": "b84d1be5-caa5-4752-b078-8f40be39051d", "actions": ["search"], "indexes": ["<YOUR_INDEX_NAME>"], "expiresAt": null, "createdAt": "2024-01-27T06:50:33.668329328Z", "updatedAt": "2024-01-27T06:50:33.668329328Z" }Now, you can use the
keyin the plugin configurationInstall
@vuepress/plugin-meilisearchpnpmpnpm add -D @vuepress/plugin-meilisearch@nextyarnyarn add -D @vuepress/plugin-meilisearch@nextnpmnpm i -D @vuepress/plugin-meilisearch@nextSet plugin options in theme config.
import {hopeTheme} from "vuepress-theme-hope"; export defaulthopeTheme({plugins: {meilisearch: {host: "<MEILISEARCH_HOST_URL>",apiKey: "<YOUR_SEARCH_ONLY_KEY>",indexUid: "<YOUR_INDEX_NAME>", }, }, });Automatic Re-scraping with Github Actions
Place your scraper config file somewhere in your project.
Then go to
Settings->Secrets and variables->Actionsin your Github repository. ClickNew repository secretand setMEILISEARCH_MASTER_KEYwith your meilisearch master key.Next add a new step
scrapein your Github Actions workflow file, which will run after the deployment step. Here is an example of how to do this:name: Deploy and Scrape on: push: branches: - main jobs: deploy: runs-on: ubuntu-latest steps: # deploy your documentation here # ... scrape: needs: deploy runs-on: ubuntu-latest name: re-scrape documentation for Meilisearch steps: - name: Checkout uses: actions/checkout@v4 - name: Run scraper env: # replace with your own MeiliSearch host URL HOST_URL: <YOUR_MEILISEARCH_HOST_URL> API_KEY: ${{ secrets.MEILISEARCH_MASTER_KEY }} # replace it with the path to your config file CONFIG_FILE_PATH: ${{ github.workspace }}/<path/to/your/scraper/config.json> run: | docker run -t --rm \ -e MEILISEARCH_HOST_URL=$HOST_URL \ -e MEILISEARCH_API_KEY=$API_KEY \ -v $CONFIG_FILE_PATH:/docs-scraper/config.json \ getmeili/docs-scraper:latest pipenv run ./docs_scraper config.jsonKey for Scraper
To secure your MeiliSearch instance, you can create a new key with limited permissions for the scraper. Similar to search key above, this key should only have access to these actions:
["indexes.create","indexes.delete","settings.update","documents.add"]
More
See plugin docs for available options.
Use @vuepress/plugin-search
Install
@vuepress/plugin-searchpnpmpnpm add -D @vuepress/plugin-search@nextyarnyarn add -D @vuepress/plugin-search@nextnpmnpm i -D @vuepress/plugin-search@nextCustomize
plugins.searchin theme options.import {hopeTheme} from "vuepress-theme-hope"; export defaulthopeTheme({plugins: {search: { // plugin options here }, // or search: true, }, });
More
See plugin docs for available options.
Changelog
05fbb-onefb50-on2aada-one639c-on22787-on2c32a-ona4c54-onbdfcb-on7948f-onb41c0-on0fe94-on54c46-on497db-on25d44-onbac94-one8287-onb54b7-onc8d60-on77535-on9a16a-onacd61-on57f4f-on2243a-on2fa50-on223f7-on23515-on9cdd7-on63d09-on1ca3e-onad52b-onc73e6-oncc994-onfd395-on290ab-on716f9-on0c093-on60006-on4a252-on1ed3a-onf6ff0-on