mrkaluzny homepage
Tips & Tricks

Full-text search with Gatsby and Decap CMS

Nov 3, 2020

Full-text search refers to techniques for searching a single document or a collection in a full-text database. Full-text search is distinguished from searches based on metadata or on parts of the original texts represented in databases (such as titles, abstracts, selected sections, or bibliographical references).

Full-text search allows users to retrieve higher quality results than using other methods. During the full-text search, a search engine examines all of the words in every stored document as it tries to match search criteria.

Implementing a full-text search into Gatsby is quite painless to set up. I've experimented with different plugins but decided to use Flexsearch (easiest to implement and set up).

Prerequisites

In this example, I'm presenting an implementation for Gatsby's website using Decap CMS to manage content. When using other CMS, the implementation may vary.

First, we need to install gatsby-plugin-flexsearch

$ yarn add gatsby-plugin-flexsearch

After that, we have to initialize the plugin in gatsby-config and configure which attributes we want to include.

{
      resolve: 'gatsby-plugin-flexsearch',
      options: {
        languages: ['en'],
        type: 'MarkdownRemark',
        fields: [
          {
            name: 'id',
            indexed: false,
            resolver: 'id',
            store: true,
          },
          {
            name: 'html',
            indexed: true,
            resolver: 'internal.content',
            attributes: {
              encode: 'balance',
              tokenize: 'strict',
              threshold: 0,
              resolution: 3,
              depth: 3,
            },
            store: true,
          },
          {
            name: 'title',
            indexed: true,
            resolver: 'frontmatter.title',
            attributes: {
              encode: 'extra',
              tokenize: 'full',
              threshold: 1,
              resolution: 3,
            },
            store: true,
          },
          {
            name: 'description',
            indexed: true,
            resolver: 'frontmatter.description',
            attributes: {
              encode: 'icase',
              tokenize: 'forward',
              threshold: 2,
              depth: 3,
            },
            store: true,
          },
          {
            name: 'type',
            indexed: false,
            resolver: 'frontmatter.type',
            store: true,
          },
          {
            name: 'slug',
            indexed: false,
            resolver: 'fields.slug',
            store: true,
          },
          {
            name: 'layout',
            indexed: false,
            resolver: 'frontmatter.layout',
            store: true,
          },
        ],
      },
    },

In the example above, I declared custom attributes to be indexed by flex search (id, layout, and slug) that are important for displaying results snippets and have to be available from the search component.

After restarting the development server flex search index and the store will be attached to the window object of your browser.

Search component with snippets

Right now, our Gatsby website will have flex search enabled. So far, so good! Now we have to enable users to perform searches against our content.

To do that, we need a search component! The most important part is the search function itself. If you want to check out the full component take a look at this gist

getSearchResults(query) {
    var index = window.__FLEXSEARCH__.en.index
    var store = window.__FLEXSEARCH__.en.store
    if (!query || !index) {
      return []
    } else {
      var results = []
      Object.keys(index).forEach((idx) => {
        results.push(...index[idx].values.search(query))
      })

      results = Array.from(new Set(results))

      var nodes = store
        .filter((node) => (results.includes(node.id) ? node : null))
        .map((node) => node.node)

      return nodes
    }
  }

To search our documents, we can create a simple function that takes the user's input and performs a search using the Flexsearch index and store.

First, we create an array for results. Then we can check each index for the querying user searched for and return the matched values. Then we're using the results array to filter Flexsearch store and return objects that are matching our query. The function is returning nodes containing information about documents that matched the query.

These nodes can then be used to render snippets for each search result. You can check out the full implementation on Dionysus website we worked on last month


Psst! If you're looking for a great Gatsby starter with Tailwind and Decap CMS you have to check out Henlo