Вы можете рассмотреть возможность использования Doctrine Inflector в сочетании с stemmer для этого.
Вот алгоритм на высоком уровне
- Разделить строку поиска по пробелам, обрабатывать слова по отдельности
- Строчные слова для поиска
- Полосатые специальные символы
- Singularize, заменить различную часть на подстановочный знак ('%')
- Стебель, заменить отличающуюся часть подстановочным знаком ('%')
Вот функция, которую я собрал
/**
* Use inflection and stemming to produce a good search string to match subtle
* differences in a MySQL table.
*
* @string $sInputString The string you want to base the search on
* @string $sSearchTable The table you want to search in
* @string $sSearchField The field you want to search
*/
function getMySqlSearchQuery($sInputString, $sSearchTable, $sSearchField)
{
$aInput = explode(' ', strtolower($sInputString));
$aSearch = [];
foreach($aInput as $sInput) {
$sInput = str_replace("'", '', $sInput);
//--------------------
// Inflect
//--------------------
$sInflected = Inflector::singularize($sInput);
// Otherwise replace the part of the inflected string where it differs from the input string
// with a % (wildcard) for the MySQL query
$iPosition = strspn($sInput ^ $sInflected, "\0");
if($iPosition !== null && $iPosition < strlen($sInput)) {
$sInput = substr($sInflected, 0, $iPosition) . '%';
} else {
$sInput = $sInput;
}
//--------------------
// Stem
//--------------------
$sStemmed = stem_english($sInput);
// Otherwise replace the part of the inflected string where it differs from the input string
// with a % (wildcard) for the MySQL query
$iPosition = strspn($sInput ^ $sStemmed, "\0");
if($iPosition !== null && $iPosition < strlen($sInput)) {
$aSearch[] = substr($sStemmed, 0, $iPosition) . '%';
} else {
$aSearch[] = $sInput;
}
}
$sSearch = implode(' ', $aSearch);
return "SELECT * FROM $sSearchTable WHERE LOWER($sSearchField) LIKE '$sSearch';";
}
Который я запускал с несколькими тестовыми строками
Input String: Mary's Hamburgers
SearchString: SELECT * FROM LIST2 WHERE LOWER(some_field) LIKE 'mary% hamburger%';
Input String: Office Supplies
SearchString: SELECT * FROM LIST2 WHERE LOWER(some_field) LIKE 'offic% suppl%';
Input String: Accounting department
SearchString: SELECT * FROM LIST2 WHERE LOWER(some_field) LIKE 'account% depart%';
Возможно, не идеально, но в любом случае это хорошее начало! Где он упадет, это когда несколько совпадений будут возвращены. Там нет логики, чтобы определить лучший матч. Вот где могут появиться такие вещи, как полный текст MySQL и Lucene . Подумав немного подробнее, вы можете использовать levenshtein для ранжирования нескольких результатов с помощью этого подхода !