Fuzzy search with Fuse.js

Helping users around spelling errors with fuss-free JavaScript library for fuzzy search.

Say you want to make a search feature in a web app where you provide a text input for users to search for other people by name.

You want your app to show all possible matches as the user types the name they are searching for.

Here's the list of searchable names your app has stored:

const names = ["d'Artagnan", "Athos", "Porthos", "Aramis"];

But names are hard for humans.

Initial approach

As a first pass, we can try filtering the list with simple string matching:

const getResults = (searchString) => {
  return names.filter((name, index) => {
    return name.toLowerCase()
      .includes(searchString.toLowerCase());
  });
};

getResults("ar"); // [ "d'Artagnan", 'Aramis' ]

Seems promising, but there are issues with this approach.

Problem 1: spelling

What if the user is looking for Aramis and isn't clear on the spelling:

getResults("arem"); // []

Hmm... no results.

Problem 2: punctuation

Or take the first name in the list: d'Artagnan. Users (well, non-French users, I presume) may not realize there's an apostrophe in the name:

getResults("dar"); // []

Oops. Another near miss that gives us no results.

Fuzzy search

This is where you'll want to reach for fuzzy search, but it sounds a little daunting. The name list above is short; setting up a connection to a specialized service isn't going to be worth the trouble.

Luckily, there's a great JavaScript module out there called Fuse.js that offers fuzzy search.

The basic setup is incredibly simple:

const Fuse = require("fuse.js");

const names = ["d'Artagnan", "Athos", "Porthos", "Aramis"];

const fuse = new Fuse(names);

Let's use this simple setup to try the two problems we encountered above with simple string matching.

Problem 1: spelling

When we botch the spelling for Aramis:

fuse.search("arem");

/*
[
  { item: 'Aramis', refIndex: 3 },
  { item: "d'Artagnan", refIndex: 0 }
]
*/

Nice! We got two results. If we surface these to the user, they will have no trouble recognizing the option they were looking for.

Problem 2: punctuation

When we omit the apostrophe in d'Artagnan's name:

getResults("dar");

/*
[
  { item: "d'Artagnan", refIndex: 0 },
  { item: 'Aramis', refIndex: 3 }
]
*/

Also nice! The user didn't need to remember the apostrophe, and if we're showing these results as the user types, they won't have to guess at how to spell the rest of d'Artagnan's name.

Options for fuzzy search with Fuse.js

Everything we've covered here is pretty bare bones, but you can fine tune Fuse.js to your situation. Fuse offers a number of useful constructor options and more.

As one slightly more complex example, here's a pull request on an early project exploration we're working on with Astoria Digital. The write-up is thorough, and covers some Fuse config options, filtering on search result scores, and a few other tidbits.