Библиотека нечеткого поиска для PHP
Это PHP-порт замечательного проекта Fuse.js, целью которого является обеспечение полной совместимости API там, где это возможно.
Посмотрите их демо и примеры, чтобы получить представление о том, на что способна эта библиотека.
Последняя совместимая версия Fuse.js: 7.0.0.
Оглавление:
Этот пакет доступен через Composer. Чтобы добавить его в свой проект, просто запустите:
composer require loilo/fuse
Обратите внимание, что для использования Fuse необходим PHP 7.4.
Вот простой пример использования:
<?php
require_once ' vendor/autoload.php ' ;
$ list = [
[
' title ' => " Old Man's War " ,
' author ' => ' John Scalzi ' ,
],
[
' title ' => ' The Lock Artist ' ,
' author ' => ' Steve Hamilton ' ,
],
[
' title ' => ' HTML5 ' ,
' author ' => ' Remy Sharp ' ,
],
[
' title ' => ' Right Ho Jeeves ' ,
' author ' => ' P.D Woodhouse ' ,
],
];
$ options = [
' keys ' => [ ' title ' , ' author ' ],
];
$ fuse = new Fuse Fuse ( $ list , $ options );
$ fuse -> search ( ' hamil ' );
Это приводит к следующим результатам (где каждый item
результата относится к самой соответствующей записи, а refIndex
предоставляет позицию элемента в исходном $list
):
[
[
' item ' => [
' title ' => ' The Lock Artist ' ,
' author ' => ' Steve Hamilton ' ,
],
' refIndex ' => 1 ,
],
[
' item ' => [
' title ' => ' HTML5 ' ,
' author ' => ' Remy Sharp ' ,
],
' refIndex ' => 2 ,
],
];
У Fuse есть множество опций для уточнения вашего поиска:
isCaseSensitive
bool
false
Указывает, должны ли сравнения учитывать регистр.
includeScore
bool
false
Следует ли включать оценку в набор результатов. Оценка 0
означает полное совпадение, а оценка 1
— полное несоответствие.
includeMatches
bool
false
Должны ли совпадения быть включены в набор результатов. Если true
, каждая запись в наборе результатов будет включать индексы совпадающих символов. Следовательно, их можно использовать для выделения.
minMatchCharLength
int
1
Будут возвращены только совпадения, длина которых превышает это значение. (Например, если вы хотите игнорировать совпадения отдельных символов в результате, установите для него значение 2
).
shouldSort
bool
true
Сортировать ли список результатов по баллам.
findAllMatches
bool
false
Если установлено значение true, функция сопоставления продолжит работу до конца шаблона поиска, даже если в строке уже найдено идеальное совпадение.
keys
array
[]
Список ключей, по которым будет производиться поиск. Это поддерживает вложенные пути, взвешенный поиск, поиск в массивах строк и объектов.
location
int
0
Примерно определяет, где в тексте находится шаблон, который ожидается найти.
threshold
float
0.6
В какой момент алгоритм сопоставления сдается. Порог 0.0
требует идеального совпадения (как букв, так и местоположения), порог 1.0
будет соответствовать чему угодно.
distance
int
100
Определяет, насколько близко должно быть совпадение к нечеткому местоположению (заданному location
). Точное совпадение букв, находящееся distance
символов от нечеткого местоположения, будет считаться полным несоответствием. distance
, равное 0
требует, чтобы совпадение находилось в точном указанном location
. Расстояние 1000
потребует, чтобы идеальное совпадение находилось в пределах 800
символов от location
, которое нужно найти, с использованием threshold
0.8
.
ignoreLocation
bool
false
Если true
, поиск будет игнорировать location
и distance
, поэтому не имеет значения, где в строке появляется шаблон.
Совет: Параметры по умолчанию выполняют поиск только по первым 60 символам. Этого должно быть достаточно, если разумно ожидать, что совпадение находится в пределах этого диапазона. Чтобы изменить это поведение, установите соответствующую комбинацию
location
,threshold
,distance
(илиignoreLocation
).Чтобы лучше понять, как эти параметры работают вместе, прочитайте о теории оценки Fuse.js.
useExtendedSearch
bool
false
Если true
, это позволяет использовать команды поиска в стиле unix. См. пример.
getFn
callable
Функция, используемая для получения значения объекта по указанному пути. По умолчанию также будет выполняться поиск по вложенным путям.
sortFn
callable
Функция, используемая для сортировки всех результатов. По умолчанию сортировка осуществляется по возрастанию оценки релевантности и возрастанию индекса.
ignoreFieldNorm
bool
false
Если true
, при вычислении оценки релевантности (используемой для сортировки) норма длины поля будет игнорироваться.
Совет: Единственный случай, когда имеет смысл установить
ignoreFieldNorm
вtrue
— это когда не имеет значения, сколько терминов существует, а только то, что термин запроса существует.
fieldNormWeight
float
1
Определяет, насколько норма длины поля влияет на подсчет очков. Значение 0
эквивалентно игнорированию нормы длины поля. Значение 0.5
значительно уменьшит влияние нормы длины поля, а значение 2.0
значительно увеличит его.
Вы можете получить доступ к значениям по умолчанию для всех вышеперечисленных параметров и манипулировать ими с помощью метода config
:
// Get an associative array of all options listed above
Fuse :: config ();
// Merge associative array of options into default config
Fuse :: config ([ ' shouldSort ' => false ]);
// Get single default option
Fuse :: config ( ' shouldSort ' );
// Set single default option
Fuse :: config ( ' shouldSort ' , false );
Следующие методы доступны для каждого экземпляра FuseFuse
:
search
Выполняет поиск во всей коллекции документов и возвращает список результатов поиска.
public function search( mixed $ pattern , ? array $ options ): array
$pattern
может быть одним из:
$options
:
limit
(тип: int
): Обозначает максимальное количество возвращаемых результатов поиска.setCollection
Установить/заменить всю коллекцию документов. Если индекс не указан, он будет создан.
public function setCollection( array $ docs , ? Fuse Core FuseIndex $ index ): void
Пример:
$ fruits = [ ' apple ' , ' orange ' ];
$ fuse = new Fuse ( $ fruits );
$ fuse -> setCollection ([ ' banana ' , ' pear ' ]);
add
Добавляет документ в коллекцию и соответствующим образом обновляет индекс.
public function add( mixed $ doc ): void
Пример:
$ fruits = [ ' apple ' , ' orange ' ];
$ fuse = new Fuse ( $ fruits );
$ fuse -> add ( ' banana ' );
sizeof ( $ fruits ); // => 3
remove
Удаляет из списка все документы, для которых предикат возвращает true, и возвращает массив удаленных документов. Предикат вызывается с двумя аргументами: ($doc, $index)
.
public function remove(? callable $ predicate ): array
Пример:
$ fruits = [ ' apple ' , ' orange ' , ' banana ' , ' pear ' ];
$ fuse = new Fuse ( $ fruits );
$ results = $ fuse -> remove (fn( $ doc ) => $ doc === ' banana ' || $ doc === ' pear ' );
sizeof ( $ fuse -> getCollection ()); // => 2
$ results ; // => ['banana', 'pear']
removeAt
Удаляет документ по указанному индексу.
public function removeAt( int $ index ): void
Пример:
$ fruits = [ ' apple ' , ' orange ' , ' banana ' , ' pear ' ];
$ fuse = new Fuse ( $ fruits );
$ fuse -> removeAt ( 1 );
$ fuse -> getCollection (); // => ['apple', 'banana', 'pear']
getIndex
Возвращает сгенерированный индекс Fuse.
public function getIndex(): Fuse Core FuseIndex
Пример:
$ fruits = [ ' apple ' , ' orange ' , ' banana ' , ' pear ' ];
$ fuse = new Fuse ( $ fruits );
$ fuse -> getIndex ()-> size (); // => 4
Следующие методы доступны для каждого экземпляра FuseFuse
:
Fuse::createIndex
Предварительно сгенерируйте индекс из списка и передайте его непосредственно в экземпляр Fuse. Если список (значительно) велик, это ускоряет создание экземпляров.
public static function createIndex( array $ keys , array $ docs , array $ options = []): Fuse Core FuseIndex
Пример:
$ list = [ ... ]; // See the example from the 'Usage' section
$ options = [ ' keys ' => [ ' title ' , ' author.firstName ' ] ];
// Create the Fuse index
$ myIndex = Fuse :: createIndex ( $ options [ ' keys ' ], $ list );
// Initialize Fuse with the index
$ fuse = new Fuse ( $ list , $ options , $ myIndex );
Fuse::parseIndex
Анализирует индекс Fuse, сериализованный в формате JSON.
public static function parseIndex( array $ data , array $ options = []): Fuse Core FuseIndex
Пример:
// (1) When the data is collected
$ list = [ ... ]; // See the example from the 'Usage' section
$ options = [ ' keys ' => [ ' title ' , ' author.firstName ' ] ];
// Create the Fuse index
$ myIndex = Fuse :: createIndex ( $ options [ ' keys ' ], $ list );
// Serialize and save it
file_put_contents ( ' fuse-index.json ' , json_encode ( $ myIndex ));
// (2) When the search is needed
// Load and deserialize index to an array
$ fuseIndex = json_decode ( file_get_contents ( ' fuse-index.json ' ), true );
$ myIndex = Fuse :: parseIndex ( $ fuseIndex );
// Initialize Fuse with the index
$ fuse = new Fuse ( $ list , $ options , $ myIndex );
Предохранитель.js | PHP-предохранитель | |
---|---|---|
Получить версию предохранителя | Fuse.version | – |
Доступ к глобальной конфигурации | Свойство Fuse.config | Метод Fuse::config |
Изменение списка | Использование fuse.add() и т. д. изменяет исходный список, передаваемый new Fuse . | В PHP массивы представляют собой примитивный тип данных, а это означает, что исходный список никогда не изменяется с помощью Fuse. Чтобы получить текущий список после добавления/удаления элементов, можно использовать метод $fuse->getCollection() . |
Обратите внимание, что я стремлюсь к равенству функций с Fuse.js и поэтому не буду добавлять в логику поиска ни функции, ни исправления, которые не отражены в самом Fuse.js.
Если у вас есть какие-либо проблемы с результатами поиска, которые не являются явными ошибками в этом порте PHP, и вы знаете JavaScript, проверьте, правильно ли работает ваш вариант использования в онлайн-демо-версии Fuse.js, поскольку это каноническая реализация Fuse. Если проблема появляется и там, пожалуйста, откройте проблему в их репозитории.
Чтобы начать разработку на Fuse, вам нужны git, PHP (≥ 7.4) и Composer.
Поскольку код форматируется с использованием Prettier, также рекомендуется установить Node.js/npm, а также использовать редактор, поддерживающий форматирование Prettier.
Клонируйте репозиторий и cd
в него:
git clone https://github.com/loilo/fuse.git
cd fuse
Установите зависимости Composer:
composer install
Установите зависимости npm (необязательно, но рекомендуется). Это необходимо только для форматирования кода, поскольку зависимости npm включают плагины Prettier, используемые в этом проекте.
npm ci
Для этого проекта предусмотрены различные виды проверок кода. Все они запускаются при отправке запроса на включение, но также могут выполняться локально:
Команда | Цель | Описание |
---|---|---|
vendor/bin/phpcs | проверить стиль кода | Запустите PHP_CodeSniffer, чтобы убедиться, что исходный код Fuse соответствует стилю кодирования PSR-12. |
vendor/bin/psalm | статический анализ | Запустите Psalm для базы кода, чтобы избежать ошибок, связанных с типами, и небезопасных шаблонов кодирования. |
vendor/bin/phpunit | проверить логику программы | Запустите все тесты PHPUnit из test папки. |
Прежде чем отправлять запрос на включение, добавьте соответствующие тесты в папку test
.