Skip to content

Commit

Permalink
feat: establish compatibility with Fuse.js 7.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
loilo committed Feb 3, 2025
1 parent 1db0ccf commit 47ded8b
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 4 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This is a PHP port of the awesome [Fuse.js](https://github.com/krisk/fuse) proje

Check out their [demo](https://fusejs.io/demo.html) and [examples](https://fusejs.io/examples.html) to get a good feel for what this library is capable of.

> Latest compatible Fuse.js version: 7.0.0
> Latest compatible Fuse.js version: 7.1.0
---

Expand Down Expand Up @@ -114,6 +114,15 @@ Indicates whether comparisons should be case sensitive.

---

#### `ignoreDiacritics`

- Type: `bool`
- Default: `false`

Indicates whether comparisons should ignore diacritics (accents).

---

#### `includeScore`

- Type: `bool`
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "loilo/fuse",
"description": "Fuzzy search for PHP based on Bitap algorithm",
"type": "library",
"version": "7.0.1",
"version": "7.1.0",
"license": "Apache-2.0",
"authors": [
{
Expand All @@ -27,6 +27,7 @@
"src/Core/config.php",
"src/Core/format.php",
"src/Core/parse.php",
"src/Helpers/diacritics.php",
"src/Helpers/get.php",
"src/Helpers/sort.php",
"src/Helpers/types.php",
Expand Down
1 change: 1 addition & 0 deletions src/Core/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function config(...$args)
$config = (object) [
// Basic options
'isCaseSensitive' => false,
'ignoreDiacritics' => false,
'includeScore' => false,
'keys' => [],
'shouldSort' => true,
Expand Down
18 changes: 18 additions & 0 deletions src/Helpers/diacritics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Fuse\Helpers;

function stripDiacritics(string $str): string
{
if (!function_exists('normalizer_normalize')) {
return $str;
}

$normalized_string = normalizer_normalize($str, \Normalizer::NFD);

return preg_replace(
"/[\u{0300}-\u{036F}\u{0483}-\u{0489}\u{0591}-\u{05BD}\u{05BF}\u{05C1}\u{05C2}\u{05C4}\u{05C5}\u{05C7}\u{0610}-\u{061A}\u{064B}-\u{065F}\u{0670}\u{06D6}-\u{06DC}\u{06DF}-\u{06E4}\u{06E7}\u{06E8}\u{06EA}-\u{06ED}\u{0711}\u{0730}-\u{074A}\u{07A6}-\u{07B0}\u{07EB}-\u{07F3}\u{07FD}\u{0816}-\u{0819}\u{081B}-\u{0823}\u{0825}-\u{0827}\u{0829}-\u{082D}\u{0859}-\u{085B}\u{08D3}-\u{08E1}\u{08E3}-\u{0903}\u{093A}-\u{093C}\u{093E}-\u{094F}\u{0951}-\u{0957}\u{0962}\u{0963}\u{0981}-\u{0983}\u{09BC}\u{09BE}-\u{09C4}\u{09C7}\u{09C8}\u{09CB}-\u{09CD}\u{09D7}\u{09E2}\u{09E3}\u{09FE}\u{0A01}-\u{0A03}\u{0A3C}\u{0A3E}-\u{0A42}\u{0A47}\u{0A48}\u{0A4B}-\u{0A4D}\u{0A51}\u{0A70}\u{0A71}\u{0A75}\u{0A81}-\u{0A83}\u{0ABC}\u{0ABE}-\u{0AC5}\u{0AC7}-\u{0AC9}\u{0ACB}-\u{0ACD}\u{0AE2}\u{0AE3}\u{0AFA}-\u{0AFF}\u{0B01}-\u{0B03}\u{0B3C}\u{0B3E}-\u{0B44}\u{0B47}\u{0B48}\u{0B4B}-\u{0B4D}\u{0B56}\u{0B57}\u{0B62}\u{0B63}\u{0B82}\u{0BBE}-\u{0BC2}\u{0BC6}-\u{0BC8}\u{0BCA}-\u{0BCD}\u{0BD7}\u{0C00}-\u{0C04}\u{0C3E}-\u{0C44}\u{0C46}-\u{0C48}\u{0C4A}-\u{0C4D}\u{0C55}\u{0C56}\u{0C62}\u{0C63}\u{0C81}-\u{0C83}\u{0CBC}\u{0CBE}-\u{0CC4}\u{0CC6}-\u{0CC8}\u{0CCA}-\u{0CCD}\u{0CD5}\u{0CD6}\u{0CE2}\u{0CE3}\u{0D00}-\u{0D03}\u{0D3B}\u{0D3C}\u{0D3E}-\u{0D44}\u{0D46}-\u{0D48}\u{0D4A}-\u{0D4D}\u{0D57}\u{0D62}\u{0D63}\u{0D82}\u{0D83}\u{0DCA}\u{0DCF}-\u{0DD4}\u{0DD6}\u{0DD8}-\u{0DDF}\u{0DF2}\u{0DF3}\u{0E31}\u{0E34}-\u{0E3A}\u{0E47}-\u{0E4E}\u{0EB1}\u{0EB4}-\u{0EB9}\u{0EBB}\u{0EBC}\u{0EC8}-\u{0ECD}\u{0F18}\u{0F19}\u{0F35}\u{0F37}\u{0F39}\u{0F3E}\u{0F3F}\u{0F71}-\u{0F84}\u{0F86}\u{0F87}\u{0F8D}-\u{0F97}\u{0F99}-\u{0FBC}\u{0FC6}\u{102B}-\u{103E}\u{1056}-\u{1059}\u{105E}-\u{1060}\u{1062}-\u{1064}\u{1067}-\u{106D}\u{1071}-\u{1074}\u{1082}-\u{108D}\u{108F}\u{109A}-\u{109D}\u{135D}-\u{135F}\u{1712}-\u{1714}\u{1732}-\u{1734}\u{1752}\u{1753}\u{1772}\u{1773}\u{17B4}-\u{17D3}\u{17DD}\u{180B}-\u{180D}\u{1885}\u{1886}\u{18A9}\u{1920}-\u{192B}\u{1930}-\u{193B}\u{1A17}-\u{1A1B}\u{1A55}-\u{1A5E}\u{1A60}-\u{1A7C}\u{1A7F}\u{1AB0}-\u{1ABE}\u{1B00}-\u{1B04}\u{1B34}-\u{1B44}\u{1B6B}-\u{1B73}\u{1B80}-\u{1B82}\u{1BA1}-\u{1BAD}\u{1BE6}-\u{1BF3}\u{1C24}-\u{1C37}\u{1CD0}-\u{1CD2}\u{1CD4}-\u{1CE8}\u{1CED}\u{1CF2}-\u{1CF4}\u{1CF7}-\u{1CF9}\u{1DC0}-\u{1DF9}\u{1DFB}-\u{1DFF}\u{20D0}-\u{20F0}\u{2CEF}-\u{2CF1}\u{2D7F}\u{2DE0}-\u{2DFF}\u{302A}-\u{302F}\u{3099}\u{309A}\u{A66F}-\u{A672}\u{A674}-\u{A67D}\u{A69E}\u{A69F}\u{A6F0}\u{A6F1}\u{A802}\u{A806}\u{A80B}\u{A823}-\u{A827}\u{A880}\u{A881}\u{A8B4}-\u{A8C5}\u{A8E0}-\u{A8F1}\u{A8FF}\u{A926}-\u{A92D}\u{A947}-\u{A953}\u{A980}-\u{A983}\u{A9B3}-\u{A9C0}\u{A9E5}\u{AA29}-\u{AA36}\u{AA43}\u{AA4C}\u{AA4D}\u{AA7B}-\u{AA7D}\u{AAB0}\u{AAB2}-\u{AAB4}\u{AAB7}\u{AAB8}\u{AABE}\u{AABF}\u{AAC1}\u{AAEB}-\u{AAEF}\u{AAF5}\u{AAF6}\u{ABE3}-\u{ABEA}\u{ABEC}\u{ABED}\u{FB1E}\u{FE00}-\u{FE0F}\u{FE20}-\u{FE2F}]/u",
'',
$normalized_string,
);
}
11 changes: 10 additions & 1 deletion src/Search/Bitap/BitapSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Fuse\Search\SearchInterface;

use function Fuse\Core\config;
use function Fuse\Helpers\stripDiacritics;
use function Fuse\Search\Bitap\search;
use function Fuse\Search\Bitap\createPatternAlphabet;

Expand All @@ -24,6 +25,7 @@ public function __construct(string $pattern, array $options = [])
$findAllMatches = $options['findAllMatches'] ?? config('findAllMatches');
$minMatchCharLength = $options['minMatchCharLength'] ?? config('minMatchCharLength');
$isCaseSensitive = $options['isCaseSensitive'] ?? config('isCaseSensitive');
$ignoreDiacritics = $options['ignoreDiacritics'] ?? config('ignoreDiacritics');
$ignoreLocation = $options['ignoreLocation'] ?? config('ignoreLocation');

$this->options = [
Expand All @@ -34,10 +36,13 @@ public function __construct(string $pattern, array $options = [])
'findAllMatches' => $findAllMatches,
'minMatchCharLength' => $minMatchCharLength,
'isCaseSensitive' => $isCaseSensitive,
'ignoreDiacritics' => $ignoreDiacritics,
'ignoreLocation' => $ignoreLocation,
];

$this->pattern = $isCaseSensitive ? $pattern : mb_strtolower($pattern);
$pattern = $isCaseSensitive ? $pattern : mb_strtolower($pattern);
$pattern = $ignoreDiacritics ? stripDiacritics($pattern) : $pattern;
$this->pattern = $pattern;

if (mb_strlen($this->pattern) === 0) {
return;
Expand Down Expand Up @@ -78,6 +83,10 @@ public function searchIn(string $text): array
$text = mb_strtolower($text);
}

if ($this->options['ignoreDiacritics']) {
$text = stripDiacritics($text);
}

// Exact match
if ($this->pattern === $text) {
$result = [
Expand Down
8 changes: 7 additions & 1 deletion src/Search/Extended/ExtendedSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Fuse\Search\SearchInterface;

use function Fuse\Core\config;
use function Fuse\Helpers\stripDiacritics;
use function Fuse\Search\Extended\parseQuery;

/**
Expand Down Expand Up @@ -47,9 +48,11 @@ class ExtendedSearch implements SearchInterface
public function __construct(string $pattern, array $options = [])
{
$isCaseSensitive = $options['isCaseSensitive'] ?? config('isCaseSensitive');
$ignoreDiacritics = $options['ignoreDiacritics'] ?? config('ignoreDiacritics');

$this->options = [
'isCaseSensitive' => $isCaseSensitive,
'ignoreDiacritics' => $ignoreDiacritics,
'includeMatches' => $options['includeMatches'] ?? config('includeMatches'),
'minMatchCharLength' => $options['minMatchCharLength'] ?? config('minMatchCharLength'),
'findAllMatches' => $options['findAllMatches'] ?? config('findAllMatches'),
Expand All @@ -59,7 +62,9 @@ public function __construct(string $pattern, array $options = [])
'distance' => $options['distance'] ?? config('distance'),
];

$this->pattern = $isCaseSensitive ? $pattern : mb_strtolower($pattern);
$pattern = $isCaseSensitive ? $pattern : mb_strtolower($pattern);
$pattern = $ignoreDiacritics ? stripDiacritics($pattern) : $pattern;
$this->pattern = $pattern;
$this->query = parseQuery($this->pattern, $this->options);
}

Expand All @@ -84,6 +89,7 @@ public function searchIn(string $text): array
$multiMatchSet = [FuzzyMatch::$type, IncludeMatch::$type];

$text = $this->options['isCaseSensitive'] ? $text : mb_strtolower($text);
$text = $this->options['ignoreDiacritics'] ? stripDiacritics($text) : $text;

$numMatches = 0;
$allIndices = [];
Expand Down
1 change: 1 addition & 0 deletions src/Search/Extended/FuzzyMatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function __construct(string $pattern, array $options = [])
'findAllMatches' => $options['findAllMatches'] ?? config('findAllMatches'),
'minMatchCharLength' => $options['minMatchCharLength'] ?? config('minMatchCharLength'),
'isCaseSensitive' => $options['isCaseSensitive'] ?? config('isCaseSensitive'),
'ignoreDiacritics' => $options['ignoreDiacritics'] ?? config('ignoreDiacritics'),
'ignoreLocation' => $options['ignoreLocation'] ?? config('ignoreLocation'),
]);
}
Expand Down
37 changes: 37 additions & 0 deletions tests/test/ExtendedSearchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,40 @@
expect($result)->toHaveCount(1);
});
});

describe('Searching using extended search ignoring diacritics', function () {
$list = [['text' => 'déjà'], ['text' => 'cafe']];

$options = [
'useExtendedSearch' => true,
'ignoreDiacritics' => true,
'threshold' => 0,
'keys' => ['text'],
];

$fuse = new Fuse($list, $options);

test('Search: query with diacritics, list with diacritics', function () use ($fuse) {
$result = $fuse->search('déjà');
expect($result)->toHaveCount(1);
expect($result[0]['refIndex'])->toBe(0);
});

test('Search: query without diacritics, list with diacritics', function () use ($fuse) {
$result = $fuse->search('deja');
expect($result)->toHaveCount(1);
expect($result[0]['refIndex'])->toBe(0);
});

test('Search: query with diacritics, list without diacritics', function () use ($fuse) {
$result = $fuse->search('café');
expect($result)->toHaveCount(1);
expect($result[0]['refIndex'])->toBe(1);
});

test('Search: query without diacritics, list without diacritics', function () use ($fuse) {
$result = $fuse->search('cafe');
expect($result)->toHaveCount(1);
expect($result[0]['refIndex'])->toBe(1);
});
});
36 changes: 36 additions & 0 deletions tests/test/FuzzySearchTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,39 @@ function () {
});
});
});

describe('Searching ignoring diactrictics', function () {
$list = [['text' => 'déjà'], ['text' => 'cafe']];

$options = [
'ignoreDiacritics' => true,
'threshold' => 0,
'keys' => ['text'],
];

$fuse = new Fuse($list, $options);

test('Search: query with diacritics, list with diacritics', function () use ($fuse) {
$result = $fuse->search('déjà');
expect($result)->toHaveCount(1);
expect($result[0]['refIndex'])->toBe(0);
});

test('Search: query without diacritics, list with diacritics', function () use ($fuse) {
$result = $fuse->search('deja');
expect($result)->toHaveCount(1);
expect($result[0]['refIndex'])->toBe(0);
});

test('Search: query with diacritics, list without diacritics', function () use ($fuse) {
$result = $fuse->search('café');
expect($result)->toHaveCount(1);
expect($result[0]['refIndex'])->toBe(1);
});

test('Search: query without diacritics, list without diacritics', function () use ($fuse) {
$result = $fuse->search('cafe');
expect($result)->toHaveCount(1);
expect($result[0]['refIndex'])->toBe(1);
});
});

0 comments on commit 47ded8b

Please sign in to comment.