Build a better search in Vuepress

Build a better search in Vuepress

Vuepress default theme provides a somehow good search capabilities for small sites, it works out of the box, but it is not too reliable, it searches the headers only and doesn't help a lot in finding the exact result you are aiming for, I faced this issue with a Vuepress theme that I am building, so here I will let you know how I handled this situation.

Using Algolia to index your site

Algolia define themselves as "Fast, reliable and modern search and discovery", it is a service that helps your site to provide better search functionality, their services vary from site indexing to API indexing, to query ranking, to Voice search, etc...

Vuepress is integrated by default with Algolia with a service called Doc Search, you can create an account and request to index your documentation site, this is pretty good service for Open-source software documentation sites, and it is being used by different popular sites such as:

I submitted one site but they rejected it, but if you were lucky you can add it to your site by following the official documentation here 👋

Using Google custom search engine

My second option was Google custom search engine, it can give you google's superpowers, it works great in medium to large static sites, but it has some ads if you are OK with them, Or you can use it over JSON API, but it is not free, it works really well, but I saw that it as Over-Kill 🔪

Using a local search library

I found plenty of other third-party providers, but I decided to build my own local search index, but thanks to FlexSearch I did not need to re-invent the wheel, FlexSearch define themselves as "Web's fastest and most memory-flexible full-text search library with zero dependencies.", I found that is true!

here I will show you how I've built my custom search engine within a few lines!

first I installed FlexSearch

npm install nextapps-de/flexsearch

Then in my Search Component, I used it as follows.

// SearchBox.vue

<template>
  ...
</template>

<script>
import Flexsearch from "flexsearch";

export default {
  data () {
    return {
      index: null,
      query: ""
    };
  },
  // In Vuepress you should use mounted not Created life-cycle hook, otherwise it will throw an error.
  mounted () {
    this.index = new Flexsearch({
      tokenize: "forward",
      doc: {
        id: "key",
        // here you choose the fields you want to index.
        // for me I will search in the title and the content of each page.
        // of course I stripped the content before so I use the plain text content not the markdown text
        field: ["title", "content"]
      }
    });
    // Vuepress injects the $site global variable in the Vue instance, you can get the pages array from the $site object
    const { pages } = this.$site;
    // finally you add the pages to the FlexSearch index.
    this.index.add(pages);
  },
  methods: {
    querySearchAsync (queryString, cb) {
      const { pages, themeConfig } = this.$site;
      const query = queryString.trim().toLowerCase();
      const max = 10;

      if (this.index === null || query.length < 3) {
        return cb([]);
      }

      // here we use the search method that FlexSearch provides.
      this.index.search(
        query,
        {
          limit: max,
          threshold: 2,
          // for other types of encoding refer to https://github.com/nextapps-de/flexsearch#encoders
          encode: 'extra'
        },
        // this callback gives you the all pages that contains the search term "query"
        (result) => {
          if (result.length) {
            // getting the value and link of each page that contains the search term
            const resolvedResult = result.map(page => {
              return {
                link: page.path,
                value: this.getQuerySnippet(page)
              };
            });
            return cb(resolvedResult);
          } else {
            return cb([{ value: `No results! Try something else.`, link: `#` }]);
          }
        }
      );
    }
  }
};
</script>

and that is it! you are free to use this code snippet, reading the readme of FlexSearch will be enough for you to get the best results you want, please give it a look for more customizations.

Read More