Fuzzy Search Plugin

:mag_right: Fuzzy-search plugin for Kirby. Searching for non-exact matches in your content has never been this easy.

This is plugin is built on top of the fuzzget PHP library.

Basic Usage

If you are already using the Kirby built-in search method, replacing it with Fuzzy Search is just a matter of renaming the search method to fuzzySearch like shown below:

$query    = get('q');
$articles = page('blog')
    ->children()
    ->visible()
-   ->search($query, 'title|text');
+   ->fuzzySearch($query, 'title|text');

Fuzzy Search is not compatible with any of the other options available on the Kirby search method.

With Fuzzy Search you can also search through custom page methods or page models. You only need to include the method name in the fuzzySearch last parameter.

// site/plugins/methods.php
page::$methods['authorName'] = function($page) {
    $author = $page->author()->value();

    if ($user = site()->user($author)) {
        return $user->firstname().' '.$user->lastname();
    }
};
$articles = page('blog')
    ->children()
    ->visible();

if ($query = get('q')) {
    $articles = $articles->fuzzySearch($query, 'title|text|authorName');
}

Searching on structured fields

Fuzzy Search ships with a handy field method that allows you to search on structured fields.

$result = page('faq')
    ->topics()
    ->fuzzySearch($query, 'question|answer');

The $result will also be a Field object and not just a simple array. That way you are free to chain any Field method, such as toStructure, yaml, or isEmpty, after getting the search result.

$result = page('contact')
    ->addresses()
    ->fuzzySearch($query, 'city')
    ->toStructure();

Searching on arrays

You also can use the fuzzySearch function to search through an array of associative arrays.

$countries = [
    ['name' => 'Australia'],
    ['name' => 'Brazil'],
    ['name' => 'Canada'],
    ['name' => 'France'],
    ['name' => 'Germany'],
    ['name' => 'Portugal'],
    ['name' => 'United Kingdom'],
    ['name' => 'United States']
];

$results = fuzzySearch($countries, 'Brasil');

Advanced Usage

If you leave out the last parameter, Fuzzy Search will search through all keys (fields) in the provided data:

site()->index()->fuzzySearch($query);

It’s the same as using the * wildcard.

site()->index()->fuzzySearch($query, '*');

Fuzzy Search is very flexible when it comes to choosing in which fields it should look for matches. Check out the other options:

Include

If you want to search for a given term only in the title and text fields, just pass their names in the last parameter separated by |:

site()->index()->fuzzySearch($query, 'title|text');

This is syntax sugar for:

site()->index()->fuzzySearch($query, [
    'include' => ['title', 'text']
]);

Ignore

Of course you can also list fields which you do not want to be searched:

site()->index()->fuzzySearch($query, '-author|-date');

The example above is the same as:

site()->index()->fuzzySearch($query, [
    'ignore' => ['author', 'date']
]);

In this case, all fields will be considered in the search except for author and date fields.

If you need to include a custom page method or page model method, you can combine it with the wildcard and ignore syntax.

site()->index()->fuzzySearch($query, '*|authorName|-date');

In this example, all fields but the date field will be searched on. $page->authorName() will be used if it is either a custom page method or page model method.


Try it out and please send your feedback.

9 Likes

The plugin just got better and I have updated the release notes to reflect the latest changes.

Haven’t checked it yet, but will try to do it soon! Looks promising, thx @pedroborges!

Could this also be used to search json files (which have an “url” in the data to resolve to) besides the Kirby pages too? Or a PHP array?

You could use PHP json_decode function to turn it into an array and pass it to the plugin.

If you need to search for an exact match in your data, doing so with array_filter or foreach is a better and faster option.

Fuzzy Search shines because in finds aproximate matches to the searched query which is helpful in situations where the user makes a typo or there are variations of the word in the content.

Thanks for developing this! I have a problem however. When I try to navigate to the search url e.g. mydomain.com/search with no query I get the following error:
Argument 2 passed to Kirby::{closure}() must be of the type string, null given, called in /srv/users/serverpilot/apps/site/public/kirby/vendor/getkirby/toolkit/helpers.php on line 282

mydomain.com/search?q=search+term works fine
mydomain.com/search?q= works fine

You are welcome, @macgyver!

That’s very likely a problem in your controller that can be easily solved by checking if q exists before passing it to fuzzySearch:

return function($site, $pages, $page) {
    $query    = get('q');
    $articles = $page->children()->visible()->flip();

    if ($query) {
        $articles = $articles->fuzzySearch($query, 'title|text');
    }

    $perpage    = $page->perpage()->int();
    $articles   = $articles->paginate($perpage >= 1 ? $perpage : 5);
    $pagination = $articles->pagination();

    return compact('articles', 'pagination', 'query');
};
1 Like

Yes! Thank for for developing this plugin! However, I am seeing a similar {closure} error as @macgyver, but it only occurs when I pass any query at all. Hitting the search page without a query works fine.

Argument 2 passed to Kirby::{closure}() must be an instance of string, string given"
/home1/newtedor/public_html/glossary/site/plugins/fuzzy-search/fuzzy-search.php 46

I am using a version of your controller example:

<?php
	return function($site, $pages, $page) {
		$query   = get('q');

		if ($query) {
			$results = $site->index()->fuzzySearch($query, 'title|text');
		}

		$perpage    = $page->perpage()->int();
		$results    = $results->paginate($perpage >= 1 ? $perpage : 10);
		$pagination = $results->pagination();

		return compact('results', 'pagination', 'query');
	};
?>

Any idea what’s going on here?

@splorp What PHP version are you using? You need PHP 7.x for the plugin (and type hinting for strings) to work. Says so in the requirements as well.

Hrm … I assumed that my server already had PHP 7.x in place. Obviously, that might not be true. I’ll check later tonight. Thanks.

@pedroborges That is good to know, thanks!

Why is it that I don’t have to check if q exists with the Kirby built-in search method?

I didn’t know that a null query worked with Kirby’s built-in search. Inspecting the code I found it returns an empty collection in that case:

if(empty($searchwords)) return $collection->limit(0);

I will do the same for Fuzzy Search to make it compatible with Kirby’s built-in search. Thank you for finding and reporting this incompatibility.

That’s correct. The library Fuzzy Search depends on requires PHP > 7.0.

I just checked the server and it’s running PHP 5.6.32 … I’ll have to look into updating that down the road.

Thanks for the help, everyone.

@macgyver Fuzzy Search should have the same behavior as Kirby’s built-in search now. You can try it out on the develop branch.

@pedroborges Excellent, thanks!

@pedroborges Thanks so much for this! However, I am having trouble getting it to work the it is described to make it work. I already had Kirby’s search, so I simply subbed in fuzzySearch as described:

<!-- TESTING FUZZY SEARCH -->
<!-- <form action="<?= search($pages->index(), '$query', 'title|text') ?>" id="navsearchform"> -->
<form action="<?= fuzzySearch($pages->index(), '$query', 'title|text') ?>" id="navsearchform"> 

However, I keep getting this breaking error (see screenshot below): “Call to undefined function fuzzySearch()”.

I have updated to the latest Kirby and PHP versions. I did notice in the error that my Kirby toolkit and CMS still say version 2.4.0, but not sure why.

Do I need to do anything else to get my setup to recognize “fuzzySearch”?

Thanks for any help!

What does your plugin folder structure look like now, what is the name of the folder?

Updating: Have you replaced both. the Kirby and the Panel folders?

Apart from the error, the search code doesn’t belong in the form action attribute, there you should. put the URL to the search page.

action="<?= $page->url() ?>"

The search code itself belongs in the controller, see the docs: https://getkirby.com/docs/cookbook/search

What @texnixe said above :point_up_2: about the action attribute :heavy_plus_sign: make sure the plugin is installed at site/plugins/fuzzy-search.