惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
DataBreaches.Net
IT之家
IT之家
The Cloudflare Blog
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
N
Netflix TechBlog - Medium
阮一峰的网络日志
阮一峰的网络日志
P
Proofpoint News Feed
L
LangChain Blog
博客园 - Franky
美团技术团队
J
Java Code Geeks
Microsoft Security Blog
Microsoft Security Blog
博客园 - 叶小钗
小众软件
小众软件
Y
Y Combinator Blog
B
Blog RSS Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
Hugging Face - Blog
Hugging Face - Blog
Jina AI
Jina AI
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
Vercel News
Vercel News

Example blog

Ghost routing explained with routes.yaml examples — routes, collections & taxonomies Ghost redirects explained with practical redirects.yaml examples How to Serve JSON or XML Data in Ghost How to Change Your Default Post Template in Ghost Ghost Custom Theme Settings How to add Google Fonts to Ghost Themes How to Configure Ghost with Mailgun Ghost Theme Structure & Organization How to Make Custom Templates in Ghost Themes How to Install Ghost CMS via Ghost-CLI How to Add Syntax Highlighting to Ghost The best Ghost CMS hosting platforms Ghost 6.0 — Social Web and Native Ghost Analytics Ghost CMS Social web beta, email 2FA and more author social links How to keep custom design settings when updating your Ghost theme How to use the public preview card in Ghost How to add members-specific sections in Ghost How to customize the default content CTA in Ghost How to build a custom homepage in Ghost An overview of Ghost editor cards and and how they work Ghost CMS News: One-time payments & collecting sales tax How to customize the private site access template in Ghost How to add custom fonts in Ghost themes Ghost CMS News: Additional payment methods & internal linking How to use Code Injection in Ghost How to set meta data of your custom routes & collections in Ghost Ghost CMS News: TK reminders, ActivityPub and improvements How to format dates and use the date helper in Ghost Ghost CMS internationalization, custom fonts & spam protection Understanding and optimizing post and page settings in Ghost CMS
Ghost CMS Search Using Fuse.js
Bright Themes · 2022-01-28 · via Example blog

Search is becoming more popular than ever, consumers have become so accustomed to searching for information that providing a search function is really important.

Ghost doesn't come with a search function by default, so adding a search box to your Ghost site can offer the following benefits:

  • Boost Conversions: By making it easier for visitors to find what they’re looking for, thus creating a positive user experience.
  • Improve SEO: A positive user experience will lead to more time spent on your website, which can help search engines like Google to consider your website to be relevant and high-quality.
  • Build Loyalty: By providing an easy way to find relevant information faster, users are more likely to return again.

A good search function is therefore just as important for a professional website as the underlying CMS or design.

We have previously talked about different search options for Ghost, and now we are going into specifics about the Fuse.js library.

Fuse.js library

Fuse.js is a powerful, lightweight fuzzy-search library, with zero dependencies.

The advantage of Fuse.js is that you don’t need to set up a dedicated backend just for the search function. Another advantage is its simplicity and performance.

It's ideal for client-side fuzzy searching of small to moderately large data sets without a dedicated backend, which is the case for most Ghost CMS websites.

Basic Options

Fuse comes with some basic options:

  • isCaseSensitive - indicates whether comparisons should be case sensitive.
  • includeScore - whether the score should be included in the result set.
  • includeMatches - whether the matches should be included in the result set
  • minMatchCharLength - only the matches whose length exceeds this value will be returned.
  • shouldSort - whether to sort the result list, by score.
  • findAllMatches - when true, the matching function will continue to the end of a search pattern even if a perfect match has already been located in the string.
  • keys - list of keys that will be searched.

Fuzzy matching options

You can also adjust the fuzzy matching options which will change how the search behaves and how the search score is built:

  • location - determines approximately wherein the text is the pattern expected to be found.
  • threshold - at what point does the matching algorithm give up.
  • distance - determines how close the match must be to the fuzzy location
  • ignoreLocation - when true, the search will ignore location and distance, so it won't matter where in the string the pattern appears.

For more details and a complete overview of options check out the official documentation.

Fuse.js in Ghost Themes

If you want to integrate Fuse in your Ghost Theme you can either integrate the script into the theme workflow or simply add the <script> tag using a CDN, for example:

<script src="https://cdn.jsdelivr.net/npm/fuse.js/dist/fuse.js"></script>

After this, we should have Fuse.js available globally. For more information check out the official installation guide. Next, we need to feed Fuse with data, meaning we have to fetch all posts that will be the base for the library to perform the search on. For this, you need to create a custom integration, from the integrations screen in Ghost Admin. Copy the API URL and Content API Key as these will be needed to fetch the posts.

Setup Content API

As mentioned before, you need the Ghost Content API to fetch all posts. If your Ghost theme workflow doesn't include the Content API library you can add it with a <script> tag, just like the fuse library.

<script src="https://unpkg.com/@tryghost/content-api@1.5.16/umd/content-api.min.js"></script>

Warning

Important to note that in the above script we are linking to version 1.5.16 of the Content API library (here is the latest version)

Here is the snippet for initializing the Content API (add your actual URL and Key):

const api = new GhostContentAPI({
  url: 'your_api_url',
  key: 'your_content_api_key',
  version: 'v4',
});

Fetch Posts & Setup Fuse.js

Now that we have both the Fuse and the Content API available, we can fetch the posts and set up the search library with the data:

// setup basic options & variable
const fuseOptions = {
  keys: [{ name: 'title' }],
};

let fuseSearch = '';

// fetch posts
api.posts
  .browse({ limit: 'all', include: 'tags,authors' })
  .then((posts) => {
    fuseSearch = new Fuse(posts, fuseOptions);
  })
  .catch((err) => {
    console.error(err);
  });

In the fuseOptions keys in this example we will search the post titles, but you can further extend it to other fields as well.

Fetching the posts, we set the limit to all and also include tags and authors. You can additionally specify the fields to be fetched (if you want to avoid loading all of them).

Finally, the fuseSearch is initialized passing the posts (data) and the fuseOptions.

Markup & Search event

As for any search, you need at least an input field, so now we will create a minimal HTML for this to work, this includes an input field and a container for the results:

<label> Search: <input class="js-fuse-search" /> </label>
<div class="js-fuse-results bg-acc-1 p-lg"></div>

We also have to add the event handling for the input field:

// script for handling the search when the input changes
document.querySelector('.js-fuse-search').onkeyup = (e) => {
  e.preventDefault();
  const results = fuseSearch.search(e.target.value, { limit: 10 });
  document.querySelector('.js-fuse-results').innerHTML = '';
  results.forEach((result) => {
    document.querySelector(
      '.js-fuse-results',
    ).innerHTML += `<h6>${result.item.title}</h6>`;
  });
};

The event handler (when a user releases a key) includes triggering the search fuseSearch.search which returns a list of posts based on the input value (e.target.value). The final step is adding the results to our container.

That's it, the search should be working.

Additional settings & adjustments

Of course, there is much more fine-tuning you could do, here is some to consider:

  • adjust the fuseOptions if necessary, for example, the keys. You could potentially add other fields to search, for example: tags , and custom_excerpt.
  • specify the fields when fetching posts with the Ghost Content API to avoid loading all fields when it's not necessary
  • to include other data besides post title adjust the markup, the result.item should contain all fields of a post, so you can also show the date, tags, author, or anything else. And of course, you can add CSS to change how it looks.

Complete code & Demo

To make everything easier here is the complete code including all scripts and markup, you can simply paste this into an HTML card in a post/page and change the your_api_url and your_content_api_key with your own integration value.

<!-- Fuse & Content API library -->
<script src="https://unpkg.com/@tryghost/content-api@1.5.16/umd/content-api.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/fuse.js/dist/fuse.js"></script>

<!-- Input field & container for result -->
<label> Search: <input class="js-fuse-search" /> </label>
<div class="js-fuse-results bg-acc-1 p-lg"></div>

<!-- Input Event handling & search -->
<script>
  document.querySelector('.js-fuse-search').onkeyup = (e) => {
    e.preventDefault();
    const results = fuseSearch.search(e.target.value, { limit: 10 });
    document.querySelector('.js-fuse-results').innerHTML = '';
    results.forEach((result) => {
      document.querySelector(
        '.js-fuse-results',
      ).innerHTML += `<h6>${result.item.title}</h6>`;
    });
  };
</script>

<!-- Initialize Content API & Fuse -->
<script>
  const fuseOptions = {
    keys: [{ name: 'title' }],
  };

  let fuseSearch = '';

  window.onload = () => {
    // setup Content API
    const api = new GhostContentAPI({
      url: 'your_api_url',
      key: 'your_content_api_key',
      version: 'v4',
    });

    // fetch posts
    api.posts
      .browse({ limit: 'all', include: 'tags,authors' })
      .then((posts) => {
        // Initialize Fuse
        fuseSearch = new Fuse(posts, fuseOptions);
      })
      .catch((err) => {
        console.error(err);
      });
  };
</script>

And here is a working demo of this setup in the Nikko theme, also the Nikko search is implemented with Fuse including some fine-tuning, to test that, go to the Nikko demo and press the search icon in the header.