Strapi plugin logo for Fuzzy Search

Fuzzy Search

A plugin for Strapi Headless CMS that provides the ability to add a weighted fuzzy search to any content type

fuzzy search logo

Strapi-plugin-fuzzy-search

All Contributors

Register a weighted fuzzy search endpoint for Strapi Headless CMS you can add your content types to in no time.

Uses fuzzysort under the hood: Simple, quick and easy. No need to worry about setting up an instance for a complex search engine.

Roadmap:

  • Include more fuzzysort options
  • Return better errors

Requirements

Strapi Version v4.x.x

Installation

Enable the fuzzy-search plugin in the ./config/plugins.js of your Strapi project.

Make sure to set the appropriate permissions for the search route in the Permissions tab of the Users & Permission Plugin for the role to be able to access the search route.

Options/Config

Mandatory settings are marked with *.

General Options

The plugin requires several configurations to be set in the .config/plugins.js file of your Strapi project to work.

KeyTypeNotes                                              
contentTypes*Array of ObjectsList the content types you want to register for fuzzysort. Each object requires the uid: string and modelName: string to be set for a content type
transliteratebooleanIf this is set to true the search will additionally run against transliterated versions of the content for the keys specified in the keys array for a given content type. E.g. 你好 will match for ni hao. Note that activating this feature for a content type comes at a performance cost and may increase the response time.
queryConstraintsObjectManipulate the db query that queries for the entries of a model, e.g. as to only select articles that have been published. These constraints to the underlying findMany() query are built with the logical operators of Strapis Query Engine API

Fuzzysort Options

The fuzzysortOptions allow for some finetuning of fuzzysorts searching algorithm to your needs.

KeyTypeNotes
characterLimitint (positive)Limits the length of characters the algorithm is searching through for any string of the content type
thresholdint (negative)Sets the threshold for the score of the entries that will be returned. The lower, the "fuzzier" the results.
limitint (positive)Limits the amount of entries returned from the search
keys*array of objectsLists the fields of the models the algorithm should search (name: string) and a factor to weight them by weight: int. The higher the weight, the higher a match for a given field will be evaluated for a content type.

Full Example config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
module.exports = ({ env }) => ({
  // ...

  "fuzzy-search": {
    enabled: true,
    config: {
      contentTypes: [
        {
          uid: "api::author.author",
          modelName: "author",
          transliterate: true,
          queryConstraints: {
            where: {
              $and: [
                {
                  publishedAt: { $notNull: true },
                },
              ],
            },
          },
          fuzzysortOptions: {
            characterLimit: 300,
            threshold: -600,
            limit: 10,
            keys: [
              {
                name: "name",
                weight: 100,
              },
              {
                name: "description",
                weight: -100,
              },
            ],
          },
        },
        {
          uid: "api::book.book",
          modelName: "book",
          fuzzysortOptions: {
            characterLimit: 500,
            keys: [
              {
                name: "title",
                weight: 200,
              },
              {
                name: "description",
                weight: -200,
              },
            ],
          },
        },
      ],
    },
  },

  // ...
});

A note on performance:

A high characterCount, threshold and limit as well as setting transliterate: true all hamper the performance of the search algorithm. We recommend that you start out with a characterCount: 500, threshold: -1000, limit: 15 and work your way from there. The characterCount especially can be quite delicate, so make sure to test every scenario when dialing in it's value.

Usage

Search

Hitting the /api/fuzzy-search/search?query=<your-query-string> will return an array of matched entries for each content type registered in the config. If no match could be found an empty array will be returned. The endpoint accepts an optional locale=<your-locale> query as well.

Alternatively (and if the graphql plugin is installed), a search query is registered that accepts query: String! and locale: String (optional) as arguments.

IMPORTANT: Please not that in order to query for the locale of a content type, localization must be enabled for the content type.

Examples

Example Requests

REST

1
2
await fetch(`${API_URL}/api/fuzzy-search/search?query=deresh&locale=en`);
// GET /api/fuzzy-search/search?query=john&locale=en

GraphQl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
query {
  search(query: "deresh", locale: "en") {
    authors {
      data {
        attributes {
          name
        }
      }
    }
    books {
      data {
        attributes {
          title
          description
        }
      }
    }
  }
}

Example Responses

IMPORTANT: Please note that as of now published as well as unpublished entries will be returned.

REST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
  "authors": [
    {
      "id": 1,
      "name": "Любко Дереш",
      "description": "As an author, Lyubko has had somewhat of a cult-like following among the younger generation in Ukraine since the appearance of his novel Cult at age eighteen, which was followed almost immediately by the publication of another novel, written in early high school.",
      "createdAt": "2022-05-05T13:08:19.312Z",
      "updatedAt": "2022-05-05T13:34:46.488Z",
      "publishedAt": "2022-05-05T13:22:17.310Z"
    }
  ],
  "books": [
    {
      "id": 1,
      "title": "Jacob's Head",
      "description": "Jacob’s Head by Lyubko Deresh is scheduled to be adapted into a movie in Ukraine in the near future.",
      "createdAt": "2022-05-05T13:08:43.816Z",
      "updatedAt": "2022-05-05T13:24:07.107Z",
      "publishedAt": "2022-05-05T13:22:23.764Z"
    }
  ]
}

GraphQl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "data": {
    "search": {
      "authors": {
        "data": [
          {
            "attributes": {
              "name": "Любко Дереш"
            }
          }
        ]
      },
      "books": {
        "data": [
          {
            "attributes": {
              "title": "Jacob's Head",
              "description": "Jacob’s Head by Lyubko Deresh is scheduled to be adapted into a movie in Ukraine in the near future."
            }
          }
        ]
      }
    }
  }
}

Why use fuzzysort and not something like Fuse.js?

While Fuse.js proofs to be an amazing library, it can yield unexpected results and what is to be perceived as "false positives" (by a human) when searching through longer strings. Fuzzysort aims to solve this problem by introducing the evaluation and scoring of exact matches. Since we had issues with Fuse.js and it's underlying algorithm, we opted for fuzzysearch to do the heavy lifting instead.

Found a bug?

If you found a bug or have any questions please submit an issue. If you think you found a way how to fix it, please feel free to create a pull request!

Contributors ✨

Thanks goes to these wonderful people (emoji key):

This project follows the all-contributors specification. Contributions of any kind welcome!

Install now

npm install strapi-plugin-fuzzy-search

Last updated

53 days ago

Strapi Compatibility

4.0.8 and above

Author

github profile image for @DomDew
@DomDew

Useful links

Create your own plugin

Check out the available plugin resources that will help you to develop your plugin or provider and get it listed on the marketplace.